diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
8 files changed, 152 insertions, 145 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index 4ad75fde1..79b42ecc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; @@ -75,24 +76,22 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> { // Retrieve and unlock secret key CanonicalizedSecretKey certificationKey; + long masterKeyId = parcel.mMasterKeyId; try { log.add(LogType.MSG_CRT_MASTER_FETCH, 1); - CanonicalizedSecretKeyRing secretKeyRing = - mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId); - log.add(LogType.MSG_CRT_UNLOCK, 1); - certificationKey = secretKeyRing.getSecretKey(); + CachedPublicKeyRing cachedPublicKeyRing = mProviderHelper.getCachedPublicKeyRing(masterKeyId); Passphrase passphrase; - switch (certificationKey.getSecretKeyType()) { + switch (cachedPublicKeyRing.getSecretKeyType(masterKeyId)) { case PIN: case PATTERN: case PASSPHRASE: passphrase = cryptoInput.getPassphrase(); if (passphrase == null) { try { - passphrase = getCachedPassphrase(certificationKey.getKeyId(), certificationKey.getKeyId()); + passphrase = getCachedPassphrase(masterKeyId, masterKeyId); } catch (PassphraseCacheInterface.NoSecretKeyException ignored) { // treat as a cache miss for error handling purposes } @@ -100,10 +99,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> { if (passphrase == null) { return new CertifyResult(log, - RequiredInputParcel.createRequiredSignPassphrase( - certificationKey.getKeyId(), - certificationKey.getKeyId(), - null), + RequiredInputParcel.createRequiredSignPassphrase(masterKeyId, masterKeyId, null), cryptoInput ); } @@ -123,7 +119,14 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> { return new CertifyResult(CertifyResult.RESULT_ERROR, log); } - if (!certificationKey.unlock(passphrase)) { + // Get actual secret key + CanonicalizedSecretKeyRing secretKeyRing = + mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId); + certificationKey = secretKeyRing.getSecretKey(); + + log.add(LogType.MSG_CRT_UNLOCK, 1); + boolean unlockSuccessful = certificationKey.unlock(passphrase); + if (!unlockSuccessful) { log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); return new CertifyResult(CertifyResult.RESULT_ERROR, log); } @@ -142,8 +145,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> { int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0; NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder( - cryptoInput.getSignatureTime(), certificationKey.getKeyId(), - certificationKey.getKeyId()); + cryptoInput.getSignatureTime(), masterKeyId, masterKeyId); // Work through all requested certifications for (CertifyAction action : parcel.mCertifyActions) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index 7f2a00617..95a0d41cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -120,7 +120,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } - public SecretKeyType getSecretKeyType() { + // This method can potentially take a LONG time (i.e. seconds), so it should only + // ever be called by ProviderHelper to be cached in the database. + public SecretKeyType getSecretKeyTypeSuperExpensive() { S2K s2k = mSecretKey.getS2K(); if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K) { // divert to card is special diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java index 97b5fa6fe..1df16254f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java @@ -70,20 +70,6 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing { return new CanonicalizedSecretKey(this, mRing.getSecretKey(id)); } - /** Returns the key id which should be used for signing. - * - * This method returns keys which are actually available (ie. secret available, and not stripped, - * revoked, or expired), hence only works on keyrings where a secret key is available! - */ - public long getSecretSignId() throws PgpGeneralException { - for(CanonicalizedSecretKey key : secretKeyIterator()) { - if (key.canSign() && key.isValid() && key.getSecretKeyType().isUsable()) { - return key.getKeyId(); - } - } - throw new PgpGeneralException("no valid signing key available"); - } - public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() { final Iterator<PGPSecretKey> it = mRing.getSecretKeys(); return new IterableIterator<>(new Iterator<CanonicalizedSecretKey>() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index 79a7a8fe1..3fc020aa7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -64,6 +64,8 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; @@ -539,7 +541,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null; - CanonicalizedSecretKey secretEncryptionKey = null; + CanonicalizedSecretKey decryptionKey = null; Passphrase passphrase = null; @@ -560,85 +562,87 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp log.add(LogType.MSG_DC_ASYM, indent, KeyFormattingUtils.convertKeyIdToHex(subKeyId)); - CanonicalizedSecretKeyRing secretKeyRing; + CachedPublicKeyRing cachedPublicKeyRing; try { // get actual keyring object based on master key id - secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing( + cachedPublicKeyRing = mProviderHelper.getCachedPublicKeyRing( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId) ); - } catch (ProviderHelper.NotFoundException e) { - // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); - continue; - } + long masterKeyId = cachedPublicKeyRing.getMasterKeyId(); + + // allow only specific keys for decryption? + if (input.getAllowedKeyIds() != null) { + Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); + Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); + + if (!input.getAllowedKeyIds().contains(masterKeyId)) { + // this key is in our db, but NOT allowed! + // continue with the next packet in the while loop + result.skippedDisallowedKey = true; + log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); + continue; + } + } - // allow only specific keys for decryption? - if (input.getAllowedKeyIds() != null) { - long masterKeyId = secretKeyRing.getMasterKeyId(); - Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); - Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); - Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); - - if (!input.getAllowedKeyIds().contains(masterKeyId)) { - // this key is in our db, but NOT allowed! - // continue with the next packet in the while loop - result.skippedDisallowedKey = true; - log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); + SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(subKeyId); + if (!secretKeyType.isUsable()) { + decryptionKey = null; + log.add(LogType.MSG_DC_ASKIP_UNAVAILABLE, indent + 1); continue; } - } - - // get subkey which has been used for this encryption packet - secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); - if (!secretEncryptionKey.canEncrypt()) { - secretEncryptionKey = null; - log.add(LogType.MSG_DC_ASKIP_BAD_FLAGS, indent + 1); - continue; - } - - if (!secretEncryptionKey.getSecretKeyType().isUsable()) { - secretEncryptionKey = null; - log.add(LogType.MSG_DC_ASKIP_UNAVAILABLE, indent + 1); - continue; - } + // get actual subkey which has been used for this encryption packet + CanonicalizedSecretKeyRing canonicalizedSecretKeyRing = mProviderHelper + .getCanonicalizedSecretKeyRing(masterKeyId); + CanonicalizedSecretKey candidateDecryptionKey = canonicalizedSecretKeyRing.getSecretKey(subKeyId); - /* secret key exists in database and is allowed! */ - asymmetricPacketFound = true; + if (!candidateDecryptionKey.canEncrypt()) { + log.add(LogType.MSG_DC_ASKIP_BAD_FLAGS, indent + 1); + continue; + } - encryptedDataAsymmetric = encData; + if (secretKeyType == SecretKeyType.DIVERT_TO_CARD) { + passphrase = null; + } else if (secretKeyType == SecretKeyType.PASSPHRASE_EMPTY) { + passphrase = new Passphrase(""); + } else if (cryptoInput.hasPassphrase()) { + passphrase = cryptoInput.getPassphrase(); + } else { + // if no passphrase was explicitly set try to get it from the cache service + try { + // returns "" if key has no passphrase + passphrase = getCachedPassphrase(subKeyId); + log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); + } catch (PassphraseCacheInterface.NoSecretKeyException e) { + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); + } - if (secretEncryptionKey.getSecretKeyType() == SecretKeyType.DIVERT_TO_CARD) { - passphrase = null; - } else if (secretKeyType == SecretKeyType.PASSPHRASE_EMPTY) { - passphrase = new Passphrase(""); - } else if (cryptoInput.hasPassphrase()) { - passphrase = cryptoInput.getPassphrase(); - } else { - // if no passphrase was explicitly set try to get it from the cache service - try { - // returns "" if key has no passphrase - passphrase = getCachedPassphrase(subKeyId); - log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); - } catch (PassphraseCacheInterface.NoSecretKeyException e) { - log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); - return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); + // if passphrase was not cached, return here indicating that a passphrase is missing! + if (passphrase == null) { + log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); + return result.with(new DecryptVerifyResult(log, + RequiredInputParcel.createRequiredDecryptPassphrase(masterKeyId, subKeyId), + cryptoInput)); + } } - // if passphrase was not cached, return here indicating that a passphrase is missing! - if (passphrase == null) { - log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); - return result.with(new DecryptVerifyResult(log, - RequiredInputParcel.createRequiredDecryptPassphrase( - secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()), - cryptoInput)); + // check for insecure encryption key + if ( ! PgpSecurityConstants.isSecureKey(candidateDecryptionKey)) { + log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); + result.insecureEncryptionKey = true; } - } - // check for insecure encryption key - if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) { - log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); - result.insecureEncryptionKey = true; + // we're good, write down the data for later + asymmetricPacketFound = true; + encryptedDataAsymmetric = encData; + decryptionKey = candidateDecryptionKey; + + } catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) { + // continue with the next packet in the while loop + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); + continue; } // break out of while, only decrypt the first packet where we have a key @@ -737,7 +741,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp try { log.add(LogType.MSG_DC_UNLOCKING, indent + 1); - if (!secretEncryptionKey.unlock(passphrase)) { + if (!decryptionKey.unlock(passphrase)) { log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); } @@ -750,7 +754,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp updateProgress(R.string.progress_preparing_streams, currentProgress, 100); CachingDataDecryptorFactory decryptorFactory - = secretEncryptionKey.getCachingDecryptorFactory(cryptoInput); + = decryptionKey.getCachingDecryptorFactory(cryptoInput); // special case: if the decryptor does not have a session key cached for this encrypted // data, and can't actually decrypt on its own, return a pending intent @@ -759,8 +763,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation( - secretEncryptionKey.getRing().getMasterKeyId(), - secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] + decryptionKey.getRing().getMasterKeyId(), + decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] ), cryptoInput)); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 1290dcdcf..a418075c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -166,12 +166,13 @@ public class PgpSignEncryptOperation extends BaseOperation { updateProgress(R.string.progress_extracting_signature_key, 0, 100); try { - // fetch the indicated master key id (the one whose name we sign in) - CanonicalizedSecretKeyRing signingKeyRing = - mProviderHelper.getCanonicalizedSecretKeyRing(input.getSignatureMasterKeyId()); - - // fetch the specific subkey to sign with, or just use the master key if none specified - signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId()); + long signingMasterKeyId = input.getSignatureMasterKeyId(); + long signingSubKeyId = input.getSignatureSubKeyId(); + { + CanonicalizedSecretKeyRing signingKeyRing = + mProviderHelper.getCanonicalizedSecretKeyRing(signingMasterKeyId); + signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId()); + } // Make sure we are allowed to sign here! if (!signingKey.canSign()) { @@ -179,7 +180,7 @@ public class PgpSignEncryptOperation extends BaseOperation { return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } - switch (signingKey.getSecretKeyType()) { + switch (mProviderHelper.getCachedPublicKeyRing(signingMasterKeyId).getSecretKeyType(signingSubKeyId)) { case DIVERT_TO_CARD: case PASSPHRASE_EMPTY: { if (!signingKey.unlock(new Passphrase())) { @@ -196,14 +197,14 @@ public class PgpSignEncryptOperation extends BaseOperation { Passphrase localPassphrase = cryptoInput.getPassphrase(); if (localPassphrase == null) { try { - localPassphrase = getCachedPassphrase(signingKeyRing.getMasterKeyId(), signingKey.getKeyId()); + localPassphrase = getCachedPassphrase(signingMasterKeyId, signingKey.getKeyId()); } catch (PassphraseCacheInterface.NoSecretKeyException ignored) { } } if (localPassphrase == null) { log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1); return new PgpSignEncryptResult(log, RequiredInputParcel.createRequiredSignPassphrase( - signingKeyRing.getMasterKeyId(), signingKey.getKeyId(), + signingMasterKeyId, signingKey.getKeyId(), cryptoInput.getSignatureTime()), cryptoInput); } if (!signingKey.unlock(localPassphrase)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java index 7c9ef719e..604a5a027 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -74,7 +74,7 @@ public class CachedPublicKeyRing extends KeyRing { public long extractOrGetMasterKeyId() throws PgpKeyNotFoundException { // try extracting from the uri first String firstSegment = mUri.getPathSegments().get(1); - if (!firstSegment.equals("find")) try { + if (!"find".equals(firstSegment)) try { return Long.parseLong(firstSegment); } catch (NumberFormatException e) { // didn't work? oh well. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 375775ff1..505d3eb83 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; @@ -254,8 +255,9 @@ public class ProviderHelper { KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER); } - public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) { - return new CachedPublicKeyRing(this, queryUri); + public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException { + long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId(); + return getCachedPublicKeyRing(masterKeyId); } public CachedPublicKeyRing getCachedPublicKeyRing(long id) { @@ -828,7 +830,7 @@ public class ProviderHelper { mIndent += 1; for (CanonicalizedSecretKey sub : keyRing.secretKeyIterator()) { long id = sub.getKeyId(); - SecretKeyType mode = sub.getSecretKeyType(); + SecretKeyType mode = sub.getSecretKeyTypeSuperExpensive(); values.put(Keys.HAS_SECRET, mode.getNum()); int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{Long.toString(id)}); 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 a973a67e0..88616117f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; @@ -112,12 +113,10 @@ public class PassphraseDialogActivity extends FragmentActivity { // handle empty passphrases by directly returning an empty crypto input parcel try { - CanonicalizedSecretKeyRing pubRing = - new ProviderHelper(this).getCanonicalizedSecretKeyRing( - requiredInput.getMasterKeyId()); + CachedPublicKeyRing pubRing = + new ProviderHelper(this).getCachedPublicKeyRing(requiredInput.getMasterKeyId()); // use empty passphrase for empty passphrase - if (pubRing.getSecretKey(requiredInput.getSubKeyId()).getSecretKeyType() == - SecretKeyType.PASSPHRASE_EMPTY) { + if (pubRing.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) { // also return passphrase back to activity Intent returnIntent = new Intent(); cryptoInputParcel.mPassphrase = new Passphrase(""); @@ -161,7 +160,6 @@ public class PassphraseDialogActivity extends FragmentActivity { private TextView mPassphraseText; private EditText[] mBackupCodeEditText; - private CanonicalizedSecretKeyRing mSecretRing = null; private boolean mIsCancelled = false; private RequiredInputParcel mRequiredInput; @@ -233,24 +231,20 @@ public class PassphraseDialogActivity extends FragmentActivity { long subKeyId = mRequiredInput.getSubKeyId(); ProviderHelper helper = new ProviderHelper(activity); - mSecretRing = helper.getCanonicalizedSecretKeyRing( + CachedPublicKeyRing cachedPublicKeyRing = helper.getCachedPublicKeyRing( KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); // yes the inner try/catch block is necessary, otherwise the final variable // above can't be statically verified to have been set in all cases because // the catch clause doesn't return. - try { - String mainUserId = mSecretRing.getPrimaryUserIdWithFallback(); - KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId); - if (mainUserIdSplit.name != null) { - userId = mainUserIdSplit.name; - } else { - userId = getString(R.string.user_id_no_name); - } - } catch (PgpKeyNotFoundException e) { - userId = null; + String mainUserId = cachedPublicKeyRing.getPrimaryUserIdWithFallback(); + KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId); + if (mainUserIdSplit.name != null) { + userId = mainUserIdSplit.name; + } else { + userId = getString(R.string.user_id_no_name); } - keyType = mSecretRing.getSecretKey(subKeyId).getSecretKeyType(); + keyType = cachedPublicKeyRing.getSecretKeyType(subKeyId); switch (keyType) { case PASSPHRASE: message = getString(R.string.passphrase_for, userId); @@ -271,7 +265,7 @@ public class PassphraseDialogActivity extends FragmentActivity { throw new AssertionError("Unhandled SecretKeyType (should not happen)"); } - } catch (ProviderHelper.NotFoundException e) { + } catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) { alert.setTitle(R.string.title_key_not_found); alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId())); alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -389,7 +383,7 @@ public class PassphraseDialogActivity extends FragmentActivity { final int timeToLiveSeconds = mTimeToLiveSpinner.getSelectedTimeToLive(); // Early breakout if we are dealing with a symmetric key - if (mSecretRing == null) { + if (mRequiredInput.mType == RequiredInputType.PASSPHRASE_SYMMETRIC) { if (!mRequiredInput.mSkipCaching) { PassphraseCacheService.addCachedPassphrase(getActivity(), Constants.key.symmetric, Constants.key.symmetric, passphrase, @@ -403,32 +397,48 @@ public class PassphraseDialogActivity extends FragmentActivity { mLayout.setDisplayedChild(1); positive.setEnabled(false); - new AsyncTask<Void, Void, Boolean>() { + new AsyncTask<Void, Void, CanonicalizedSecretKey>() { + @Override - protected Boolean doInBackground(Void... params) { + protected CanonicalizedSecretKey doInBackground(Void... params) { try { - // wait some 100ms here, give the user time to appreciate the progress bar - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // never mind + long timeBeforeOperation = System.currentTimeMillis(); + + Long subKeyId = mRequiredInput.getSubKeyId(); + CanonicalizedSecretKeyRing secretKeyRing = + new ProviderHelper(getActivity()).getCanonicalizedSecretKeyRing( + KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); + CanonicalizedSecretKey secretKeyToUnlock = + secretKeyRing.getSecretKey(subKeyId); + + // this is the operation may take a very long time (100ms to several seconds!) + boolean unlockSucceeded = secretKeyToUnlock.unlock(passphrase); + + // if it didn't take that long, give the user time to appreciate the progress bar + long operationTime = System.currentTimeMillis() -timeBeforeOperation; + if (operationTime < 100) { + try { + Thread.sleep(100 -operationTime); + } catch (InterruptedException e) { + // ignore + } } - // make sure this unlocks - return mSecretRing.getSecretKey(mRequiredInput.getSubKeyId()).unlock(passphrase); - } catch (PgpGeneralException e) { + + return unlockSucceeded ? secretKeyToUnlock : null; + } catch (NotFoundException | PgpGeneralException e) { Toast.makeText(getActivity(), R.string.error_could_not_extract_private_key, Toast.LENGTH_SHORT).show(); getActivity().setResult(RESULT_CANCELED); dismiss(); getActivity().finish(); - return false; + return null; } } /** Handle a good or bad passphrase. This happens in the UI thread! */ @Override - protected void onPostExecute(Boolean result) { + protected void onPostExecute(CanonicalizedSecretKey result) { super.onPostExecute(result); // if we were cancelled in the meantime, the result isn't relevant anymore @@ -437,7 +447,7 @@ public class PassphraseDialogActivity extends FragmentActivity { } // if the passphrase was wrong, reset and re-enable the dialogue - if (!result) { + if (result == null) { mPassphraseEditText.setText(""); mPassphraseEditText.setError(getString(R.string.wrong_passphrase)); mLayout.setDisplayedChild(0); @@ -455,8 +465,8 @@ public class PassphraseDialogActivity extends FragmentActivity { try { PassphraseCacheService.addCachedPassphrase(getActivity(), - mSecretRing.getMasterKeyId(), mRequiredInput.getSubKeyId(), passphrase, - mSecretRing.getPrimaryUserIdWithFallback(), timeToLiveSeconds); + mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId(), passphrase, + result.getRing().getPrimaryUserIdWithFallback(), timeToLiveSeconds); } catch (PgpKeyNotFoundException e) { Log.e(Constants.TAG, "adding of a passphrase failed", e); } |