aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java290
1 files changed, 149 insertions, 141 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 571729bc5..8a0bf99d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,28 +18,58 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPCompressedData;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPOnePassSignature;
+import org.spongycastle.openpgp.PGPOnePassSignatureList;
+import org.spongycastle.openpgp.PGPPBEEncryptedData;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.security.SignatureException;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Set;
/**
* This class uses a Builder pattern!
@@ -50,9 +80,9 @@ public class PgpDecryptVerify {
private OutputStream mOutStream;
private ProgressDialogUpdater mProgressDialogUpdater;
- private boolean mAssumeSymmetric;
+ private boolean mAllowSymmetricDecryption;
private String mPassphrase;
- private long mEnforcedKeyId;
+ private Set<Long> mAllowedKeyIds;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
@@ -61,9 +91,9 @@ public class PgpDecryptVerify {
this.mOutStream = builder.mOutStream;
this.mProgressDialogUpdater = builder.mProgressDialogUpdater;
- this.mAssumeSymmetric = builder.mAssumeSymmetric;
+ this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption;
this.mPassphrase = builder.mPassphrase;
- this.mEnforcedKeyId = builder.mEnforcedKeyId;
+ this.mAllowedKeyIds = builder.mAllowedKeyIds;
}
public static class Builder {
@@ -74,9 +104,9 @@ public class PgpDecryptVerify {
// optional
private ProgressDialogUpdater mProgressDialogUpdater = null;
- private boolean mAssumeSymmetric = false;
- private String mPassphrase = "";
- private long mEnforcedKeyId = 0;
+ private boolean mAllowSymmetricDecryption = true;
+ private String mPassphrase = null;
+ private Set<Long> mAllowedKeyIds = null;
public Builder(Context context, InputData data, OutputStream outStream) {
this.mContext = context;
@@ -89,8 +119,8 @@ public class PgpDecryptVerify {
return this;
}
- public Builder assumeSymmetric(boolean assumeSymmetric) {
- this.mAssumeSymmetric = assumeSymmetric;
+ public Builder allowSymmetricDecryption(boolean allowSymmetricDecryption) {
+ this.mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
}
@@ -100,14 +130,14 @@ public class PgpDecryptVerify {
}
/**
- * Allow this key id alone for decryption.
- * This means only ciphertexts encrypted for this private key can be decrypted.
+ * Allow these key ids alone for decryption.
+ * This means only ciphertexts encrypted for one of these private key can be decrypted.
*
- * @param enforcedKeyId
+ * @param allowedKeyIds
* @return
*/
- public Builder enforcedKeyId(long enforcedKeyId) {
- this.mEnforcedKeyId = enforcedKeyId;
+ public Builder allowedKeyIds(Set<Long> allowedKeyIds) {
+ this.mAllowedKeyIds = allowedKeyIds;
return this;
}
@@ -128,35 +158,6 @@ public class PgpDecryptVerify {
}
}
- public static boolean hasSymmetricEncryption(Context context, InputStream inputStream)
- throws PgpGeneralException, IOException {
- InputStream in = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
- // the first object might be a PGP marker packet.
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
- }
-
- Iterator<?> it = enc.getEncryptedDataObjects();
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Decrypts and/or verifies data based on parameters of class
*
@@ -221,25 +222,82 @@ public class PgpDecryptVerify {
currentProgress += 5;
- // TODO: currently we always only look at the first known key or symmetric encryption,
- // there might be more...
- if (mAssumeSymmetric) {
- PGPPBEEncryptedData pbe = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- pbe = (PGPPBEEncryptedData) obj;
- break;
+ PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
+ PGPPBEEncryptedData encryptedDataSymmetric = null;
+ PGPSecretKey secretKey = null;
+ Iterator<?> it = enc.getEncryptedDataObjects();
+ boolean symmetricPacketFound = false;
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ updateProgress(R.string.progress_finding_key, currentProgress, 100);
+
+ PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
+ long masterKeyId = ProviderHelper.getMasterKeyId(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
+ );
+ PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRing(mContext, masterKeyId);
+ if (secretKeyRing == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
- }
+ secretKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ if (secretKey == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
+ }
+ // secret key exists in database
- if (pbe == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_symmetric_encryption_packet));
+ // allow only a specific key for decryption?
+ if (mAllowedKeyIds != null) {
+ Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
+ Log.d(Constants.TAG, "allowedKeyIds: " + mAllowedKeyIds);
+ Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
+
+ if (!mAllowedKeyIds.contains(masterKeyId)) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_no_secret_key_found));
+ }
+ }
+
+ encryptedDataAsymmetric = encData;
+
+ // if no passphrase was explicitly set try to get it from the cache service
+ if (mPassphrase == null) {
+ // returns "" if key has no passphrase
+ mPassphrase =
+ PassphraseCacheService.getCachedPassphrase(mContext, masterKeyId);
+
+ // if passphrase was not cached, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setKeyIdPassphraseNeeded(masterKeyId);
+ returnData.setStatus(PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED);
+ return returnData;
+ }
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
+ } else if (mAllowSymmetricDecryption && obj instanceof PGPPBEEncryptedData) {
+ symmetricPacketFound = true;
+
+ encryptedDataSymmetric = (PGPPBEEncryptedData) obj;
+
+ // if no passphrase is given, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setStatus(PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED);
+ return returnData;
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
}
+ }
+ if (symmetricPacketFound) {
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
@@ -248,64 +306,11 @@ public class PgpDecryptVerify {
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray());
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataSymmetric;
currentProgress += 5;
} else {
- updateProgress(R.string.progress_finding_key, currentProgress, 100);
-
- PGPPublicKeyEncryptedData pbe = null;
- PGPSecretKey secretKey = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPublicKeyEncryptedData) {
- PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(mContext, encData.getKeyID());
- if (secretKey != null) {
- // secret key exists in database
-
- // allow only a specific key for decryption?
- if (mEnforcedKeyId != 0) {
- // TODO: improve this code! get master key directly!
- PGPSecretKeyRing secretKeyRing =
- ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, encData.getKeyID());
- long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
- Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
- Log.d(Constants.TAG, "enforcedKeyId: " + mEnforcedKeyId);
- Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
-
- if (mEnforcedKeyId != masterKeyId) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_secret_key_found));
- }
- }
-
- pbe = encData;
-
- // if no passphrase was explicitly set try to get it from the cache service
- if (mPassphrase == null) {
- // returns "" if key has no passphrase
- mPassphrase =
- PassphraseCacheService.getCachedPassphrase(mContext, encData.getKeyID());
-
- // if passphrase was not cached, return here
- // indicating that a passphrase is missing!
- if (mPassphrase == null) {
- returnData.setKeyPassphraseNeeded(true);
- return returnData;
- }
- }
-
- break;
- }
-
-
- }
- }
-
if (secretKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
@@ -331,9 +336,9 @@ public class PgpDecryptVerify {
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataAsymmetric;
currentProgress += 5;
}
@@ -363,7 +368,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
signatureKey = ProviderHelper
- .getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
+ .getPGPPublicKeyRing(mContext, signature.getKeyID()).getPublicKey();
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -373,10 +378,10 @@ public class PgpDecryptVerify {
signatureIndex = i;
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(
mContext, signatureKeyId);
if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
+ userId = PgpKeyHelper.getMainUserId(signKeyRing.getPublicKey());
}
signatureResult.setUserId(userId);
break;
@@ -388,7 +393,7 @@ public class PgpDecryptVerify {
if (signature != null) {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
} else {
@@ -546,25 +551,27 @@ public class PgpDecryptVerify {
long signatureKeyId = 0;
PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
+ signature = sigList.get(i);
+ signatureKeyId = signature.getKeyID();
+
+ // find data about this subkey
+ HashMap<String, Object> data = ProviderHelper.getGenericData(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(signature.getKeyID())),
+ new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
+ new int[] { ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_STRING });
+ // any luck? otherwise, try next.
+ if(data.get(KeyRings.MASTER_KEY_ID) == null) {
signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
- signatureKeyId);
- if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
- }
- signatureResult.setUserId(userId);
- break;
+ // do NOT reset signatureKeyId, that one is shown when no known one is found!
+ continue;
}
+
+ // this one can't fail now (yay database constraints)
+ signatureKey = ProviderHelper.getPGPPublicKeyRing(mContext, (Long) data.get(KeyRings.MASTER_KEY_ID)).getPublicKey();
+ signatureResult.setUserId((String) data.get(KeyRings.USER_ID));
+
+ break;
}
signatureResult.setKeyId(signatureKeyId);
@@ -621,11 +628,11 @@ public class PgpDecryptVerify {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(context,
signatureKeyId);
PGPPublicKey mKey = null;
if (signKeyRing != null) {
- mKey = PgpKeyHelper.getMasterKey(signKeyRing);
+ mKey = signKeyRing.getPublicKey();
}
if (signature.getKeyID() != mKey.getKeyID()) {
@@ -685,7 +692,8 @@ public class PgpDecryptVerify {
}
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
- PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ PGPPublicKey masterPublicKey,
+ PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()