working version of DecryptVerifyResult

This commit is contained in:
Vincent Breitmoser
2014-09-13 19:30:10 +02:00
parent 4c636a1471
commit dbbefe2f41
6 changed files with 112 additions and 63 deletions

View File

@@ -49,6 +49,8 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.service.results.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.results.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.results.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.results.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@@ -70,6 +72,7 @@ import java.util.Set;
*/ */
public class PgpDecryptVerify { public class PgpDecryptVerify {
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@@ -83,6 +86,7 @@ public class PgpDecryptVerify {
private PgpDecryptVerify(Builder builder) { private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder // private Constructor can only be called from Builder
this.mProviderHelper = builder.mProviderHelper; this.mProviderHelper = builder.mProviderHelper;
this.mPassphraseCache = builder.mPassphraseCache;
this.mData = builder.mData; this.mData = builder.mData;
this.mOutStream = builder.mOutStream; this.mOutStream = builder.mOutStream;
@@ -97,6 +101,7 @@ public class PgpDecryptVerify {
public static class Builder { public static class Builder {
// mandatory parameter // mandatory parameter
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@@ -108,8 +113,10 @@ public class PgpDecryptVerify {
private boolean mDecryptMetadataOnly = false; private boolean mDecryptMetadataOnly = false;
private byte[] mDecryptedSessionKey = null; private byte[] mDecryptedSessionKey = null;
public Builder(ProviderHelper providerHelper, InputData data, OutputStream outStream) { public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache,
InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper; this.mProviderHelper = providerHelper;
this.mPassphraseCache = passphraseCache;
this.mData = data; this.mData = data;
this.mOutStream = outStream; this.mOutStream = outStream;
} }
@@ -169,6 +176,16 @@ public class PgpDecryptVerify {
} }
} }
public interface PassphraseCache {
public String getCachedPassphrase(long masterKeyId)
throws NoSecretKeyException;
}
public static class NoSecretKeyException extends Exception {
public NoSecretKeyException() {
}
}
/** /**
* Decrypts and/or verifies data based on parameters of class * Decrypts and/or verifies data based on parameters of class
*/ */
@@ -286,12 +303,24 @@ public class PgpDecryptVerify {
encryptedDataAsymmetric = encData; encryptedDataAsymmetric = encData;
// if passphrase was not cached, return here indicating that a passphrase is missing! // if no passphrase was explicitly set try to get it from the cache service
if (mPassphrase == null) { if (mPassphrase == null) {
DecryptVerifyResult result = try {
new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, log); // returns "" if key has no passphrase
result.setKeyIdPassphraseNeeded(subKeyId); mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId);
return result; } catch (NoSecretKeyException e) {
// log.add(LogLevel.ERROR, LogType.MSG_DEC_ERROR_NO_KEY);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
// if passphrase was not cached, return here
// indicating that a passphrase is missing!
if (mPassphrase == null) {
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, log);
result.setKeyIdPassphraseNeeded(subKeyId);
return result;
}
} }
// break out of while, only decrypt the first packet where we have a key // break out of while, only decrypt the first packet where we have a key

View File

@@ -462,32 +462,23 @@ public class OpenPgpService extends RemoteService {
.setDecryptMetadataOnly(decryptMetadataOnly) .setDecryptMetadataOnly(decryptMetadataOnly)
.setNfcState(nfcDecryptedSessionKey); .setNfcState(nfcDecryptedSessionKey);
DecryptVerifyResult decryptVerifyResult; // TODO: currently does not support binary signed-only content
try { DecryptVerifyResult decryptVerifyResult = builder.build().execute();
// TODO: currently does not support binary signed-only content
decryptVerifyResult = builder.build().execute();
// throw exceptions upwards to client with meaningful messages if (decryptVerifyResult.isPending()) {
} catch (PgpDecryptVerify.InvalidDataException e) { switch (decryptVerifyResult.getResult()) {
throw new Exception(getString(R.string.error_invalid_data)); case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE:
} catch (PgpDecryptVerify.KeyExtractionException e) { return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
throw new Exception(getString(R.string.error_could_not_extract_private_key)); case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE:
} catch (PgpDecryptVerify.WrongPassphraseException e) { throw new PgpGeneralException(
throw new Exception(getString(R.string.error_wrong_passphrase)); "Decryption of symmetric content not supported by API!");
} catch (PgpDecryptVerify.NoSecretKeyException e) { case DecryptVerifyResult.RESULT_PENDING_NFC:
throw new Exception(getString(R.string.error_no_secret_key_found)); // TODO get passphrase here? currently not in DecryptVerifyResult
} catch (PgpDecryptVerify.IntegrityCheckFailedException e) { return getNfcDecryptIntent(
throw new Exception(getString(R.string.error_integrity_check_failed)); data, null, decryptVerifyResult.getNfcEncryptedSessionKey());
} catch (PgpDecryptVerify.NeedNfcDataException e) { }
// return PendingIntent to execute NFC activity throw new PgpGeneralException(
return getNfcDecryptIntent(data, e.mPassphrase, e.mEncryptedSessionKey); "Encountered unhandled type of pending action not supported by API!");
}
if (DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client
return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
} else if (DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE == decryptVerifyResult.getStatus()) {
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
} }
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();

View File

@@ -337,15 +337,22 @@ public class KeychainIntentService extends IntentService implements Progressable
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
/* TODO find passphrase from cache, if not provided
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
*/
// verifyText and decrypt returning additional resultData values for the // verifyText and decrypt returning additional resultData values for the
// verification of signatures // verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this), inputData, outStream new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
@Override
public String getCachedPassphrase(long masterKeyId) {
try {
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
return null;
}
}
},
inputData, outStream
); );
builder.setProgressable(this) builder.setProgressable(this)
.setAllowSymmetricDecryption(true) .setAllowSymmetricDecryption(true)
@@ -378,15 +385,22 @@ public class KeychainIntentService extends IntentService implements Progressable
Bundle resultData = new Bundle(); Bundle resultData = new Bundle();
/* TODO find passphrase from cache, if not provided
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
*/
// verifyText and decrypt returning additional resultData values for the // verifyText and decrypt returning additional resultData values for the
// verification of signatures // verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this), inputData, null new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
}
}
},
inputData, null
); );
builder.setProgressable(this) builder.setProgressable(this)
.setAllowSymmetricDecryption(true) .setAllowSymmetricDecryption(true)

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.service.results; package org.sufficientlysecure.keychain.service.results;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable;
import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
@@ -34,7 +33,7 @@ public class DecryptVerifyResult extends OperationResultParcel {
public static final int RESULT_PENDING_NFC = RESULT_PENDING +48; public static final int RESULT_PENDING_NFC = RESULT_PENDING +48;
long mKeyIdPassphraseNeeded; long mKeyIdPassphraseNeeded;
byte[] mSessionKey; byte[] mNfcSessionKey;
OpenPgpSignatureResult mSignatureResult; OpenPgpSignatureResult mSignatureResult;
OpenPgpMetadata mDecryptMetadata; OpenPgpMetadata mDecryptMetadata;
@@ -48,7 +47,11 @@ public class DecryptVerifyResult extends OperationResultParcel {
} }
public void setNfcEncryptedSessionKey(byte[] sessionKey) { public void setNfcEncryptedSessionKey(byte[] sessionKey) {
mSessionKey = sessionKey; mNfcSessionKey = sessionKey;
}
public byte[] getNfcEncryptedSessionKey() {
return mNfcSessionKey;
} }
public OpenPgpSignatureResult getSignatureResult() { public OpenPgpSignatureResult getSignatureResult() {
@@ -80,7 +83,7 @@ public class DecryptVerifyResult extends OperationResultParcel {
mKeyIdPassphraseNeeded = source.readLong(); mKeyIdPassphraseNeeded = source.readLong();
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
mSessionKey = source.readInt() != 0 ? source.createByteArray() : null; mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null;
} }
public int describeContents() { public int describeContents() {
@@ -92,9 +95,9 @@ public class DecryptVerifyResult extends OperationResultParcel {
dest.writeLong(mKeyIdPassphraseNeeded); dest.writeLong(mKeyIdPassphraseNeeded);
dest.writeParcelable(mSignatureResult, 0); dest.writeParcelable(mSignatureResult, 0);
dest.writeParcelable(mDecryptMetadata, 0); dest.writeParcelable(mDecryptMetadata, 0);
if (mSessionKey != null) { if (mNfcSessionKey != null) {
dest.writeInt(1); dest.writeInt(1);
dest.writeByteArray(mSessionKey); dest.writeByteArray(mNfcSessionKey);
} else { } else {
dest.writeInt(0); dest.writeInt(0);
} }

View File

@@ -261,13 +261,18 @@ public class DecryptFileFragment extends DecryptFragment {
DecryptVerifyResult result = DecryptVerifyResult result =
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
switch (result.getResult()) { if (result.isPending()) {
case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: switch (result.getResult()) {
showPassphraseDialog(result.getKeyIdPassphraseNeeded()); case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE:
return; showPassphraseDialog(result.getKeyIdPassphraseNeeded());
case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: return;
showPassphraseDialog(Constants.key.symmetric); case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE:
return; showPassphraseDialog(Constants.key.symmetric);
return;
}
// error, we can't work with this!
result.createNotify(getActivity());
return;
} }
// display signature result in activity // display signature result in activity

View File

@@ -146,13 +146,18 @@ public class DecryptMessageFragment extends DecryptFragment {
DecryptVerifyResult result = DecryptVerifyResult result =
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
switch (result.getResult()) { if (result.isPending()) {
case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: switch (result.getResult()) {
showPassphraseDialog(result.getKeyIdPassphraseNeeded()); case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE:
return; showPassphraseDialog(result.getKeyIdPassphraseNeeded());
case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: return;
showPassphraseDialog(Constants.key.symmetric); case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE:
return; showPassphraseDialog(Constants.key.symmetric);
return;
}
// error, we can't work with this!
result.createNotify(getActivity());
return;
} }
byte[] decryptedMessage = returnData byte[] decryptedMessage = returnData
@@ -160,6 +165,8 @@ public class DecryptMessageFragment extends DecryptFragment {
mMessage.setText(new String(decryptedMessage)); mMessage.setText(new String(decryptedMessage));
mMessage.setHorizontallyScrolling(false); mMessage.setHorizontallyScrolling(false);
result.createNotify(getActivity());
// display signature result in activity // display signature result in activity
onResult(result); onResult(result);
} }