diff options
6 files changed, 91 insertions, 43 deletions
diff --git a/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java b/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java index 703af94f4..7679f8486 100644 --- a/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java +++ b/OpenKeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java @@ -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; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java index c4525e5cd..31a3f91b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java @@ -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; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index 94606bff9..a27e4a8d5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -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); - CachingDataDecryptorFactory decryptorFactory - = decryptionKey.getCachingDecryptorFactory(cryptoInput); + currentProgress += 2; + updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - // 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)) { + decryptorFactory = decryptionKey.getCachingDecryptorFactory(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)); + // 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) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 02d9ba62d..e17310b65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java index 849418905..080c34c04 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -17,18 +17,18 @@ package org.sufficientlysecure.keychain.service.input; -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; +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.util.ParcelableProxy; +import org.sufficientlysecure.keychain.util.Passphrase; + /** * This is a base class for the input of crypto operations. */ diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib -Subproject 710a0d8fe8d89cb9a1f247007000a7f49a29c52 +Subproject f027645214ff41a54e15cc46058ce9f1867cad5 |