mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -05:00
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:
parent
403f74f558
commit
dbfa55f6b9
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
|
||||||
|
*
|
||||||
|
* Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.spongycastle.openpgp.operator.jcajce;
|
||||||
|
|
||||||
|
import org.spongycastle.jcajce.util.NamedJcaJceHelper;
|
||||||
|
import org.spongycastle.openpgp.PGPException;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
|
import org.spongycastle.openpgp.operator.PGPDataDecryptor;
|
||||||
|
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
|
||||||
|
{
|
||||||
|
private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
|
||||||
|
private final Map<ByteBuffer, byte[]> mSessionKeyCache;
|
||||||
|
|
||||||
|
private OperatorHelper mOperatorHelper;
|
||||||
|
|
||||||
|
public CachingDataDecryptorFactory(String providerName,
|
||||||
|
final Map<ByteBuffer,byte[]> sessionKeyCache)
|
||||||
|
{
|
||||||
|
mWrappedDecryptor = null;
|
||||||
|
mSessionKeyCache = sessionKeyCache;
|
||||||
|
|
||||||
|
mOperatorHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachingDataDecryptorFactory(PublicKeyDataDecryptorFactory wrapped,
|
||||||
|
final Map<ByteBuffer,byte[]> sessionKeyCache)
|
||||||
|
{
|
||||||
|
mWrappedDecryptor = wrapped;
|
||||||
|
mSessionKeyCache = sessionKeyCache;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCachedSessionData(PGPPublicKeyEncryptedData encData) throws PGPException {
|
||||||
|
ByteBuffer bi = ByteBuffer.wrap(encData.getSessionKey()[0]);
|
||||||
|
return mSessionKeyCache.containsKey(bi);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ByteBuffer, byte[]> getCachedSessionKeys() {
|
||||||
|
return mSessionKeyCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canDecrypt() {
|
||||||
|
return mWrappedDecryptor != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException {
|
||||||
|
ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
|
||||||
|
if (mSessionKeyCache.containsKey(bi)) {
|
||||||
|
return mSessionKeyCache.get(bi);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
|
||||||
|
mSessionKeyCache.put(bi, sessionData);
|
||||||
|
return sessionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
|
||||||
|
throws PGPException {
|
||||||
|
if (mWrappedDecryptor != null) {
|
||||||
|
return mWrappedDecryptor.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
|
||||||
|
}
|
||||||
|
return mOperatorHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,278 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
|
|
||||||
*
|
|
||||||
* Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.spongycastle.openpgp.operator.jcajce;
|
|
||||||
|
|
||||||
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
|
|
||||||
import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
|
|
||||||
import org.spongycastle.jcajce.util.NamedJcaJceHelper;
|
|
||||||
import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
|
|
||||||
import org.spongycastle.openpgp.PGPException;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.spongycastle.openpgp.operator.PGPDataDecryptor;
|
|
||||||
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is based on JcePublicKeyDataDecryptorFactoryBuilder
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NfcSyncPublicKeyDataDecryptorFactoryBuilder
|
|
||||||
{
|
|
||||||
private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
|
|
||||||
private OperatorHelper contentHelper = new OperatorHelper(new DefaultJcaJceHelper());
|
|
||||||
private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
|
|
||||||
// private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder();
|
|
||||||
// private JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
|
|
||||||
|
|
||||||
public static class NfcInteractionNeeded extends RuntimeException
|
|
||||||
{
|
|
||||||
public byte[] encryptedSessionKey;
|
|
||||||
|
|
||||||
public NfcInteractionNeeded(byte[] encryptedSessionKey)
|
|
||||||
{
|
|
||||||
super("NFC interaction required!");
|
|
||||||
this.encryptedSessionKey = encryptedSessionKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public NfcSyncPublicKeyDataDecryptorFactoryBuilder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces.
|
|
||||||
*
|
|
||||||
* @param provider provider object for cryptographic primitives.
|
|
||||||
* @return the current builder.
|
|
||||||
*/
|
|
||||||
public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(Provider provider)
|
|
||||||
{
|
|
||||||
this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
|
|
||||||
keyConverter.setProvider(provider);
|
|
||||||
this.contentHelper = helper;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces.
|
|
||||||
*
|
|
||||||
* @param providerName the name of the provider to reference for cryptographic primitives.
|
|
||||||
* @return the current builder.
|
|
||||||
*/
|
|
||||||
public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(String providerName)
|
|
||||||
{
|
|
||||||
this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
|
|
||||||
keyConverter.setProvider(providerName);
|
|
||||||
this.contentHelper = helper;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(Provider provider)
|
|
||||||
{
|
|
||||||
this.contentHelper = new OperatorHelper(new ProviderJcaJceHelper(provider));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(String providerName)
|
|
||||||
{
|
|
||||||
this.contentHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKeyDataDecryptorFactory build(final Map<ByteBuffer,byte[]> nfcDecryptedMap) {
|
|
||||||
return new PublicKeyDataDecryptorFactory()
|
|
||||||
{
|
|
||||||
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
|
|
||||||
throws PGPException
|
|
||||||
{
|
|
||||||
if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
|
|
||||||
{
|
|
||||||
throw new PGPException("ECDH not supported!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return decryptSessionData(keyAlgorithm, secKeyData, nfcDecryptedMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
|
|
||||||
throws PGPException
|
|
||||||
{
|
|
||||||
return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// public PublicKeyDataDecryptorFactory build(final PrivateKey privKey)
|
|
||||||
// {
|
|
||||||
// return new PublicKeyDataDecryptorFactory()
|
|
||||||
// {
|
|
||||||
// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
|
|
||||||
// throws PGPException
|
|
||||||
// {
|
|
||||||
// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
|
|
||||||
// {
|
|
||||||
// throw new PGPException("ECDH requires use of PGPPrivateKey for decryption");
|
|
||||||
// }
|
|
||||||
// return decryptSessionData(keyAlgorithm, privKey, secKeyData);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
|
|
||||||
// throws PGPException
|
|
||||||
// {
|
|
||||||
// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey, final byte[] nfcDecrypted)
|
|
||||||
// {
|
|
||||||
// return new PublicKeyDataDecryptorFactory()
|
|
||||||
// {
|
|
||||||
// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
|
|
||||||
// throws PGPException
|
|
||||||
// {
|
|
||||||
// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
|
|
||||||
// {
|
|
||||||
// return decryptSessionData(privKey.getPrivateKeyDataPacket(), privKey.getPublicKeyPacket(), secKeyData);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return decryptSessionData(keyAlgorithm, keyConverter.getPrivateKey(privKey), secKeyData, nfcDecrypted);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
|
|
||||||
// throws PGPException
|
|
||||||
// {
|
|
||||||
// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData)
|
|
||||||
// throws PGPException
|
|
||||||
// {
|
|
||||||
// ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
|
|
||||||
// X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID());
|
|
||||||
//
|
|
||||||
// byte[] enc = secKeyData[0];
|
|
||||||
//
|
|
||||||
// int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
|
|
||||||
// byte[] pEnc = new byte[pLen];
|
|
||||||
//
|
|
||||||
// System.arraycopy(enc, 2, pEnc, 0, pLen);
|
|
||||||
//
|
|
||||||
// byte[] keyEnc = new byte[enc[pLen + 2]];
|
|
||||||
//
|
|
||||||
// System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length);
|
|
||||||
//
|
|
||||||
// Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm());
|
|
||||||
//
|
|
||||||
// ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize();
|
|
||||||
//
|
|
||||||
// RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
|
|
||||||
// Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap");
|
|
||||||
//
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// c.init(Cipher.UNWRAP_MODE, key);
|
|
||||||
//
|
|
||||||
// Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
|
|
||||||
//
|
|
||||||
// return PGPPad.unpadSessionData(paddedSessionKey.getEncoded());
|
|
||||||
// }
|
|
||||||
// catch (InvalidKeyException e)
|
|
||||||
// {
|
|
||||||
// throw new PGPException("error setting asymmetric cipher", e);
|
|
||||||
// }
|
|
||||||
// catch (NoSuchAlgorithmException e)
|
|
||||||
// {
|
|
||||||
// throw new PGPException("error setting asymmetric cipher", e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private byte[] decryptSessionData(int keyAlgorithm, byte[][] secKeyData,
|
|
||||||
Map<ByteBuffer,byte[]> nfcDecryptedMap)
|
|
||||||
throws PGPException
|
|
||||||
{
|
|
||||||
// Cipher c1 = helper.createPublicKeyCipher(keyAlgorithm);
|
|
||||||
//
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// c1.init(Cipher.DECRYPT_MODE, privKey);
|
|
||||||
// }
|
|
||||||
// catch (InvalidKeyException e)
|
|
||||||
// {
|
|
||||||
// throw new PGPException("error setting asymmetric cipher", e);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT
|
|
||||||
|| keyAlgorithm == PGPPublicKey.RSA_GENERAL)
|
|
||||||
{
|
|
||||||
ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
|
|
||||||
|
|
||||||
if (nfcDecryptedMap.containsKey(bi)) {
|
|
||||||
return nfcDecryptedMap.get(bi);
|
|
||||||
} else {
|
|
||||||
// catch this when decryptSessionData() is executed and divert digest to card,
|
|
||||||
// when doing the operation again reuse nfcDecrypted
|
|
||||||
throw new NfcInteractionNeeded(bi.array());
|
|
||||||
}
|
|
||||||
|
|
||||||
// c1.update(bi, 2, bi.length - 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PGPException("ElGamal not supported!");
|
|
||||||
|
|
||||||
// ElGamalKey k = (ElGamalKey)privKey;
|
|
||||||
// int size = (k.getParameters().getP().bitLength() + 7) / 8;
|
|
||||||
// byte[] tmp = new byte[size];
|
|
||||||
//
|
|
||||||
// byte[] bi = secKeyData[0]; // encoded MPI
|
|
||||||
// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
|
|
||||||
// {
|
|
||||||
// c1.update(bi, 3, bi.length - 3);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
|
|
||||||
// c1.update(tmp);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// bi = secKeyData[1]; // encoded MPI
|
|
||||||
// for (int i = 0; i != tmp.length; i++)
|
|
||||||
// {
|
|
||||||
// tmp[i] = 0;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
|
|
||||||
// {
|
|
||||||
// c1.update(bi, 3, bi.length - 3);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
|
|
||||||
// c1.update(tmp);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// return c1.doFinal();
|
|
||||||
// }
|
|
||||||
// catch (Exception e)
|
|
||||||
// {
|
|
||||||
// throw new PGPException("exception decrypting session data", e);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,6 +22,7 @@ import android.os.Parcel;
|
|||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpMetadata;
|
import org.openintents.openpgp.OpenPgpMetadata;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
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.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
|
|
||||||
@ -36,6 +37,8 @@ public class DecryptVerifyResult extends InputPendingResult {
|
|||||||
// https://tools.ietf.org/html/rfc4880#page56
|
// https://tools.ietf.org/html/rfc4880#page56
|
||||||
String mCharset;
|
String mCharset;
|
||||||
|
|
||||||
|
CryptoInputParcel mCachedCryptoInputParcel;
|
||||||
|
|
||||||
byte[] mOutputBytes;
|
byte[] mOutputBytes;
|
||||||
|
|
||||||
public DecryptVerifyResult(int result, OperationLog log) {
|
public DecryptVerifyResult(int result, OperationLog log) {
|
||||||
@ -50,6 +53,7 @@ public class DecryptVerifyResult extends InputPendingResult {
|
|||||||
super(source);
|
super(source);
|
||||||
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
||||||
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.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;
|
mSignatureResult = signatureResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CryptoInputParcel getCachedCryptoInputParcel() {
|
||||||
|
return mCachedCryptoInputParcel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCachedCryptoInputParcel(CryptoInputParcel cachedCryptoInputParcel) {
|
||||||
|
mCachedCryptoInputParcel = cachedCryptoInputParcel;
|
||||||
|
}
|
||||||
|
|
||||||
public OpenPgpMetadata getDecryptMetadata() {
|
public OpenPgpMetadata getDecryptMetadata() {
|
||||||
return mDecryptMetadata;
|
return mDecryptMetadata;
|
||||||
}
|
}
|
||||||
@ -97,6 +109,7 @@ public class DecryptVerifyResult extends InputPendingResult {
|
|||||||
super.writeToParcel(dest, flags);
|
super.writeToParcel(dest, flags);
|
||||||
dest.writeParcelable(mSignatureResult, 0);
|
dest.writeParcelable(mSignatureResult, 0);
|
||||||
dest.writeParcelable(mDecryptMetadata, 0);
|
dest.writeParcelable(mDecryptMetadata, 0);
|
||||||
|
dest.writeParcelable(mCachedCryptoInputParcel, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
|
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
|
||||||
|
@ -21,23 +21,18 @@ package org.sufficientlysecure.keychain.pgp;
|
|||||||
import org.spongycastle.bcpg.S2K;
|
import org.spongycastle.bcpg.S2K;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
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.PBESecretKeyDecryptor;
|
||||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
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.JcaPGPContentSignerBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
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;
|
||||||
@ -51,7 +46,6 @@ import java.security.interfaces.RSAPrivateCrtKey;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
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) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
throw new PrivateKeyNotUnlockedException();
|
throw new PrivateKeyNotUnlockedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||||
return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
|
return new CachingDataDecryptorFactory(
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME,
|
||||||
cryptoInput.getCryptoData()
|
cryptoInput.getCryptoData());
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return new JcePublicKeyDataDecryptorFactoryBuilder()
|
return new CachingDataDecryptorFactory(
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
|
new JcePublicKeyDataDecryptorFactoryBuilder()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey),
|
||||||
|
cryptoInput.getCryptoData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import android.webkit.MimeTypeMap;
|
|||||||
import org.openintents.openpgp.OpenPgpMetadata;
|
import org.openintents.openpgp.OpenPgpMetadata;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.spongycastle.bcpg.ArmoredInputStream;
|
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||||
|
import org.spongycastle.bcpg.PublicKeyEncSessionPacket;
|
||||||
import org.spongycastle.openpgp.PGPCompressedData;
|
import org.spongycastle.openpgp.PGPCompressedData;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedDataList;
|
import org.spongycastle.openpgp.PGPEncryptedDataList;
|
||||||
@ -40,11 +41,10 @@ import org.spongycastle.openpgp.PGPUtil;
|
|||||||
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||||
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
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.JcaPGPContentVerifierBuilderProvider;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||||
@ -541,24 +541,33 @@ public class PgpDecryptVerify extends BaseOperation {
|
|||||||
currentProgress += 2;
|
currentProgress += 2;
|
||||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
|
|
||||||
try {
|
CachingDataDecryptorFactory decryptorFactory
|
||||||
PublicKeyDataDecryptorFactory decryptorFactory
|
= secretEncryptionKey.getCachingDecryptorFactory(cryptoInput);
|
||||||
= secretEncryptionKey.getDecryptorFactory(cryptoInput);
|
|
||||||
try {
|
// special case: if the decryptor does not have a session key cached for this encrypted
|
||||||
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
|
// data, and can't actually decrypt on its own, return a pending intent
|
||||||
} catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
|
if (!decryptorFactory.canDecrypt()
|
||||||
log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
|
&& !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
|
||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
|
|
||||||
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
|
|
||||||
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
|
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
|
||||||
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
|
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
|
||||||
secretEncryptionKey.getRing().getMasterKeyId(),
|
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;
|
encryptedData = encryptedDataAsymmetric;
|
||||||
} else {
|
} else {
|
||||||
// there wasn't even any useful data
|
// 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
|
// Return a positive result, with metadata and verification info
|
||||||
DecryptVerifyResult result =
|
DecryptVerifyResult result =
|
||||||
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
|
||||||
|
result.setCachedCryptoInputParcel(cryptoInput);
|
||||||
result.setDecryptMetadata(metadata);
|
result.setDecryptMetadata(metadata);
|
||||||
result.setSignatureResult(signatureResultBuilder.build());
|
result.setSignatureResult(signatureResultBuilder.build());
|
||||||
result.setCharset(charset);
|
result.setCharset(charset);
|
||||||
|
@ -97,8 +97,12 @@ public class CryptoInputParcel implements Parcelable {
|
|||||||
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
|
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
|
||||||
|
mCryptoData.putAll(cachedSessionKeys);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<ByteBuffer, byte[]> getCryptoData() {
|
public Map<ByteBuffer, byte[]> getCryptoData() {
|
||||||
return Collections.unmodifiableMap(mCryptoData);
|
return mCryptoData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getSignatureTime() {
|
public Date getSignatureTime() {
|
||||||
@ -138,4 +142,5 @@ public class CryptoInputParcel implements Parcelable {
|
|||||||
b.append("}");
|
b.append("}");
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
|
|
||||||
import java.io.File;
|
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_URI = "uri";
|
||||||
public static final String ARG_OPEN_DIRECTLY = "open_directly";
|
public static final String ARG_OPEN_DIRECTLY = "open_directly";
|
||||||
|
|
||||||
@ -69,8 +69,8 @@ public class DecryptFilesFragment extends DecryptFragment {
|
|||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
*/
|
*/
|
||||||
public static DecryptFilesFragment newInstance(Uri uri, boolean openDirectly) {
|
public static DecryptFilesInputFragment newInstance(Uri uri, boolean openDirectly) {
|
||||||
DecryptFilesFragment frag = new DecryptFilesFragment();
|
DecryptFilesInputFragment frag = new DecryptFilesInputFragment();
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putParcelable(ARG_URI, uri);
|
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() {
|
view.findViewById(R.id.decrypt_files_browse).setOnClickListener(new View.OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
|
FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
|
||||||
} else {
|
} else {
|
||||||
FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*",
|
FileHelper.openFile(DecryptFilesInputFragment.this, mInputUri, "*/*",
|
||||||
REQUEST_CODE_INPUT);
|
REQUEST_CODE_INPUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,9 +128,9 @@ public class DecryptFilesFragment extends DecryptFragment {
|
|||||||
// should only come from args
|
// should only come from args
|
||||||
if (state.getBoolean(ARG_OPEN_DIRECTLY, false)) {
|
if (state.getBoolean(ARG_OPEN_DIRECTLY, false)) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
|
FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
|
||||||
} else {
|
} 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() {
|
private void startDecrypt() {
|
||||||
mCurrentCryptoOperation = KeychainIntentService.ACTION_DECRYPT_VERIFY;
|
mCurrentCryptoOperation = KeychainIntentService.ACTION_DECRYPT_VERIFY;
|
||||||
cryptoOperation(new CryptoInputParcel());
|
cryptoOperation(new CryptoInputParcel());
|
||||||
@ -233,18 +237,19 @@ public class DecryptFilesFragment extends DecryptFragment {
|
|||||||
// get returned data bundle
|
// get returned data bundle
|
||||||
Bundle returnData = message.getData();
|
Bundle returnData = message.getData();
|
||||||
|
|
||||||
DecryptVerifyResult pgpResult =
|
DecryptVerifyResult result =
|
||||||
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
||||||
|
|
||||||
if (pgpResult.success()) {
|
if (result.success()) {
|
||||||
switch (mCurrentCryptoOperation) {
|
switch (mCurrentCryptoOperation) {
|
||||||
case KeychainIntentService.ACTION_DECRYPT_METADATA: {
|
case KeychainIntentService.ACTION_DECRYPT_METADATA: {
|
||||||
askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
|
displayMetadata(result);
|
||||||
|
// askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KeychainIntentService.ACTION_DECRYPT_VERIFY: {
|
case KeychainIntentService.ACTION_DECRYPT_VERIFY: {
|
||||||
// display signature result in activity
|
// display signature result in activity
|
||||||
loadVerifyResult(pgpResult);
|
loadVerifyResult(result);
|
||||||
|
|
||||||
if (mDeleteAfter.isChecked()) {
|
if (mDeleteAfter.isChecked()) {
|
||||||
// Create and show dialog to delete original file
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user