api: allow caching of sessionKey in OpenPgpDecryptResult

This commit is contained in:
Vincent Breitmoser 2016-04-29 15:40:35 +02:00
parent 5fdc312b2d
commit 8e4d68c55a
6 changed files with 92 additions and 44 deletions

View file

@ -6,15 +6,16 @@
package org.bouncycastle.openpgp.operator.jcajce;
import java.nio.ByteBuffer;
import java.util.Map;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import java.nio.ByteBuffer;
import java.util.Map;
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
{
private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
@ -59,6 +60,10 @@ public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactor
return mSessionKeyCache.get(bi);
}
if (mWrappedDecryptor == null) {
throw new IllegalStateException("tried to decrypt without wrapped decryptor, this is a bug!");
}
byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
mSessionKeyCache.put(bi, sessionData);
return sessionData;

View file

@ -26,6 +26,8 @@ public class OpenPgpDecryptionResultBuilder {
// builder
private boolean mInsecure = false;
private boolean mEncrypted = false;
private byte[] sessionKey;
private byte[] decryptedSessionKey;
public void setInsecure(boolean insecure) {
this.mInsecure = insecure;
@ -36,24 +38,26 @@ public class OpenPgpDecryptionResultBuilder {
}
public OpenPgpDecryptionResult build() {
OpenPgpDecryptionResult result = new OpenPgpDecryptionResult();
if (mInsecure) {
Log.d(Constants.TAG, "RESULT_INSECURE");
result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE);
return result;
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_INSECURE, sessionKey, decryptedSessionKey);
}
if (mEncrypted) {
Log.d(Constants.TAG, "RESULT_ENCRYPTED");
result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED);
} else {
Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey);
}
return result;
Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
}
public void setSessionKey(byte[] sessionKey, byte[] decryptedSessionKey) {
if ((sessionKey == null) != (decryptedSessionKey == null)) {
throw new AssertionError("sessionKey must be null iff decryptedSessionKey is null!");
}
this.sessionKey = sessionKey;
this.decryptedSessionKey = decryptedSessionKey;
}
}

View file

@ -26,9 +26,12 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import android.content.Context;
import android.support.annotation.NonNull;
@ -60,7 +63,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.util.CharsetVerifier;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@ -73,6 +75,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.CharsetVerifier;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@ -197,6 +200,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
PGPEncryptedData encryptedData;
InputStream cleartextStream;
// the cached session key
byte[] sessionKey;
byte[] decryptedSessionKey;
int symmetricEncryptionAlgo = 0;
boolean skippedDisallowedKey = false;
@ -304,6 +311,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
// if this worked out so far, the data is encrypted
decryptionResultBuilder.setEncrypted(true);
if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
}
if (esResult.insecureEncryptionKey) {
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
@ -545,10 +555,14 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
boolean anyPacketFound = false;
boolean decryptedSessionKeyAvailable = false;
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
CanonicalizedSecretKey decryptionKey = null;
CachingDataDecryptorFactory cachedKeyDecryptorFactory = new CachingDataDecryptorFactory(
Constants.BOUNCY_CASTLE_PROVIDER_NAME, cryptoInput.getCryptoData());
;
Passphrase passphrase = null;
@ -569,6 +583,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_ASYM, indent,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
decryptedSessionKeyAvailable = cachedKeyDecryptorFactory.hasCachedSessionData(encData);
if (decryptedSessionKeyAvailable) {
asymmetricPacketFound = true;
encryptedDataAsymmetric = encData;
break;
}
CachedPublicKeyRing cachedPublicKeyRing;
try {
// get actual keyring object based on master key id
@ -746,34 +767,38 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
currentProgress += 2;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
try {
log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
if (!decryptionKey.unlock(passphrase)) {
log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
CachingDataDecryptorFactory decryptorFactory;
if (decryptedSessionKeyAvailable) {
decryptorFactory = cachedKeyDecryptorFactory;
} else {
try {
log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
if (!decryptionKey.unlock(passphrase)) {
log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
} catch (PgpGeneralException e) {
log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
} catch (PgpGeneralException e) {
log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
CachingDataDecryptorFactory decryptorFactory
= decryptionKey.getCachingDecryptorFactory(cryptoInput);
decryptorFactory = 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
if (!decryptorFactory.canDecrypt()
&& !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createSecurityTokenDecryptOperation(
decryptionKey.getRing().getMasterKeyId(),
decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
), 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)) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return result.with(new DecryptVerifyResult(log,
RequiredInputParcel.createSecurityTokenDecryptOperation(
decryptionKey.getRing().getMasterKeyId(),
decryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
), cryptoInput));
}
}
try {
@ -786,8 +811,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
result.encryptedData = encryptedDataAsymmetric;
cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
Map<ByteBuffer, byte[]> cachedSessionKeys = decryptorFactory.getCachedSessionKeys();
cryptoInput.addCryptoData(cachedSessionKeys);
if (cachedSessionKeys.size() >= 1) {
Entry<ByteBuffer, byte[]> entry = cachedSessionKeys.entrySet().iterator().next();
result.sessionKey = entry.getKey().array();
result.decryptedSessionKey = entry.getValue();
}
} else {
// there wasn't even any useful data
if (!anyPacketFound) {

View file

@ -35,6 +35,7 @@ import android.app.Service;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@ -453,6 +454,14 @@ public class OpenPgpService extends Service {
cryptoInput.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
if (data.hasExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT_WRAPPER)) {
Bundle wrapperBundle = data.getBundleExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT_WRAPPER);
wrapperBundle.setClassLoader(getClassLoader());
OpenPgpDecryptionResult decryptionResult = wrapperBundle.getParcelable(OpenPgpApi.EXTRA_DECRYPTION_RESULT);
if (decryptionResult != null && decryptionResult.hasDecryptedSessionKey()) {
cryptoInput.addCryptoData(decryptionResult.sessionKey, decryptionResult.decryptedSessionKey);
}
}
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);

View file

@ -17,18 +17,18 @@
package org.sufficientlysecure.keychain.service.input;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.net.Proxy;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* This is a base class for the input of crypto operations.
*/

@ -1 +1 @@
Subproject commit 710a0d8fe8d89cb9a1f247007000a7f49a29c527
Subproject commit f027645214ff41a54e15cc46058ce9f1867cad5f