From d686c55a0a86ef845795fc03a8a5de44b5fe73cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 17 Sep 2014 13:45:16 +0200 Subject: Work on new result handling (WIP) --- .../keychain/pgp/PassphraseCacheInterface.java | 11 ++ .../keychain/pgp/PgpDecryptVerify.java | 26 +-- .../keychain/pgp/PgpSignEncrypt.java | 49 ++++-- .../keychain/remote/OpenPgpService.java | 192 ++++++++++++--------- .../keychain/service/KeychainIntentService.java | 24 ++- .../service/results/DecryptVerifyResult.java | 8 +- .../keychain/service/results/OperationResult.java | 2 +- .../service/results/SignEncryptResult.java | 17 +- .../keychain/ui/EncryptTextActivity.java | 137 +++++++++------ .../keychain/ui/EncryptTextFragment.java | 9 +- .../keychain/ui/PassphraseDialogActivity.java | 8 +- .../ui/dialog/CustomAlertDialogBuilder.java | 8 +- 12 files changed, 308 insertions(+), 183 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java new file mode 100644 index 000000000..433c4db00 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java @@ -0,0 +1,11 @@ +package org.sufficientlysecure.keychain.pgp; + +public interface PassphraseCacheInterface { + public static class NoSecretKeyException extends Exception { + public NoSecretKeyException() { + } + } + + public String getCachedPassphrase(long masterKeyId) throws NoSecretKeyException; + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 89e25b2e0..1d8ca1b54 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -72,7 +72,7 @@ import java.util.Set; */ public class PgpDecryptVerify { private ProviderHelper mProviderHelper; - private PassphraseCache mPassphraseCache; + private PassphraseCacheInterface mPassphraseCache; private InputData mData; private OutputStream mOutStream; @@ -101,7 +101,7 @@ public class PgpDecryptVerify { public static class Builder { // mandatory parameter private ProviderHelper mProviderHelper; - private PassphraseCache mPassphraseCache; + private PassphraseCacheInterface mPassphraseCache; private InputData mData; private OutputStream mOutStream; @@ -113,12 +113,12 @@ public class PgpDecryptVerify { private boolean mDecryptMetadataOnly = false; private byte[] mDecryptedSessionKey = null; - public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache, + public Builder(ProviderHelper providerHelper, PassphraseCacheInterface passphraseCache, InputData data, OutputStream outStream) { - this.mProviderHelper = providerHelper; - this.mPassphraseCache = passphraseCache; - this.mData = data; - this.mOutStream = outStream; + mProviderHelper = providerHelper; + mPassphraseCache = passphraseCache; + mData = data; + mOutStream = outStream; } public Builder setProgressable(Progressable progressable) { @@ -176,16 +176,6 @@ 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 */ @@ -322,7 +312,7 @@ public class PgpDecryptVerify { // returns "" if key has no passphrase mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId); log.add(LogType.MSG_DC_PASS_CACHED, indent +1); - } catch (NoSecretKeyException e) { + } catch (PassphraseCacheInterface.NoSecretKeyException e) { log.add(LogType.MSG_DC_ERROR_NO_KEY, indent +1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index e06335104..25840495e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -59,6 +59,7 @@ import java.util.LinkedList; */ public class PgpSignEncrypt { private ProviderHelper mProviderHelper; + private PassphraseCacheInterface mPassphraseCache; private String mVersionHeader; private InputData mData; private OutputStream mOutStream; @@ -93,6 +94,7 @@ public class PgpSignEncrypt { private PgpSignEncrypt(Builder builder) { // private Constructor can only be called from Builder this.mProviderHelper = builder.mProviderHelper; + this.mPassphraseCache = builder.mPassphraseCache; this.mVersionHeader = builder.mVersionHeader; this.mData = builder.mData; this.mOutStream = builder.mOutStream; @@ -117,6 +119,7 @@ public class PgpSignEncrypt { public static class Builder { // mandatory parameter private ProviderHelper mProviderHelper; + private PassphraseCacheInterface mPassphraseCache; private InputData mData; private OutputStream mOutStream; @@ -138,8 +141,10 @@ public class PgpSignEncrypt { private byte[] mNfcSignedHash = null; private Date mNfcCreationTimestamp = null; - public Builder(ProviderHelper providerHelper, InputData data, OutputStream outStream) { + public Builder(ProviderHelper providerHelper, PassphraseCacheInterface passphraseCache, + InputData data, OutputStream outStream) { mProviderHelper = providerHelper; + mPassphraseCache = passphraseCache; mData = data; mOutStream = outStream; } @@ -290,20 +295,15 @@ public class PgpSignEncrypt { /* Get keys for signature generation for later usage */ CanonicalizedSecretKey signingKey = null; + long signKeyId; if (enableSignature) { - // If we weren't handed a passphrase, throw early - if (mSignaturePassphrase == null) { - log.add(LogType.MSG_SE_ERROR_NO_PASSPHRASE, indent); - return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log); - } - try { // fetch the indicated master key id (the one whose name we sign in) CanonicalizedSecretKeyRing signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId); // fetch the specific subkey to sign with, or just use the master key if none specified - long signKeyId = mSignatureSubKeyId != null ? mSignatureSubKeyId : mSignatureMasterKeyId; + signKeyId = mSignatureSubKeyId != null ? mSignatureSubKeyId : mSignatureMasterKeyId; signingKey = signingKeyRing.getSecretKey(signKeyId); // make sure it's a signing key alright! } catch (ProviderHelper.NotFoundException e) { @@ -317,6 +317,28 @@ public class PgpSignEncrypt { return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log); } + // if no passphrase was explicitly set try to get it from the cache service + if (mSignaturePassphrase == null) { + try { + // returns "" if key has no passphrase + mSignaturePassphrase = mPassphraseCache.getCachedPassphrase(signKeyId); + // TODO +// log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); + } catch (PassphraseCacheInterface.NoSecretKeyException e) { + // TODO +// log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log); + } + + // if passphrase was not cached, return here indicating that a passphrase is missing! + if (mSignaturePassphrase == null) { + log.add(LogType.MSG_SE_PENDING_PASSPHRASE, indent + 1); + SignEncryptResult result = new SignEncryptResult(SignEncryptResult.RESULT_PENDING_PASSPHRASE, log); + result.setKeyIdPassphraseNeeded(signKeyId); + return result; + } + } + updateProgress(R.string.progress_extracting_signature_key, 0, 100); try { @@ -369,10 +391,10 @@ public class PgpSignEncrypt { log.add(LogType.MSG_SE_KEY_OK, indent + 1, PgpKeyHelper.convertKeyIdToHex(id)); } catch (PgpGeneralException e) { - log.add(LogType.MSG_SE_KEY_WARN, indent +1, + log.add(LogType.MSG_SE_KEY_WARN, indent + 1, PgpKeyHelper.convertKeyIdToHex(id)); } catch (ProviderHelper.NotFoundException e) { - log.add(LogType.MSG_SE_KEY_UNKNOWN, indent +1, + log.add(LogType.MSG_SE_KEY_UNKNOWN, indent + 1, PgpKeyHelper.convertKeyIdToHex(id)); } } @@ -407,9 +429,10 @@ public class PgpSignEncrypt { /* actual encryption */ updateProgress(R.string.progress_encrypting, 8, 100); log.add(enableSignature - ? LogType.MSG_SE_SIGCRYPTING - : LogType.MSG_SE_ENCRYPTING, - indent); + ? LogType.MSG_SE_SIGCRYPTING + : LogType.MSG_SE_ENCRYPTING, + indent + ); indent += 1; encryptionOut = cPk.open(out, new byte[1 << 16]); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index a26ee009a..c11f283be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -32,6 +32,7 @@ import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.nfc.NfcActivity; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; @@ -259,6 +260,17 @@ public class OpenPgpService extends RemoteService { // sign-only PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( new ProviderHelper(getContext()), + new PassphraseCacheInterface() { + @Override + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { + try { + return PassphraseCacheService.getCachedPassphrase( + OpenPgpService.this, masterKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + throw new PassphraseCacheInterface.NoSecretKeyException(); + } + } + }, inputData, os); builder.setEnableAsciiArmorOutput(asciiArmor) .setVersionHeader(PgpHelper.getVersionForHeader(this)) @@ -271,24 +283,27 @@ public class OpenPgpService extends RemoteService { builder.setCleartextInput(true); // execute PGP operation! - SignEncryptResult result = builder.build().execute(); - - if (result.isPending()) { - switch (result.getResult()) { - case SignEncryptResult.RESULT_PENDING_NFC: - // return PendingIntent to execute NFC activity - // pass through the signature creation timestamp to be used again on second execution - // of PgpSignEncrypt when we have the signed hash! - data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); - return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo()); - - default: - throw new Exception("Encountered unhandled pending state - please file a bug report!"); + SignEncryptResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + // return PendingIntent to execute NFC activity + // pass through the signature creation timestamp to be used again on second execution + // of PgpSignEncrypt when we have the signed hash! + data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); + return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); + } else { + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); } - } - - if (!result.success()) { - LogEntryParcel errorMsg = result.getLog().getLast(); + } else if (pgpResult.success()) { + // see end of method + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); throw new Exception(getString(errorMsg.mType.getMsgId())); } @@ -346,6 +361,17 @@ public class OpenPgpService extends RemoteService { PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( new ProviderHelper(getContext()), + new PassphraseCacheInterface() { + @Override + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { + try { + return PassphraseCacheService.getCachedPassphrase( + OpenPgpService.this, masterKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + throw new PassphraseCacheInterface.NoSecretKeyException(); + } + } + }, inputData, os); builder.setEnableAsciiArmorOutput(asciiArmor) .setVersionHeader(PgpHelper.getVersionForHeader(this)) @@ -384,24 +410,27 @@ public class OpenPgpService extends RemoteService { } // execute PGP operation! - SignEncryptResult result = builder.build().execute(); - - if (result.isPending()) { - switch (result.getResult()) { - case SignEncryptResult.RESULT_PENDING_NFC: - // return PendingIntent to execute NFC activity - // pass through the signature creation timestamp to be used again on second execution - // of PgpSignEncrypt when we have the signed hash! - data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); - return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo()); - - default: - throw new Exception("Encountered unhandled pending state - please file a bug report!"); + SignEncryptResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + // return PendingIntent to execute NFC activity + // pass through the signature creation timestamp to be used again on second execution + // of PgpSignEncrypt when we have the signed hash! + data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); + return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); + } else { + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); } - } - - if (!result.success()) { - LogEntryParcel errorMsg = result.getLog().getLast(); + } else if (pgpResult.success()) { + // see end of method + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); throw new Exception(getString(errorMsg.mType.getMsgId())); } @@ -445,14 +474,14 @@ public class OpenPgpService extends RemoteService { PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( new ProviderHelper(this), - new PgpDecryptVerify.PassphraseCache() { + new PassphraseCacheInterface() { @Override - public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException { + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { try { return PassphraseCacheService.getCachedPassphrase( OpenPgpService.this, masterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { - throw new PgpDecryptVerify.NoSecretKeyException(); + throw new PassphraseCacheInterface.NoSecretKeyException(); } } }, @@ -470,57 +499,64 @@ public class OpenPgpService extends RemoteService { .setNfcState(nfcDecryptedSessionKey); // TODO: currently does not support binary signed-only content - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); - - if (decryptVerifyResult.isPending()) { - switch (decryptVerifyResult.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded()); - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - throw new PgpGeneralException( - "Decryption of symmetric content not supported by API!"); - case DecryptVerifyResult.RESULT_PENDING_NFC: - // TODO get passphrase here? currently not in DecryptVerifyResult - return getNfcDecryptIntent( - data, null, decryptVerifyResult.getNfcEncryptedSessionKey()); + DecryptVerifyResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { + throw new PgpGeneralException( + "Decryption of symmetric content not supported by API!"); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == + DecryptVerifyResult.RESULT_PENDING_NFC) { + // TODO get passphrase here? currently not in DecryptVerifyResult + return getNfcDecryptIntent( + data, null, pgpResult.getNfcEncryptedSessionKey()); + } else { + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); } - throw new PgpGeneralException( - "Encountered unhandled type of pending action not supported by API!"); - } + } else if (pgpResult.success()) { - OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); - if (signatureResult != null) { - result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); + OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult(); + if (signatureResult != null) { + result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); - if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) { - // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5 - if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED - || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) { - signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR); + if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) { + // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5 + if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED + || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) { + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR); + } } - } - if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { - // If signature is unknown we return an _additional_ PendingIntent - // to retrieve the missing key - Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); - intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId()); - intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); + if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { + // If signature is unknown we return an _additional_ PendingIntent + // to retrieve the missing key + Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); + intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId()); + intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, - intent, - PendingIntent.FLAG_CANCEL_CURRENT); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT); - result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + } } - } - if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { - OpenPgpMetadata metadata = decryptVerifyResult.getDecryptMetadata(); - if (metadata != null) { - result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); + if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { + OpenPgpMetadata metadata = pgpResult.getDecryptMetadata(); + if (metadata != null) { + result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); + } } + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); + throw new Exception(getString(errorMsg.mType.getMsgId())); } } finally { is.close(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 19248dfa3..9d06fe22a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; @@ -269,6 +270,17 @@ public class KeychainIntentService extends IntentService implements Progressable /* Operation */ PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( new ProviderHelper(this), + new PassphraseCacheInterface() { + @Override + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { + try { + return PassphraseCacheService.getCachedPassphrase( + KeychainIntentService.this, masterKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + throw new PassphraseCacheInterface.NoSecretKeyException(); + } + } + }, inputData, outStream ); builder.setProgressable(this) @@ -342,14 +354,14 @@ public class KeychainIntentService extends IntentService implements Progressable // verification of signatures PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( new ProviderHelper(this), - new PgpDecryptVerify.PassphraseCache() { + new PassphraseCacheInterface() { @Override - public String getCachedPassphrase(long masterKeyId) { + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { try { return PassphraseCacheService.getCachedPassphrase( KeychainIntentService.this, masterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { - return null; + throw new PassphraseCacheInterface.NoSecretKeyException(); } } }, @@ -390,14 +402,14 @@ public class KeychainIntentService extends IntentService implements Progressable // verification of signatures PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( new ProviderHelper(this), - new PgpDecryptVerify.PassphraseCache() { + new PassphraseCacheInterface() { @Override - public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException { + public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException { try { return PassphraseCacheService.getCachedPassphrase( KeychainIntentService.this, masterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { - throw new PgpDecryptVerify.NoSecretKeyException(); + throw new PassphraseCacheInterface.NoSecretKeyException(); } } }, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/DecryptVerifyResult.java index 18533ffe6..2113c1ab1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/DecryptVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/DecryptVerifyResult.java @@ -26,12 +26,12 @@ import org.openintents.openpgp.OpenPgpSignatureResult; public class DecryptVerifyResult extends OperationResult { // the fourth bit indicates a "data pending" result! (it's also a form of non-success) - public static final int RESULT_PENDING = RESULT_ERROR +8; + public static final int RESULT_PENDING = RESULT_ERROR + 8; // fifth to sixth bit in addition indicate specific type of pending - public static final int RESULT_PENDING_ASYM_PASSPHRASE = RESULT_PENDING +16; - public static final int RESULT_PENDING_SYM_PASSPHRASE = RESULT_PENDING +32; - public static final int RESULT_PENDING_NFC = RESULT_PENDING +48; + public static final int RESULT_PENDING_ASYM_PASSPHRASE = RESULT_PENDING + 16; + public static final int RESULT_PENDING_SYM_PASSPHRASE = RESULT_PENDING + 32; + public static final int RESULT_PENDING_NFC = RESULT_PENDING + 48; long mKeyIdPassphraseNeeded; byte[] mNfcSessionKey; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java index 39946a026..714f8d9cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java @@ -475,7 +475,6 @@ public abstract class OperationResult implements Parcelable { MSG_SE_ERROR_SIGN_KEY(LogLevel.ERROR, R.string.msg_se_error_sign_key), MSG_SE_ERROR_KEY_SIGN (LogLevel.ERROR, R.string.msg_se_error_key_sign), MSG_SE_ERROR_NFC (LogLevel.ERROR, R.string.msg_se_error_nfc), - MSG_SE_ERROR_NO_PASSPHRASE (LogLevel.ERROR, R.string.msg_se_error_no_passphrase), MSG_SE_ERROR_PGP (LogLevel.ERROR, R.string.msg_se_error_pgp), MSG_SE_ERROR_SIG (LogLevel.ERROR, R.string.msg_se_error_sig), MSG_SE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_se_error_unlock), @@ -484,6 +483,7 @@ public abstract class OperationResult implements Parcelable { MSG_SE_KEY_WARN (LogLevel.WARN, R.string.msg_se_key_warn), MSG_SE_OK (LogLevel.OK, R.string.msg_se_ok), MSG_SE_PENDING_NFC (LogLevel.INFO, R.string.msg_se_pending_nfc), + MSG_SE_PENDING_PASSPHRASE (LogLevel.INFO, R.string.msg_se_pending_passphrase), MSG_SE (LogLevel.DEBUG, R.string.msg_se), MSG_SE_SIGNING (LogLevel.DEBUG, R.string.msg_se_signing), MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/SignEncryptResult.java index 9d492e545..e4e843e83 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/SignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/SignEncryptResult.java @@ -24,15 +24,26 @@ import java.util.Date; public class SignEncryptResult extends OperationResult { // the fourth bit indicates a "data pending" result! (it's also a form of non-success) - public static final int RESULT_PENDING = RESULT_ERROR +8; + public static final int RESULT_PENDING = RESULT_ERROR + 8; // fifth to sixth bit in addition indicate specific type of pending - public static final int RESULT_PENDING_NFC = RESULT_PENDING +16; + public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16; + public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32; + + long mKeyIdPassphraseNeeded; byte[] mNfcHash; int mNfcAlgo; Date mNfcTimestamp; + public long getKeyIdPassphraseNeeded() { + return mKeyIdPassphraseNeeded; + } + + public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) { + mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; + } + public void setNfcData(byte[] sessionKey, int nfcAlgo, Date nfcTimestamp) { mNfcHash = sessionKey; mNfcAlgo = nfcAlgo; @@ -52,7 +63,7 @@ public class SignEncryptResult extends OperationResult { } public boolean isPending() { - return (mResult & RESULT_PENDING) != 0; + return (mResult & RESULT_PENDING) == RESULT_PENDING; } public SignEncryptResult(int result, OperationLog log) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 4bf1cad3c..56a2ef233 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -22,7 +22,6 @@ import android.app.ProgressDialog; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v4.app.Fragment; @@ -35,15 +34,11 @@ import org.sufficientlysecure.keychain.api.OpenKeychainIntents; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.ShareHelper; +import org.sufficientlysecure.keychain.nfc.NfcActivity; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; -import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.results.SignEncryptResult; -import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -200,22 +195,36 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi SignEncryptResult result = message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); - // TODO if (result.isPending()) - - if (!result.success()) { - result.createNotify(EncryptTextActivity.this).show(); - return; - } - - if (mShareAfterEncrypt) { - // Share encrypted message/file - startActivity(sendWithChooserExcludingEncrypt(message)); + if (result.isPending()) { + Log.d(Constants.TAG, "result.getResult() " + result.getResult()); + if ((result.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + Log.d(Constants.TAG, "passp"); + startPassphraseDialog(result.getKeyIdPassphraseNeeded()); + } else if ((result.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + Log.d(Constants.TAG, "nfc"); + + // use after nfc sign +//// data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); + startNfcSign("123456", result.getNfcHash(), result.getNfcAlgo()); + } else { + throw new RuntimeException("Unhandled pending result!"); + } + + } else if (result.success()) { + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt(message)); + } else { + // Copy to clipboard + copyToClipboard(message); + result.createNotify(EncryptTextActivity.this).show(); + // Notify.showNotify(EncryptTextActivity.this, + // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); + } } else { - // Copy to clipboard - copyToClipboard(message); result.createNotify(EncryptTextActivity.this).show(); - // Notify.showNotify(EncryptTextActivity.this, - // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); } } } @@ -231,6 +240,36 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi startService(intent); } + private void startNfcSign(String pin, byte[] hashToSign, int hashAlgo) { + Intent data = new Intent(); + + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(this, NfcActivity.class); + intent.setAction(NfcActivity.ACTION_SIGN_HASH); + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(NfcActivity.EXTRA_DATA, data); + intent.putExtra(NfcActivity.EXTRA_PIN, pin); + + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + startActivityForResult(intent, 0); + } + + private void startPassphraseDialog(long subkeyId) { + Intent data = new Intent(); + + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(this, PassphraseDialogActivity.class); + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); + +// intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + startActivityForResult(intent, 0); + } + private Bundle createEncryptBundle() { // fill values for this action Bundle data = new Bundle(); @@ -326,35 +365,35 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi return false; } - try { - // TODO This should really not be decided here. We do need the info for the passphrase - // TODO dialog fragment though, so that's just the way it is for now. - if (mSigningKeyId != 0) { - CachedPublicKeyRing signingRing = - new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId); - long sigSubKeyId = signingRing.getSignId(); - // Make sure the passphrase is cached, then start over. - if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) { - PassphraseDialogFragment.show(this, sigSubKeyId, - new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - // restart - startEncrypt(); - } - } - } - ); - - return false; - } - } - } catch (PgpGeneralException e) { - Log.e(Constants.TAG, "Key not found!", e); - } catch (PassphraseCacheService.KeyNotFoundException e) { - Log.e(Constants.TAG, "Key not found!", e); - } +// try { +// // TODO This should really not be decided here. We do need the info for the passphrase +// // TODO dialog fragment though, so that's just the way it is for now. +// if (mSigningKeyId != 0) { +// CachedPublicKeyRing signingRing = +// new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId); +// long sigSubKeyId = signingRing.getSignId(); +// // Make sure the passphrase is cached, then start over. +// if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) { +// PassphraseDialogFragment.show(this, sigSubKeyId, +// new Handler() { +// @Override +// public void handleMessage(Message message) { +// if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { +// // restart +// startEncrypt(); +// } +// } +// } +// ); +// +// return false; +// } +// } +// } catch (PgpGeneralException e) { +// Log.e(Constants.TAG, "Key not found!", e); +// } catch (PassphraseCacheService.KeyNotFoundException e) { +// Log.e(Constants.TAG, "Key not found!", e); +// } } return true; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index fa9036565..b13cb7837 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -32,13 +32,12 @@ import org.sufficientlysecure.keychain.R; public class EncryptTextFragment extends Fragment { public static final String ARG_TEXT = "text"; - private TextView mMessage = null; + private TextView mText; private View mEncryptShare; private View mEncryptClipboard; private EncryptActivityInterface mEncryptInterface; - @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -56,8 +55,8 @@ public class EncryptTextFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false); - mMessage = (TextView) view.findViewById(R.id.message); - mMessage.addTextChangedListener(new TextWatcher() { + mText = (TextView) view.findViewById(R.id.encrypt_text_text); + mText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -98,7 +97,7 @@ public class EncryptTextFragment extends Fragment { String text = mEncryptInterface.getMessage(); if (text != null) { - mMessage.setText(text); + mText.setText(text); } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index fa9444862..705eb51ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -59,7 +59,7 @@ import org.sufficientlysecure.keychain.util.Log; public class PassphraseDialogActivity extends FragmentActivity { public static final String MESSAGE_DATA_PASSPHRASE = "passphrase"; - public static final String EXTRA_SECRET_KEY_ID = "secret_key_id"; + public static final String EXTRA_SUBKEY_ID = "secret_key_id"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -76,7 +76,7 @@ public class PassphraseDialogActivity extends FragmentActivity { // this activity itself has no content view (see manifest) - long keyId = getIntent().getLongExtra(EXTRA_SECRET_KEY_ID, 0); + long keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); show(this, keyId); } @@ -92,7 +92,7 @@ public class PassphraseDialogActivity extends FragmentActivity { // do NOT check if the key even needs a passphrase. that's not our job here. PassphraseDialogFragment frag = new PassphraseDialogFragment(); Bundle args = new Bundle(); - args.putLong(EXTRA_SECRET_KEY_ID, keyId); + args.putLong(EXTRA_SUBKEY_ID, keyId); frag.setArguments(args); @@ -116,7 +116,7 @@ public class PassphraseDialogActivity extends FragmentActivity { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); - mSubKeyId = getArguments().getLong(EXTRA_SECRET_KEY_ID); + mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java index 4b40b7ef1..b33bb2763 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java @@ -2,12 +2,14 @@ package org.sufficientlysecure.keychain.ui.dialog; import android.app.Activity; import android.app.AlertDialog; +import android.view.ContextThemeWrapper; import android.view.View; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -/** This class extends AlertDiaog.Builder, styling the header using emphasis color. +/** + * This class extends AlertDiaog.Builder, styling the header using emphasis color. * Note that this class is a huge hack, because dialog boxes aren't easily stylable. * Also, the dialog NEEDS to be called with show() directly, not create(), otherwise * the order of internal operations will lead to a crash! @@ -15,7 +17,9 @@ import org.sufficientlysecure.keychain.R; public class CustomAlertDialogBuilder extends AlertDialog.Builder { public CustomAlertDialogBuilder(Activity activity) { - super(activity); + // if the progress dialog is displayed from the application class, design is missing + // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay + super(new ContextThemeWrapper(activity, R.style.Theme_AppCompat_Light)); } @Override -- cgit v1.2.3