aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-05-03 15:55:10 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-05-03 15:55:28 +0200
commit4b3cfd4fa41ef8339a0691b38c4c6ec481b5c3df (patch)
tree4ee6791ed0cd82c08020a6c1a56b9ab736c62543 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain
parent4053e1ebd795f5b5fba8ed61185e5a5fdf9b0820 (diff)
downloadopen-keychain-4b3cfd4fa41ef8339a0691b38c4c6ec481b5c3df.tar.gz
open-keychain-4b3cfd4fa41ef8339a0691b38c4c6ec481b5c3df.tar.bz2
open-keychain-4b3cfd4fa41ef8339a0691b38c4c6ec481b5c3df.zip
wrapped-key-ring: first steps, get rid of key imports in decryptverify and signencrypt
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java61
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java219
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java121
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java274
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java96
12 files changed, 744 insertions, 320 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java
new file mode 100644
index 000000000..5cf89b6a6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java
@@ -0,0 +1,61 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPSignature;
+
+public abstract class CachedKeyRing {
+
+ private final long mMasterKeyId;
+ private final int mKeySize;
+ private final boolean mIsRevoked;
+ private final boolean mCanCertify;
+ private final long mCreation;
+ private final long mExpiry;
+ private final int mAlgorithm;
+ private final byte[] mFingerprint;
+ private final String mUserId;
+ private final int mVerified;
+ private final boolean mHasSecret;
+
+ protected CachedKeyRing(long masterKeyId, int keySize, boolean isRevoked,
+ boolean canCertify, long creation, long expiry, int algorithm,
+ byte[] fingerprint, String userId, int verified, boolean hasSecret)
+ {
+ mMasterKeyId = masterKeyId;
+ mKeySize = keySize;
+ mIsRevoked = isRevoked;
+ mCanCertify = canCertify;
+ mCreation = creation;
+ mExpiry = expiry;
+ mAlgorithm = algorithm;
+ mFingerprint = fingerprint;
+ mUserId = userId;
+ mVerified = verified;
+ mHasSecret = hasSecret;
+ }
+
+ public boolean isRevoked() {
+ return mIsRevoked;
+ }
+
+ public byte[] getFingerprint() {
+
+ return mFingerprint;
+ }
+
+ public String getPrimaryUserId() {
+ return mUserId;
+ }
+
+ public long getMasterKeyId() {
+ return mMasterKeyId;
+ }
+
+ public int getVerified() {
+ return mVerified;
+ }
+
+ public void initSignature(PGPSignature sig) {
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java
new file mode 100644
index 000000000..fb065c85f
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java
@@ -0,0 +1,53 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPOnePassSignature;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.sufficientlysecure.keychain.Constants;
+
+public class CachedPublicKey {
+
+ // this is the parent key ring
+ private final CachedPublicKeyRing mRing;
+
+ private final PGPPublicKey mKey;
+
+ CachedPublicKey(CachedPublicKeyRing ring, PGPPublicKey key) {
+ mRing = ring;
+ mKey = key;
+ }
+
+ public CachedPublicKeyRing getKeyRing() {
+ return mRing;
+ }
+
+ JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
+ return new JcePublicKeyKeyEncryptionMethodGenerator(mKey);
+ }
+
+ public void initSignature(PGPSignature sig) throws PGPException {
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ sig.init(contentVerifierBuilderProvider, mKey);
+ }
+
+ public void initSignature(PGPOnePassSignature sig) throws PGPException {
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ sig.init(contentVerifierBuilderProvider, mKey);
+ }
+
+ public byte[] getFingerprint() {
+ return mKey.getFingerprint();
+ }
+
+ // Note that this method has package visibility - no access outside the pgp package!
+ PGPPublicKey getKey() {
+ return mKey;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java
new file mode 100644
index 000000000..fb142adce
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java
@@ -0,0 +1,219 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.io.IOException;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+
+public class CachedPublicKeyRing extends CachedKeyRing {
+
+ private PGPPublicKeyRing mRing;
+ private final byte[] mPubKey;
+
+ public CachedPublicKeyRing(long masterKeyId, int keySize, boolean isRevoked,
+ boolean canCertify, long creation, long expiry, int algorithm,
+ byte[] fingerprint, String userId, int verified, boolean hasSecret,
+ byte[] pubkey)
+ {
+ super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry,
+ algorithm, fingerprint, userId, verified, hasSecret);
+
+ mPubKey = pubkey;
+ }
+
+ private PGPPublicKeyRing getRing() {
+ if(mRing == null) {
+ mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey);
+ }
+ return mRing;
+ }
+
+ public void encode(ArmoredOutputStream stream) throws IOException {
+ getRing().encode(stream);
+ }
+
+ public CachedPublicKey getSubkey(long id) {
+ return new CachedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ public CachedPublicKey getFirstEncryptSubkey() throws PgpGeneralException {
+ // only return master key if no other encryption key is available
+ PGPPublicKey masterKey = null;
+ for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
+ if (key.isRevoked() || !isEncryptionKey(key) || isExpired(key)) {
+ continue;
+ }
+ if (key.isMasterKey()) {
+ masterKey = key;
+ } else {
+ return new CachedPublicKey(this, key);
+ }
+ }
+ if(masterKey == null) {
+ // TODO proper exception
+ throw new PgpGeneralException("key not found");
+ }
+ return new CachedPublicKey(this, masterKey);
+ }
+
+ public boolean verifySubkeyBinding(CachedPublicKey cachedSubkey) {
+ boolean validSubkeyBinding = false;
+ boolean validTempSubkeyBinding = false;
+ boolean validPrimaryKeyBinding = false;
+
+ PGPPublicKey masterKey = getRing().getPublicKey();
+ PGPPublicKey subKey = cachedSubkey.getKey();
+
+ // Is this the master key? Match automatically, then.
+ if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
+ return true;
+ }
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ Iterator<PGPSignature> itr = subKey.getSignatures();
+
+ while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
+ //gpg has an invalid subkey binding error on key import I think, but doesn't shout
+ //about keys without subkey signing. Can't get it to import a slightly broken one
+ //either, so we will err on bad subkey binding here.
+ PGPSignature sig = itr.next();
+ if (sig.getKeyID() == masterKey.getKeyID() &&
+ sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
+ //check and if ok, check primary key binding.
+ try {
+ sig.init(contentVerifierBuilderProvider, masterKey);
+ validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+
+ if (validTempSubkeyBinding) {
+ validSubkeyBinding = true;
+ }
+ if (validTempSubkeyBinding) {
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ }
+ }
+ }
+ return validSubkeyBinding && validPrimaryKeyBinding;
+
+ }
+
+
+ static boolean isEncryptionKey(PGPPublicKey key) {
+ if (!key.isEncryptionKey()) {
+ return false;
+ }
+
+ if (key.getVersion() <= 3) {
+ // this must be true now
+ return key.isEncryptionKey();
+ }
+
+ // special cases
+ if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
+ return true;
+ }
+
+ if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null
+ && (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null
+ && (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static boolean isExpired(PGPPublicKey key) {
+ Date creationDate = key.getCreationTime();
+ Date expiryDate = key.getValidSeconds() > 0
+ ? new Date(creationDate.getTime() + key.getValidSeconds() * 1000) : null;
+
+ Date now = new Date();
+ return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
+ }
+
+ static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
+ PGPPublicKey masterPublicKey,
+ PGPPublicKey signingPublicKey) {
+ boolean validPrimaryKeyBinding = false;
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureList eSigList;
+
+ if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
+ try {
+ eSigList = pkts.getEmbeddedSignatures();
+ } catch (IOException e) {
+ return false;
+ } catch (PGPException e) {
+ return false;
+ }
+ for (int j = 0; j < eSigList.size(); ++j) {
+ PGPSignature emSig = eSigList.get(j);
+ if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
+ try {
+ emSig.init(contentVerifierBuilderProvider, signingPublicKey);
+ validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ return validPrimaryKeyBinding;
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java
new file mode 100644
index 000000000..514fca6fb
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java
@@ -0,0 +1,121 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPV3SignatureGenerator;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+public class CachedSecretKey {
+
+ // this is the parent key ring
+ private final CachedSecretKeyRing mRing;
+
+ private final PGPSecretKey mKey;
+ private PGPPrivateKey mPrivateKey = null;
+
+ CachedSecretKey(CachedSecretKeyRing ring, PGPSecretKey key) {
+ mRing = ring;
+ mKey = key;
+ }
+
+ public CachedSecretKeyRing getRing() {
+ return mRing;
+ }
+
+ public void unlock(String passphrase) throws PgpGeneralException {
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ mPrivateKey = mKey.extractPrivateKey(keyDecryptor);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("error extracting key!", e);
+ }
+ if(mPrivateKey == null) {
+ throw new PgpGeneralException("error extracting key (bad passphrase?)");
+ }
+ }
+
+ public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(signatureType, mPrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ return signatureGenerator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
+ signatureV3Generator.init(signatureType, mPrivateKey);
+ return signatureV3Generator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PublicKeyDataDecryptorFactory getDecryptorFactory() {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+ return new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
+ }
+
+ static class PrivateKeyNotUnlockedException extends RuntimeException {
+ // this exception is a programming error which happens when an operation which requires
+ // the private key is called without a previous call to unlock()
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java
new file mode 100644
index 000000000..a2b2b2832
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java
@@ -0,0 +1,74 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+public class CachedSecretKeyRing extends CachedKeyRing {
+
+ private PGPSecretKeyRing mRing;
+
+ public CachedSecretKeyRing(long masterKeyId, int keySize, boolean isRevoked,
+ boolean canCertify, long creation, long expiry, int algorithm,
+ byte[] fingerprint, String userId, int verified, boolean hasSecret,
+ byte[] blob)
+ {
+ super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry,
+ algorithm, fingerprint, userId, verified, hasSecret);
+
+ mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob);
+ }
+
+ CachedSecretKey getSubKey(long id) {
+ return new CachedSecretKey(this, mRing.getSecretKey(id));
+ }
+
+ /** This returns the subkey that should be used for signing.
+ * At this point, this is simply the first suitable subkey.
+ */
+ CachedSecretKey getSigningSubKey() {
+ for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mRing.getSecretKeys())) {
+ if (isSigningKey(key.getPublicKey())) {
+ return new CachedSecretKey(this, key);
+ }
+ }
+ // TODO exception
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean isSigningKey(PGPPublicKey key) {
+ if (key.getVersion() <= 3) {
+ return true;
+ }
+
+ // special case
+ if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 506c161ba..c84b9413f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
-import android.net.Uri;
-
import org.spongycastle.bcpg.ArmoredInputStream;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@@ -31,29 +28,18 @@ 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.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.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
@@ -67,7 +53,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
/**
@@ -248,7 +233,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- PGPSecretKey secretEncryptionKey = null;
+ CachedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -260,15 +245,13 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- long masterKeyId;
- PGPSecretKeyRing secretKeyRing;
+ CachedSecretKeyRing secretKeyRing;
try {
- // get master key id for this encryption key id
- masterKeyId = mProviderHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
- );
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId);
+ secretKeyRing = mProviderHelper.getCachedSecretKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
+ Long.toString(encData.getKeyID()))
+ );
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
continue;
@@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
}
/* secret key exists in database! */
+ long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
@@ -359,23 +343,15 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- PGPPrivateKey privateKey;
try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- mPassphrase.toCharArray());
- privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor);
- } catch (PGPException e) {
+ secretEncryptionKey.unlock(mPassphrase);
+ } catch (PgpGeneralException e) {
throw new WrongPassphraseException();
}
- if (privateKey == null) {
- throw new KeyExtractionException();
- }
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
+ PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@@ -388,10 +364,9 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- PGPPublicKey signatureKey = null;
int signatureIndex = -1;
+ CachedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -403,6 +378,8 @@ public class PgpDecryptVerify {
currentProgress += 10;
}
+ PGPOnePassSignature signature = null;
+
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@@ -410,19 +387,15 @@ public class PgpDecryptVerify {
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ CachedPublicKeyRing signingRing = mProviderHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
+ Long.toString(sigKeyId)
+ )
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -430,43 +403,17 @@ public class PgpDecryptVerify {
}
}
- if (masterKeyId != null) {
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingKey.getKeyRing().getMasterKeyId());
+ signatureResultBuilder.userId(signingKey.getKeyRing().getPrimaryUserId());
+ signatureResultBuilder.signatureKeyCertified(signingKey.getKeyRing().getVerified() > 0);
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signingKey.initSignature(signature);
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -541,7 +488,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
- boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey);
+ boolean validKeyBinding = signingKey.getKeyRing().verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -617,22 +564,20 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
+ CachedPublicKey signingKey = null;
+ int signatureIndex = -1;
+
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
- int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ CachedPublicKeyRing signingRing = mProviderHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
+ Long.toString(sigKeyId)
+ )
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -641,44 +586,18 @@ public class PgpDecryptVerify {
}
PGPSignature signature = null;
- PGPPublicKey signatureKey = null;
- if (masterKeyId != null) {
+
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingKey.getKeyRing().getMasterKeyId());
+ signatureResultBuilder.userId(signingKey.getKeyRing().getPrimaryUserId());
+ signatureResultBuilder.signatureKeyCertified(signingKey.getKeyRing().getVerified() > 0);
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signingKey.initSignature(signature);
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -710,7 +629,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
- boolean validKeyBinding = verifyKeyBinding(signature, signatureKey);
+ boolean validKeyBinding = signingKey.getKeyRing().verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -722,113 +641,6 @@ public class PgpDecryptVerify {
return result;
}
- private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
- long signatureKeyId = signature.getKeyID();
- boolean validKeyBinding = false;
-
- PGPPublicKey mKey = null;
- try {
- PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
- signatureKeyId);
- mKey = signKeyRing.getPublicKey();
- } catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found");
- }
-
- if (signature.getKeyID() != mKey.getKeyID()) {
- validKeyBinding = verifyKeyBinding(mKey, signatureKey);
- } else { //if the key used to make the signature was the master key, no need to check binding sigs
- validKeyBinding = true;
- }
- return validKeyBinding;
- }
-
- private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
- boolean validSubkeyBinding = false;
- boolean validTempSubkeyBinding = false;
- boolean validPrimaryKeyBinding = false;
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
-
- while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
- //gpg has an invalid subkey binding error on key import I think, but doesn't shout
- //about keys without subkey signing. Can't get it to import a slightly broken one
- //either, so we will err on bad subkey binding here.
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterPublicKey.getKeyID() &&
- sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
- //check and if ok, check primary key binding.
- try {
- sig.init(contentVerifierBuilderProvider, masterPublicKey);
- validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
-
- if (validTempSubkeyBinding) {
- validSubkeyBinding = true;
- }
- if (validTempSubkeyBinding) {
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- }
- }
- }
- return (validSubkeyBinding & validPrimaryKeyBinding);
- }
-
- private boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
- PGPPublicKey masterPublicKey,
- PGPPublicKey signingPublicKey) {
- boolean validPrimaryKeyBinding = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureList eSigList;
-
- if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
- try {
- eSigList = pkts.getEmbeddedSignatures();
- } catch (IOException e) {
- return false;
- } catch (PGPException e) {
- return false;
- }
- for (int j = 0; j < eSigList.size(); ++j) {
- PGPSignature emSig = eSigList.get(j);
- if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
- try {
- emSig.init(contentVerifierBuilderProvider, signingPublicKey);
- validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
- }
- }
- }
-
- return validPrimaryKeyBinding;
- }
-
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index bfbcd582e..6aeb19e65 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -32,8 +32,8 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
@@ -208,9 +208,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId);
+ CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(
+ KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
+ );
- publicKeyRing.encode(arOutStream);
+ ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user?
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index f90250f57..ecaeac7e7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -52,14 +52,17 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+ @Deprecated
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
+ @Deprecated
public static Date getCreationDate(PGPSecretKey key) {
return key.getPublicKey().getCreationTime();
}
+ @Deprecated
public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) {
@@ -73,10 +76,12 @@ public class PgpKeyHelper {
return calendar.getTime();
}
+ @Deprecated
public static Date getExpiryDate(PGPSecretKey key) {
return getExpiryDate(key.getPublicKey());
}
+ @Deprecated
public static boolean isExpired(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key);
@@ -89,6 +94,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
@@ -105,6 +111,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
@@ -118,6 +125,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
@@ -131,6 +139,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
@@ -143,26 +152,7 @@ public class PgpKeyHelper {
return signingKeys;
}
- private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
- Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
- PGPPublicKey masterKey = null;
- for (int i = 0; i < encryptKeys.size(); ++i) {
- PGPPublicKey key = encryptKeys.get(i);
- if (!isExpired(key) && !key.isRevoked()) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
+ @Deprecated
private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
@@ -181,6 +171,7 @@ public class PgpKeyHelper {
return usableKeys;
}
+ @Deprecated
private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
@@ -199,16 +190,6 @@ public class PgpKeyHelper {
return usableKeys;
}
-
- public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- Log.e(Constants.TAG, "encryptKeys is null!");
- return null;
- }
- return encryptKeys.get(0);
- }
-
public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
if (signingKeys.size() == 0) {
@@ -253,6 +234,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
@@ -298,6 +280,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
+ @Deprecated
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@@ -328,11 +311,13 @@ public class PgpKeyHelper {
return false;
}
+ @Deprecated
public static boolean isSigningKey(PGPSecretKey key) {
return isSigningKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
+ @Deprecated
public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@@ -358,11 +343,13 @@ public class PgpKeyHelper {
return false;
}
+ @Deprecated
public static boolean isAuthenticationKey(PGPSecretKey key) {
return isAuthenticationKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
+ @Deprecated
public static boolean isAuthenticationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@@ -388,6 +375,7 @@ public class PgpKeyHelper {
return false;
}
+ @Deprecated
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 30cac9b77..48cc5d6da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -279,19 +268,15 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
- String signingUserId = null;
+ CachedSecretKey signingKey = null;
if (enableSignature) {
+ CachedSecretKeyRing signingKeyRing = null;
try {
- signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId);
- signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
- KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ signingKeyRing = mProviderHelper.getCachedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
- signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing);
+ signingKey = signingKeyRing.getSigningSubKey();
if (signingKey == null) {
throw new NoSigningKeyException();
}
@@ -302,10 +287,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
+ try {
+ signingKey.unlock(mSignaturePassphrase);
+ } catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
}
@@ -333,13 +317,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id);
- PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing);
- if (key != null) {
- JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
- new JcePublicKeyKeyEncryptionMethodGenerator(key);
- cPk.addMethod(pubKeyEncryptionGenerator);
- }
+ CachedPublicKeyRing keyRing = mProviderHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
+ CachedPublicKey key = keyRing.getFirstEncryptSubkey();
+ cPk.addMethod(key.getPubKeyEncryptionGenerator());
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "key not found!", e);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
@@ -353,29 +336,18 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
- // content signer based on signing key algorithm and chosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- int signatureType;
- if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) {
- // for sign-only ascii text
- signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
- } else {
- signatureType = PGPSignature.BINARY_DOCUMENT;
- }
-
- if (mSignatureForceV3) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(signatureType, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(signatureType, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, signingUserId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
+ try {
+ boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
+ if (mSignatureForceV3) {
+ signatureV3Generator = signingKey.getV3SignatureGenerator(
+ mSignatureHashAlgorithm,cleartext);
+ } else {
+ signatureGenerator = signingKey.getSignatureGenerator(
+ mSignatureHashAlgorithm, cleartext);
+ }
+ } catch (PgpGeneralException e) {
+ // TODO throw correct type of exception (which shouldn't be PGPException)
+ throw new KeyExtractionException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index a4fa3dac9..a3c9fab1b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -110,6 +110,8 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
+ public static final String PUBKEY_DATA = "pubkey_data";
+ public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
+ public static Uri buildGenericKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
+ }
+
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 1dc822ac2..64dab3020 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -254,6 +254,12 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
+ projectionMap.put(KeyRings.PUBKEY_DATA,
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PUBKEY_DATA);
+ projectionMap.put(KeyRings.PRIVKEY_DATA,
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PRIVKEY_DATA);
projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
@@ -295,6 +301,22 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET
+ ")"
+ // fairly expensive join (due to blob data), only do it when requested
+ + (Arrays.asList(projection).contains(KeyRings.PUBKEY_DATA) ?
+ " INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ + ")"
+ : "")
+ // fairly expensive join (due to blob data), only do it when requested
+ + (Arrays.asList(projection).contains(KeyRings.PRIVKEY_DATA) ?
+ " LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ + ")"
+ : "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 214a9988c..80a3fe6e6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -36,9 +36,10 @@ import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -167,6 +168,7 @@ public class ProviderHelper {
}
}
+ @Deprecated
public Map<Long, PGPKeyRing> getPGPKeyRings(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
@@ -188,6 +190,94 @@ public class ProviderHelper {
return result;
}
+ public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws NotFoundException {
+ Cursor cursor = mContentResolver.query(queryUri,
+ new String[] {
+ KeyRings.MASTER_KEY_ID, KeyRings.KEY_SIZE,
+ KeyRings.IS_REVOKED, KeyRings.CAN_CERTIFY,
+ KeyRings.CREATION, KeyRings.EXPIRY,
+ KeyRings.ALGORITHM, KeyRings.FINGERPRINT,
+ KeyRings.USER_ID, KeyRings.VERIFIED,
+ KeyRings.HAS_SECRET, KeyRings.PUBKEY_DATA
+ }, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ long masterKeyId = cursor.getLong(0);
+ int keySize = cursor.getInt(1);
+ boolean isRevoked = cursor.getInt(2) > 0;
+ boolean canCertify = cursor.getInt(3) > 0;
+ long creation = cursor.getLong(4);
+ long expiry = cursor.getLong(5);
+ int algorithm = cursor.getInt(6);
+ byte[] fingerprint = cursor.getBlob(7);
+ String userId = cursor.getString(8);
+ int verified = cursor.getInt(9);
+ boolean hasSecret = cursor.getInt(10) > 0;
+ byte[] pubkey = cursor.getBlob(11);
+ return new CachedPublicKeyRing(
+ masterKeyId, keySize, isRevoked, canCertify,
+ creation, expiry, algorithm, fingerprint,
+ userId, verified, hasSecret, pubkey
+ );
+ } else {
+ throw new NotFoundException("Key not found!");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ public CachedSecretKeyRing getCachedSecretKeyRing(long id) throws NotFoundException {
+ return getCachedSecretKeyRing(KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
+ }
+
+ public CachedSecretKeyRing getCachedSecretKeyRing(Uri queryUri) throws NotFoundException {
+ Cursor cursor = mContentResolver.query(queryUri,
+ new String[] {
+ KeyRings.MASTER_KEY_ID, KeyRings.KEY_SIZE,
+ KeyRings.IS_REVOKED, KeyRings.CAN_CERTIFY,
+ KeyRings.CREATION, KeyRings.EXPIRY,
+ KeyRings.ALGORITHM, KeyRings.FINGERPRINT,
+ KeyRings.USER_ID, KeyRings.VERIFIED,
+ KeyRings.HAS_SECRET, KeyRings.PRIVKEY_DATA
+ }, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ // check if a privkey is actually available
+ byte[] privkey = cursor.getBlob(11);
+ if(privkey == null) {
+ throw new NotFoundException("Key found, but no secret key available!");
+ }
+
+ long masterKeyId = cursor.getLong(0);
+ int keySize = cursor.getInt(1);
+ boolean isRevoked = cursor.getInt(2) > 0;
+ boolean canCertify = cursor.getInt(3) > 0;
+ long creation = cursor.getLong(4);
+ long expiry = cursor.getLong(5);
+ int algorithm = cursor.getInt(6);
+ byte[] fingerprint = cursor.getBlob(7);
+ String userId = cursor.getString(8);
+ int verified = cursor.getInt(9);
+ boolean hasSecret = cursor.getInt(10) > 0;
+ return new CachedSecretKeyRing(
+ masterKeyId, keySize, isRevoked, canCertify,
+ creation, expiry, algorithm, fingerprint,
+ userId, verified, hasSecret, privkey
+ );
+ } else {
+ throw new NotFoundException("Key not found!");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Deprecated
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
Map<Long, PGPKeyRing> result = getPGPKeyRings(queryUri);
if (result.isEmpty()) {
@@ -197,6 +287,7 @@ public class ProviderHelper {
}
}
+ @Deprecated
public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
@@ -204,6 +295,7 @@ public class ProviderHelper {
return getPGPPublicKeyRing(masterKeyId);
}
+ @Deprecated
public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
@@ -214,6 +306,7 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
+ @Deprecated
public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
@@ -222,6 +315,7 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
+ @Deprecated
public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(queryUri);