api: allow caching of sessionKey in OpenPgpDecryptResult
This commit is contained in:
@@ -6,15 +6,16 @@
|
|||||||
|
|
||||||
package org.bouncycastle.openpgp.operator.jcajce;
|
package org.bouncycastle.openpgp.operator.jcajce;
|
||||||
|
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
|
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
|
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
|
||||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
|
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
|
||||||
{
|
{
|
||||||
private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
|
private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
|
||||||
@@ -59,6 +60,10 @@ public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactor
|
|||||||
return mSessionKeyCache.get(bi);
|
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);
|
byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
|
||||||
mSessionKeyCache.put(bi, sessionData);
|
mSessionKeyCache.put(bi, sessionData);
|
||||||
return sessionData;
|
return sessionData;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public class OpenPgpDecryptionResultBuilder {
|
|||||||
// builder
|
// builder
|
||||||
private boolean mInsecure = false;
|
private boolean mInsecure = false;
|
||||||
private boolean mEncrypted = false;
|
private boolean mEncrypted = false;
|
||||||
|
private byte[] sessionKey;
|
||||||
|
private byte[] decryptedSessionKey;
|
||||||
|
|
||||||
public void setInsecure(boolean insecure) {
|
public void setInsecure(boolean insecure) {
|
||||||
this.mInsecure = insecure;
|
this.mInsecure = insecure;
|
||||||
@@ -36,24 +38,26 @@ public class OpenPgpDecryptionResultBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpDecryptionResult build() {
|
public OpenPgpDecryptionResult build() {
|
||||||
OpenPgpDecryptionResult result = new OpenPgpDecryptionResult();
|
|
||||||
|
|
||||||
if (mInsecure) {
|
if (mInsecure) {
|
||||||
Log.d(Constants.TAG, "RESULT_INSECURE");
|
Log.d(Constants.TAG, "RESULT_INSECURE");
|
||||||
result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE);
|
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_INSECURE, sessionKey, decryptedSessionKey);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEncrypted) {
|
if (mEncrypted) {
|
||||||
Log.d(Constants.TAG, "RESULT_ENCRYPTED");
|
Log.d(Constants.TAG, "RESULT_ENCRYPTED");
|
||||||
result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED);
|
return new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED, sessionKey, decryptedSessionKey);
|
||||||
} else {
|
|
||||||
Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
|
|
||||||
result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@@ -60,7 +63,6 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.Constants.key;
|
import org.sufficientlysecure.keychain.Constants.key;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
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.DecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
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.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
import org.sufficientlysecure.keychain.util.CharsetVerifier;
|
||||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@@ -197,6 +200,10 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
PGPEncryptedData encryptedData;
|
PGPEncryptedData encryptedData;
|
||||||
InputStream cleartextStream;
|
InputStream cleartextStream;
|
||||||
|
|
||||||
|
// the cached session key
|
||||||
|
byte[] sessionKey;
|
||||||
|
byte[] decryptedSessionKey;
|
||||||
|
|
||||||
int symmetricEncryptionAlgo = 0;
|
int symmetricEncryptionAlgo = 0;
|
||||||
|
|
||||||
boolean skippedDisallowedKey = false;
|
boolean skippedDisallowedKey = false;
|
||||||
@@ -304,6 +311,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
|
|
||||||
// if this worked out so far, the data is encrypted
|
// if this worked out so far, the data is encrypted
|
||||||
decryptionResultBuilder.setEncrypted(true);
|
decryptionResultBuilder.setEncrypted(true);
|
||||||
|
if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
|
||||||
|
decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (esResult.insecureEncryptionKey) {
|
if (esResult.insecureEncryptionKey) {
|
||||||
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
|
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 asymmetricPacketFound = false;
|
||||||
boolean symmetricPacketFound = false;
|
boolean symmetricPacketFound = false;
|
||||||
boolean anyPacketFound = false;
|
boolean anyPacketFound = false;
|
||||||
|
boolean decryptedSessionKeyAvailable = false;
|
||||||
|
|
||||||
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
|
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
|
||||||
PGPPBEEncryptedData encryptedDataSymmetric = null;
|
PGPPBEEncryptedData encryptedDataSymmetric = null;
|
||||||
CanonicalizedSecretKey decryptionKey = null;
|
CanonicalizedSecretKey decryptionKey = null;
|
||||||
|
CachingDataDecryptorFactory cachedKeyDecryptorFactory = new CachingDataDecryptorFactory(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME, cryptoInput.getCryptoData());
|
||||||
|
;
|
||||||
|
|
||||||
Passphrase passphrase = null;
|
Passphrase passphrase = null;
|
||||||
|
|
||||||
@@ -569,6 +583,13 @@ 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));
|
||||||
|
|
||||||
|
decryptedSessionKeyAvailable = cachedKeyDecryptorFactory.hasCachedSessionData(encData);
|
||||||
|
if (decryptedSessionKeyAvailable) {
|
||||||
|
asymmetricPacketFound = true;
|
||||||
|
encryptedDataAsymmetric = encData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
CachedPublicKeyRing cachedPublicKeyRing;
|
CachedPublicKeyRing cachedPublicKeyRing;
|
||||||
try {
|
try {
|
||||||
// get actual keyring object based on master key id
|
// get actual keyring object based on master key id
|
||||||
@@ -746,34 +767,38 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
currentProgress += 2;
|
currentProgress += 2;
|
||||||
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||||
|
|
||||||
try {
|
CachingDataDecryptorFactory decryptorFactory;
|
||||||
log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
|
if (decryptedSessionKeyAvailable) {
|
||||||
if (!decryptionKey.unlock(passphrase)) {
|
decryptorFactory = cachedKeyDecryptorFactory;
|
||||||
log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
|
} 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));
|
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;
|
currentProgress += 2;
|
||||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
|
|
||||||
CachingDataDecryptorFactory decryptorFactory
|
decryptorFactory = decryptionKey.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
|
||||||
if (!decryptorFactory.canDecrypt()
|
if (!decryptorFactory.canDecrypt()
|
||||||
&& !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
|
&& !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));
|
|
||||||
|
|
||||||
|
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 {
|
try {
|
||||||
@@ -786,8 +811,13 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
|
result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
|
||||||
result.encryptedData = encryptedDataAsymmetric;
|
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 {
|
} else {
|
||||||
// there wasn't even any useful data
|
// there wasn't even any useful data
|
||||||
if (!anyPacketFound) {
|
if (!anyPacketFound) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.app.Service;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -453,6 +454,14 @@ public class OpenPgpService extends Service {
|
|||||||
cryptoInput.mPassphrase =
|
cryptoInput.mPassphrase =
|
||||||
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
|
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);
|
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,18 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.input;
|
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.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
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.
|
* This is a base class for the input of crypto operations.
|
||||||
*/
|
*/
|
||||||
|
|||||||
2
extern/openpgp-api-lib
vendored
2
extern/openpgp-api-lib
vendored
Submodule extern/openpgp-api-lib updated: 710a0d8fe8...f027645214
Reference in New Issue
Block a user