introduce CachingDataDecryptorFactory towards cached session keys

this commit introduces the CachingDataDecryptorFactory, which wraps
a DataDecryptorFactory but supports caching of decrypted session keys.

this change also gets rid of runtimeexception based control flow in
PgpDecryptVerify.
This commit is contained in:
Vincent Breitmoser
2015-06-01 00:05:55 +02:00
parent 403f74f558
commit dbfa55f6b9
8 changed files with 145 additions and 319 deletions

View File

@@ -22,6 +22,7 @@ import android.os.Parcel;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -36,6 +37,8 @@ public class DecryptVerifyResult extends InputPendingResult {
// https://tools.ietf.org/html/rfc4880#page56
String mCharset;
CryptoInputParcel mCachedCryptoInputParcel;
byte[] mOutputBytes;
public DecryptVerifyResult(int result, OperationLog log) {
@@ -50,6 +53,7 @@ public class DecryptVerifyResult extends InputPendingResult {
super(source);
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
mCachedCryptoInputParcel = source.readParcelable(CryptoInputParcel.class.getClassLoader());
}
@@ -65,6 +69,14 @@ public class DecryptVerifyResult extends InputPendingResult {
mSignatureResult = signatureResult;
}
public CryptoInputParcel getCachedCryptoInputParcel() {
return mCachedCryptoInputParcel;
}
public void setCachedCryptoInputParcel(CryptoInputParcel cachedCryptoInputParcel) {
mCachedCryptoInputParcel = cachedCryptoInputParcel;
}
public OpenPgpMetadata getDecryptMetadata() {
return mDecryptMetadata;
}
@@ -97,6 +109,7 @@ public class DecryptVerifyResult extends InputPendingResult {
super.writeToParcel(dest, flags);
dest.writeParcelable(mSignatureResult, 0);
dest.writeParcelable(mDecryptMetadata, 0);
dest.writeParcelable(mCachedCryptoInputParcel, 0);
}
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {

View File

@@ -21,23 +21,18 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
@@ -51,7 +46,6 @@ import java.security.interfaces.RSAPrivateCrtKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -270,19 +264,20 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
}
public PublicKeyDataDecryptorFactory getDecryptorFactory(CryptoInputParcel cryptoInput) {
public CachingDataDecryptorFactory getCachingDecryptorFactory(CryptoInputParcel cryptoInput) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
cryptoInput.getCryptoData()
);
return new CachingDataDecryptorFactory(
Constants.BOUNCY_CASTLE_PROVIDER_NAME,
cryptoInput.getCryptoData());
} else {
return new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
return new CachingDataDecryptorFactory(
new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey),
cryptoInput.getCryptoData());
}
}

View File

@@ -24,6 +24,7 @@ import android.webkit.MimeTypeMap;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.PublicKeyEncSessionPacket;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@@ -40,11 +41,10 @@ import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
@@ -541,24 +541,33 @@ public class PgpDecryptVerify extends BaseOperation {
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
try {
PublicKeyDataDecryptorFactory decryptorFactory
= secretEncryptionKey.getDecryptorFactory(cryptoInput);
try {
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
} catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
CachingDataDecryptorFactory decryptorFactory
= secretEncryptionKey.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
if (!decryptorFactory.canDecrypt()
&& !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
secretEncryptionKey.getRing().getMasterKeyId(),
secretEncryptionKey.getKeyId(), e.encryptedSessionKey
secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
));
}
try {
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
} catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
encryptedData = encryptedDataAsymmetric;
} else {
// there wasn't even any useful data
@@ -821,6 +830,7 @@ public class PgpDecryptVerify extends BaseOperation {
// Return a positive result, with metadata and verification info
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setCachedCryptoInputParcel(cryptoInput);
result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
result.setCharset(charset);

View File

@@ -97,8 +97,12 @@ public class CryptoInputParcel implements Parcelable {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}
public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
mCryptoData.putAll(cachedSessionKeys);
}
public Map<ByteBuffer, byte[]> getCryptoData() {
return Collections.unmodifiableMap(mCryptoData);
return mCryptoData;
}
public Date getSignatureTime() {
@@ -138,4 +142,5 @@ public class CryptoInputParcel implements Parcelable {
b.append("}");
return b.toString();
}
}

View File

@@ -48,7 +48,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
public class DecryptFilesFragment extends DecryptFragment {
public class DecryptFilesInputFragment extends DecryptFragment {
public static final String ARG_URI = "uri";
public static final String ARG_OPEN_DIRECTLY = "open_directly";
@@ -69,8 +69,8 @@ public class DecryptFilesFragment extends DecryptFragment {
/**
* Creates new instance of this fragment
*/
public static DecryptFilesFragment newInstance(Uri uri, boolean openDirectly) {
DecryptFilesFragment frag = new DecryptFilesFragment();
public static DecryptFilesInputFragment newInstance(Uri uri, boolean openDirectly) {
DecryptFilesInputFragment frag = new DecryptFilesInputFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_URI, uri);
@@ -94,9 +94,9 @@ public class DecryptFilesFragment extends DecryptFragment {
view.findViewById(R.id.decrypt_files_browse).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
} else {
FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*",
FileHelper.openFile(DecryptFilesInputFragment.this, mInputUri, "*/*",
REQUEST_CODE_INPUT);
}
}
@@ -128,9 +128,9 @@ public class DecryptFilesFragment extends DecryptFragment {
// should only come from args
if (state.getBoolean(ARG_OPEN_DIRECTLY, false)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
} else {
FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*", REQUEST_CODE_INPUT);
FileHelper.openFile(DecryptFilesInputFragment.this, mInputUri, "*/*", REQUEST_CODE_INPUT);
}
}
}
@@ -180,6 +180,10 @@ public class DecryptFilesFragment extends DecryptFragment {
}
}
private void displayMetadata(DecryptVerifyResult result) {
}
private void startDecrypt() {
mCurrentCryptoOperation = KeychainIntentService.ACTION_DECRYPT_VERIFY;
cryptoOperation(new CryptoInputParcel());
@@ -233,18 +237,19 @@ public class DecryptFilesFragment extends DecryptFragment {
// get returned data bundle
Bundle returnData = message.getData();
DecryptVerifyResult pgpResult =
DecryptVerifyResult result =
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
if (pgpResult.success()) {
if (result.success()) {
switch (mCurrentCryptoOperation) {
case KeychainIntentService.ACTION_DECRYPT_METADATA: {
askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
displayMetadata(result);
// askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
break;
}
case KeychainIntentService.ACTION_DECRYPT_VERIFY: {
// display signature result in activity
loadVerifyResult(pgpResult);
loadVerifyResult(result);
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
@@ -269,7 +274,7 @@ public class DecryptFilesFragment extends DecryptFragment {
}
}
}
pgpResult.createNotify(getActivity()).show(DecryptFilesFragment.this);
result.createNotify(getActivity()).show(DecryptFilesInputFragment.this);
}
}