performance: avoid expensive getSecretKeyType call, use cached where possible
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>() {
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)});
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user