api: allow caching of sessionKey in OpenPgpDecryptResult
This commit is contained in:
parent
5fdc312b2d
commit
8e4d68c55a
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
2
extern/openpgp-api-lib
vendored
2
extern/openpgp-api-lib
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 710a0d8fe8d89cb9a1f247007000a7f49a29c527
|
||||
Subproject commit f027645214ff41a54e15cc46058ce9f1867cad5f
|
Loading…
Reference in a new issue