performance: avoid expensive getSecretKeyType call, use cached where possible

This commit is contained in:
Vincent Breitmoser
2016-02-01 15:22:36 +01:00
parent e3b8cea04d
commit b1ea126142
8 changed files with 160 additions and 153 deletions

View File

@@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; 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;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
@@ -75,24 +76,22 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
// Retrieve and unlock secret key // Retrieve and unlock secret key
CanonicalizedSecretKey certificationKey; CanonicalizedSecretKey certificationKey;
long masterKeyId = parcel.mMasterKeyId;
try { try {
log.add(LogType.MSG_CRT_MASTER_FETCH, 1); 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; Passphrase passphrase;
switch (certificationKey.getSecretKeyType()) { switch (cachedPublicKeyRing.getSecretKeyType(masterKeyId)) {
case PIN: case PIN:
case PATTERN: case PATTERN:
case PASSPHRASE: case PASSPHRASE:
passphrase = cryptoInput.getPassphrase(); passphrase = cryptoInput.getPassphrase();
if (passphrase == null) { if (passphrase == null) {
try { try {
passphrase = getCachedPassphrase(certificationKey.getKeyId(), certificationKey.getKeyId()); passphrase = getCachedPassphrase(masterKeyId, masterKeyId);
} catch (PassphraseCacheInterface.NoSecretKeyException ignored) { } catch (PassphraseCacheInterface.NoSecretKeyException ignored) {
// treat as a cache miss for error handling purposes // treat as a cache miss for error handling purposes
} }
@@ -100,10 +99,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
if (passphrase == null) { if (passphrase == null) {
return new CertifyResult(log, return new CertifyResult(log,
RequiredInputParcel.createRequiredSignPassphrase( RequiredInputParcel.createRequiredSignPassphrase(masterKeyId, masterKeyId, null),
certificationKey.getKeyId(),
certificationKey.getKeyId(),
null),
cryptoInput cryptoInput
); );
} }
@@ -123,7 +119,14 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
return new CertifyResult(CertifyResult.RESULT_ERROR, log); 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); log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
return new CertifyResult(CertifyResult.RESULT_ERROR, log); 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; int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0;
NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder( NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(
cryptoInput.getSignatureTime(), certificationKey.getKeyId(), cryptoInput.getSignatureTime(), masterKeyId, masterKeyId);
certificationKey.getKeyId());
// Work through all requested certifications // Work through all requested certifications
for (CertifyAction action : parcel.mCertifyActions) { for (CertifyAction action : parcel.mCertifyActions) {

View File

@@ -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(); S2K s2k = mSecretKey.getS2K();
if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K) { if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K) {
// divert to card is special // divert to card is special

View File

@@ -70,20 +70,6 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
return new CanonicalizedSecretKey(this, mRing.getSecretKey(id)); 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() { public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys(); final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
return new IterableIterator<>(new Iterator<CanonicalizedSecretKey>() { return new IterableIterator<>(new Iterator<CanonicalizedSecretKey>() {

View File

@@ -64,6 +64,8 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; 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.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -539,7 +541,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null;
CanonicalizedSecretKey secretEncryptionKey = null; CanonicalizedSecretKey decryptionKey = null;
Passphrase passphrase = null; Passphrase passphrase = null;
@@ -560,87 +562,89 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_ASYM, indent, log.add(LogType.MSG_DC_ASYM, indent,
KeyFormattingUtils.convertKeyIdToHex(subKeyId)); KeyFormattingUtils.convertKeyIdToHex(subKeyId));
CanonicalizedSecretKeyRing secretKeyRing; CachedPublicKeyRing cachedPublicKeyRing;
try { try {
// get actual keyring object based on master key id // get actual keyring object based on master key id
secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing( cachedPublicKeyRing = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId) KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)
); );
} catch (ProviderHelper.NotFoundException e) { 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;
}
}
SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(subKeyId);
if (!secretKeyType.isUsable()) {
decryptionKey = 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);
if (!candidateDecryptionKey.canEncrypt()) {
log.add(LogType.MSG_DC_ASKIP_BAD_FLAGS, indent + 1);
continue;
}
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 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));
}
}
// check for insecure encryption key
if ( ! PgpSecurityConstants.isSecureKey(candidateDecryptionKey)) {
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 // continue with the next packet in the while loop
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
continue; 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);
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;
}
/* secret key exists in database and is allowed! */
asymmetricPacketFound = true;
encryptedDataAsymmetric = encData;
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(
secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()),
cryptoInput));
}
}
// check for insecure encryption key
if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) {
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
result.insecureEncryptionKey = true;
}
// 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
break; break;
@@ -737,7 +741,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
try { try {
log.add(LogType.MSG_DC_UNLOCKING, indent + 1); 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); log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log)); 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); updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
CachingDataDecryptorFactory decryptorFactory CachingDataDecryptorFactory decryptorFactory
= secretEncryptionKey.getCachingDecryptorFactory(cryptoInput); = decryptionKey.getCachingDecryptorFactory(cryptoInput);
// special case: if the decryptor does not have a session key cached for this encrypted // 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 // 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); log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation( return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
secretEncryptionKey.getRing().getMasterKeyId(), decryptionKey.getRing().getMasterKeyId(),
secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
), cryptoInput)); ), cryptoInput));
} }

View File

@@ -166,12 +166,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
updateProgress(R.string.progress_extracting_signature_key, 0, 100); updateProgress(R.string.progress_extracting_signature_key, 0, 100);
try { try {
// fetch the indicated master key id (the one whose name we sign in) long signingMasterKeyId = input.getSignatureMasterKeyId();
CanonicalizedSecretKeyRing signingKeyRing = long signingSubKeyId = input.getSignatureSubKeyId();
mProviderHelper.getCanonicalizedSecretKeyRing(input.getSignatureMasterKeyId()); {
CanonicalizedSecretKeyRing signingKeyRing =
// fetch the specific subkey to sign with, or just use the master key if none specified mProviderHelper.getCanonicalizedSecretKeyRing(signingMasterKeyId);
signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId()); signingKey = signingKeyRing.getSecretKey(input.getSignatureSubKeyId());
}
// Make sure we are allowed to sign here! // Make sure we are allowed to sign here!
if (!signingKey.canSign()) { if (!signingKey.canSign()) {
@@ -179,7 +180,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} }
switch (signingKey.getSecretKeyType()) { switch (mProviderHelper.getCachedPublicKeyRing(signingMasterKeyId).getSecretKeyType(signingSubKeyId)) {
case DIVERT_TO_CARD: case DIVERT_TO_CARD:
case PASSPHRASE_EMPTY: { case PASSPHRASE_EMPTY: {
if (!signingKey.unlock(new Passphrase())) { if (!signingKey.unlock(new Passphrase())) {
@@ -196,14 +197,14 @@ public class PgpSignEncryptOperation extends BaseOperation {
Passphrase localPassphrase = cryptoInput.getPassphrase(); Passphrase localPassphrase = cryptoInput.getPassphrase();
if (localPassphrase == null) { if (localPassphrase == null) {
try { try {
localPassphrase = getCachedPassphrase(signingKeyRing.getMasterKeyId(), signingKey.getKeyId()); localPassphrase = getCachedPassphrase(signingMasterKeyId, signingKey.getKeyId());
} catch (PassphraseCacheInterface.NoSecretKeyException ignored) { } catch (PassphraseCacheInterface.NoSecretKeyException ignored) {
} }
} }
if (localPassphrase == null) { if (localPassphrase == null) {
log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1); log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1);
return new PgpSignEncryptResult(log, RequiredInputParcel.createRequiredSignPassphrase( return new PgpSignEncryptResult(log, RequiredInputParcel.createRequiredSignPassphrase(
signingKeyRing.getMasterKeyId(), signingKey.getKeyId(), signingMasterKeyId, signingKey.getKeyId(),
cryptoInput.getSignatureTime()), cryptoInput); cryptoInput.getSignatureTime()), cryptoInput);
} }
if (!signingKey.unlock(localPassphrase)) { if (!signingKey.unlock(localPassphrase)) {

View File

@@ -74,7 +74,7 @@ public class CachedPublicKeyRing extends KeyRing {
public long extractOrGetMasterKeyId() throws PgpKeyNotFoundException { public long extractOrGetMasterKeyId() throws PgpKeyNotFoundException {
// try extracting from the uri first // try extracting from the uri first
String firstSegment = mUri.getPathSegments().get(1); String firstSegment = mUri.getPathSegments().get(1);
if (!firstSegment.equals("find")) try { if (!"find".equals(firstSegment)) try {
return Long.parseLong(firstSegment); return Long.parseLong(firstSegment);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// didn't work? oh well. // didn't work? oh well.

View File

@@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
@@ -254,8 +255,9 @@ public class ProviderHelper {
KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER); KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
} }
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) { public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {
return new CachedPublicKeyRing(this, queryUri); long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId();
return getCachedPublicKeyRing(masterKeyId);
} }
public CachedPublicKeyRing getCachedPublicKeyRing(long id) { public CachedPublicKeyRing getCachedPublicKeyRing(long id) {
@@ -828,7 +830,7 @@ public class ProviderHelper {
mIndent += 1; mIndent += 1;
for (CanonicalizedSecretKey sub : keyRing.secretKeyIterator()) { for (CanonicalizedSecretKey sub : keyRing.secretKeyIterator()) {
long id = sub.getKeyId(); long id = sub.getKeyId();
SecretKeyType mode = sub.getSecretKeyType(); SecretKeyType mode = sub.getSecretKeyTypeSuperExpensive();
values.put(Keys.HAS_SECRET, mode.getNum()); values.put(Keys.HAS_SECRET, mode.getNum());
int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?",
new String[]{Long.toString(id)}); new String[]{Long.toString(id)});

View File

@@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; 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 // handle empty passphrases by directly returning an empty crypto input parcel
try { try {
CanonicalizedSecretKeyRing pubRing = CachedPublicKeyRing pubRing =
new ProviderHelper(this).getCanonicalizedSecretKeyRing( new ProviderHelper(this).getCachedPublicKeyRing(requiredInput.getMasterKeyId());
requiredInput.getMasterKeyId());
// use empty passphrase for empty passphrase // use empty passphrase for empty passphrase
if (pubRing.getSecretKey(requiredInput.getSubKeyId()).getSecretKeyType() == if (pubRing.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) {
SecretKeyType.PASSPHRASE_EMPTY) {
// also return passphrase back to activity // also return passphrase back to activity
Intent returnIntent = new Intent(); Intent returnIntent = new Intent();
cryptoInputParcel.mPassphrase = new Passphrase(""); cryptoInputParcel.mPassphrase = new Passphrase("");
@@ -161,7 +160,6 @@ public class PassphraseDialogActivity extends FragmentActivity {
private TextView mPassphraseText; private TextView mPassphraseText;
private EditText[] mBackupCodeEditText; private EditText[] mBackupCodeEditText;
private CanonicalizedSecretKeyRing mSecretRing = null;
private boolean mIsCancelled = false; private boolean mIsCancelled = false;
private RequiredInputParcel mRequiredInput; private RequiredInputParcel mRequiredInput;
@@ -233,24 +231,20 @@ public class PassphraseDialogActivity extends FragmentActivity {
long subKeyId = mRequiredInput.getSubKeyId(); long subKeyId = mRequiredInput.getSubKeyId();
ProviderHelper helper = new ProviderHelper(activity); ProviderHelper helper = new ProviderHelper(activity);
mSecretRing = helper.getCanonicalizedSecretKeyRing( CachedPublicKeyRing cachedPublicKeyRing = helper.getCachedPublicKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
// yes the inner try/catch block is necessary, otherwise the final variable // 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 // above can't be statically verified to have been set in all cases because
// the catch clause doesn't return. // the catch clause doesn't return.
try { String mainUserId = cachedPublicKeyRing.getPrimaryUserIdWithFallback();
String mainUserId = mSecretRing.getPrimaryUserIdWithFallback(); KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId);
KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId); if (mainUserIdSplit.name != null) {
if (mainUserIdSplit.name != null) { userId = mainUserIdSplit.name;
userId = mainUserIdSplit.name; } else {
} else { userId = getString(R.string.user_id_no_name);
userId = getString(R.string.user_id_no_name);
}
} catch (PgpKeyNotFoundException e) {
userId = null;
} }
keyType = mSecretRing.getSecretKey(subKeyId).getSecretKeyType(); keyType = cachedPublicKeyRing.getSecretKeyType(subKeyId);
switch (keyType) { switch (keyType) {
case PASSPHRASE: case PASSPHRASE:
message = getString(R.string.passphrase_for, userId); message = getString(R.string.passphrase_for, userId);
@@ -271,7 +265,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
throw new AssertionError("Unhandled SecretKeyType (should not happen)"); 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.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId())); alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId()));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@@ -389,7 +383,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
final int timeToLiveSeconds = mTimeToLiveSpinner.getSelectedTimeToLive(); final int timeToLiveSeconds = mTimeToLiveSpinner.getSelectedTimeToLive();
// Early breakout if we are dealing with a symmetric key // Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) { if (mRequiredInput.mType == RequiredInputType.PASSPHRASE_SYMMETRIC) {
if (!mRequiredInput.mSkipCaching) { if (!mRequiredInput.mSkipCaching) {
PassphraseCacheService.addCachedPassphrase(getActivity(), PassphraseCacheService.addCachedPassphrase(getActivity(),
Constants.key.symmetric, Constants.key.symmetric, passphrase, Constants.key.symmetric, Constants.key.symmetric, passphrase,
@@ -403,32 +397,48 @@ public class PassphraseDialogActivity extends FragmentActivity {
mLayout.setDisplayedChild(1); mLayout.setDisplayedChild(1);
positive.setEnabled(false); positive.setEnabled(false);
new AsyncTask<Void, Void, Boolean>() { new AsyncTask<Void, Void, CanonicalizedSecretKey>() {
@Override @Override
protected Boolean doInBackground(Void... params) { protected CanonicalizedSecretKey doInBackground(Void... params) {
try { try {
// wait some 100ms here, give the user time to appreciate the progress bar long timeBeforeOperation = System.currentTimeMillis();
try {
Thread.sleep(100); Long subKeyId = mRequiredInput.getSubKeyId();
} catch (InterruptedException e) { CanonicalizedSecretKeyRing secretKeyRing =
// never mind 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); return unlockSucceeded ? secretKeyToUnlock : null;
} catch (PgpGeneralException e) { } catch (NotFoundException | PgpGeneralException e) {
Toast.makeText(getActivity(), R.string.error_could_not_extract_private_key, Toast.makeText(getActivity(), R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
getActivity().setResult(RESULT_CANCELED); getActivity().setResult(RESULT_CANCELED);
dismiss(); dismiss();
getActivity().finish(); getActivity().finish();
return false; return null;
} }
} }
/** Handle a good or bad passphrase. This happens in the UI thread! */ /** Handle a good or bad passphrase. This happens in the UI thread! */
@Override @Override
protected void onPostExecute(Boolean result) { protected void onPostExecute(CanonicalizedSecretKey result) {
super.onPostExecute(result); super.onPostExecute(result);
// if we were cancelled in the meantime, the result isn't relevant anymore // 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 the passphrase was wrong, reset and re-enable the dialogue
if (!result) { if (result == null) {
mPassphraseEditText.setText(""); mPassphraseEditText.setText("");
mPassphraseEditText.setError(getString(R.string.wrong_passphrase)); mPassphraseEditText.setError(getString(R.string.wrong_passphrase));
mLayout.setDisplayedChild(0); mLayout.setDisplayedChild(0);
@@ -455,8 +465,8 @@ public class PassphraseDialogActivity extends FragmentActivity {
try { try {
PassphraseCacheService.addCachedPassphrase(getActivity(), PassphraseCacheService.addCachedPassphrase(getActivity(),
mSecretRing.getMasterKeyId(), mRequiredInput.getSubKeyId(), passphrase, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId(), passphrase,
mSecretRing.getPrimaryUserIdWithFallback(), timeToLiveSeconds); result.getRing().getPrimaryUserIdWithFallback(), timeToLiveSeconds);
} catch (PgpKeyNotFoundException e) { } catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e); Log.e(Constants.TAG, "adding of a passphrase failed", e);
} }