From 761d87b661ef14023870ad7be107d33d69ab03e7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 21 May 2014 21:07:32 +0200 Subject: wrapped-key-ring: split up CachedKeyRing and WrappedKeyRing --- .../keychain/pgp/CachedKeyRing.java | 68 -------- .../keychain/pgp/CachedPublicKey.java | 62 ------- .../keychain/pgp/CachedPublicKeyRing.java | 184 -------------------- .../keychain/pgp/CachedSecretKey.java | 189 --------------------- .../keychain/pgp/CachedSecretKeyRing.java | 128 -------------- .../sufficientlysecure/keychain/pgp/KeyRing.java | 25 +++ .../keychain/pgp/PgpDecryptVerify.java | 30 ++-- .../keychain/pgp/PgpImportExport.java | 5 +- .../keychain/pgp/PgpKeyHelper.java | 3 - .../keychain/pgp/PgpKeyOperation.java | 4 +- .../keychain/pgp/PgpSignEncrypt.java | 10 +- .../keychain/pgp/WrappedKeyRing.java | 81 +++++++++ .../keychain/pgp/WrappedPublicKey.java | 62 +++++++ .../keychain/pgp/WrappedPublicKeyRing.java | 179 +++++++++++++++++++ .../keychain/pgp/WrappedSecretKey.java | 189 +++++++++++++++++++++ .../keychain/pgp/WrappedSecretKeyRing.java | 123 ++++++++++++++ .../pgp/exception/PgpGeneralException.java | 3 + 17 files changed, 690 insertions(+), 655 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java deleted file mode 100644 index def673469..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedKeyRing.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -public abstract class CachedKeyRing { - - private final long mMasterKeyId; - private final String mUserId; - private final boolean mHasAnySecret; - private final boolean mIsRevoked; - private final boolean mCanCertify; - private final long mHasEncryptId; - private final long mHasSignId; - private final int mVerified; - - protected CachedKeyRing(long masterKeyId, String userId, boolean hasAnySecret, - boolean isRevoked, boolean canCertify, long hasEncryptId, long hasSignId, - int verified) - { - mMasterKeyId = masterKeyId; - mUserId = userId; - mHasAnySecret = hasAnySecret; - mIsRevoked = isRevoked; - mCanCertify = canCertify; - mHasEncryptId = hasEncryptId; - mHasSignId = hasSignId; - mVerified = verified; - } - - public long getMasterKeyId() { - return mMasterKeyId; - } - - public String getPrimaryUserId() { - return mUserId; - } - - public boolean hasAnySecret() { - return mHasAnySecret; - } - - public boolean isRevoked() { - return mIsRevoked; - } - - public boolean canCertify() { - return mCanCertify; - } - - public long getEncryptId() { - return mHasEncryptId; - } - - public boolean hasEncrypt() { - return mHasEncryptId != 0; - } - - public long getSignId() { - return mHasSignId; - } - - public boolean hasSign() { - return mHasSignId != 0; - } - - public int getVerified() { - return mVerified; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java deleted file mode 100644 index dee03db6f..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java +++ /dev/null @@ -1,62 +0,0 @@ -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; -import org.sufficientlysecure.keychain.util.IterableIterator; - -import java.security.SignatureException; - -public class CachedPublicKey extends UncachedPublicKey { - - // this is the parent key ring - final CachedKeyRing mRing; - - CachedPublicKey(CachedKeyRing ring, PGPPublicKey key) { - super(key); - mRing = ring; - } - - public IterableIterator getUserIds() { - return new IterableIterator(mPublicKey.getUserIDs()); - } - - public CachedKeyRing getKeyRing() { - return mRing; - } - - JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() { - return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey); - } - - public void initSignature(PGPSignature sig) throws PGPException { - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - sig.init(contentVerifierBuilderProvider, mPublicKey); - } - - public void initSignature(PGPOnePassSignature sig) throws PGPException { - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - sig.init(contentVerifierBuilderProvider, mPublicKey); - } - - /** Verify a signature for this pubkey, after it has been initialized by the signer using - * initSignature(). This method should probably move into a wrapped PGPSignature class - * at some point. - */ - public boolean verifySignature(PGPSignature sig, String uid) throws PGPException { - try { - return sig.verifyCertification(uid, mPublicKey); - } catch (SignatureException e) { - throw new PGPException("Error!", e); - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java deleted file mode 100644 index 108ed1a4a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKeyRing.java +++ /dev/null @@ -1,184 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.SignatureSubpacketTags; -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.Iterator; - -public class CachedPublicKeyRing extends CachedKeyRing { - - private PGPPublicKeyRing mRing; - private final byte[] mPubKey; - - public CachedPublicKeyRing(long masterKeyId, String userId, boolean hasAnySecret, - boolean isRevoked, boolean canCertify, long hasEncryptId, long hasSignId, - int verified, byte[] blob) - { - super(masterKeyId, userId, hasAnySecret, isRevoked, canCertify, - hasEncryptId, hasSignId, verified); - - mPubKey = blob; - } - - 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() { - return new CachedPublicKey(this, getRing().getPublicKey()); - } - - public CachedPublicKey getSubkey(long id) { - return new CachedPublicKey(this, getRing().getPublicKey(id)); - } - - /** Getter that returns the subkey that should be used for signing. */ - CachedPublicKey getEncryptionSubKey() throws PgpGeneralException { - PGPPublicKey key = getRing().getPublicKey(getEncryptId()); - if(key != null) { - CachedPublicKey cKey = new CachedPublicKey(this, key); - if(!cKey.canEncrypt()) { - throw new PgpGeneralException("key error"); - } - return cKey; - } - // TODO handle with proper exception - throw new PgpGeneralException("no encryption key available"); - } - - public boolean verifySubkeyBinding(CachedPublicKey cachedSubkey) { - boolean validSubkeyBinding = false; - boolean validTempSubkeyBinding = false; - boolean validPrimaryKeyBinding = false; - - PGPPublicKey masterKey = getRing().getPublicKey(); - PGPPublicKey subKey = cachedSubkey.getPublicKey(); - - // 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 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 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; - } - - public IterableIterator iterator() { - final Iterator it = getRing().getPublicKeys(); - return new IterableIterator(new Iterator() { - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public CachedPublicKey next() { - return new CachedPublicKey(CachedPublicKeyRing.this, it.next()); - } - - @Override - public void remove() { - it.remove(); - } - }); - } - -} \ 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 deleted file mode 100644 index 8e45ceb4f..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.PGPSignatureGenerator; -import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; -import org.spongycastle.openpgp.PGPSignatureSubpacketVector; -import org.spongycastle.openpgp.PGPUtil; -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; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; -import org.sufficientlysecure.keychain.util.IterableIterator; - -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; -import java.util.List; - -public class CachedSecretKey extends CachedPublicKey { - - private final PGPSecretKey mSecretKey; - private PGPPrivateKey mPrivateKey = null; - - CachedSecretKey(CachedSecretKeyRing ring, PGPSecretKey key) { - super(ring, key.getPublicKey()); - mSecretKey = key; - } - - public CachedSecretKeyRing getRing() { - return (CachedSecretKeyRing) mRing; - } - - /** Returns the wrapped PGPSecretKeyRing. - * This function is for compatibility only, should not be used anymore and will be removed - */ - @Deprecated - public PGPSecretKey getKeyExternal() { - return mSecretKey; - } - - public boolean unlock(String passphrase) throws PgpGeneralException { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); - } catch (PGPException e) { - return false; - } - if(mPrivateKey == null) { - throw new PgpGeneralException("error extracting key"); - } - return true; - } - - 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( - mSecretKey.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( - mSecretKey.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); - } - - /** - * Certify the given pubkeyid with the given masterkeyid. - * - * @param publicKeyRing Keyring to add certification to. - * @param userIds User IDs to certify, must not be null or empty - * @return A keyring with added certifications - */ - public UncachedKeyRing certifyUserIds(CachedPublicKeyRing publicKeyRing, List userIds) - throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, - PGPException, SignatureException { - - if(mPrivateKey == null) { - throw new PrivateKeyNotUnlockedException(); - } - - // create a signatureGenerator from the supplied masterKeyId and passphrase - PGPSignatureGenerator signatureGenerator; - { - // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); - } - - { // supply signatureGenerator with a SubpacketVector - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketVector packetVector = spGen.generate(); - signatureGenerator.setHashedSubpackets(packetVector); - } - - // get the master subkey (which we certify for) - PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey(); - - // fetch public key ring, add the certification and return it - for (String userId : new IterableIterator(userIds.iterator())) { - PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); - publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); - } - - PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey); - - return new UncachedKeyRing(ring); - } - - 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() - } - - public UncachedSecretKey getUncached() { - return new UncachedSecretKey(mSecretKey); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java deleted file mode 100644 index 398092aeb..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; -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.NoSuchProviderException; -import java.util.Iterator; - -public class CachedSecretKeyRing extends CachedKeyRing { - - private PGPSecretKeyRing mRing; - - public CachedSecretKeyRing(long masterKeyId, String userId, boolean hasAnySecret, - boolean isRevoked, boolean canCertify, long hasEncryptId, long hasSignId, - int verified, byte[] blob) - { - super(masterKeyId, userId, hasAnySecret, - isRevoked, canCertify, hasEncryptId, hasSignId, - verified); - - mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob); - } - - PGPSecretKeyRing getRing() { - return mRing; - } - - public CachedSecretKey getSubKey() { - return new CachedSecretKey(this, mRing.getSecretKey()); - } - - public CachedSecretKey getSubKey(long id) { - return new CachedSecretKey(this, mRing.getSecretKey(id)); - } - - /** Getter that returns the subkey that should be used for signing. */ - CachedSecretKey getSigningSubKey() throws PgpGeneralException { - PGPSecretKey key = mRing.getSecretKey(getSignId()); - if(key != null) { - CachedSecretKey cKey = new CachedSecretKey(this, key); - if(!cKey.canSign()) { - throw new PgpGeneralException("key error"); - } - return cKey; - } - // TODO handle with proper exception - throw new PgpGeneralException("no signing key available"); - } - - public boolean hasPassphrase() { - PGPSecretKey secretKey = null; - boolean foundValidKey = false; - for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) { - secretKey = (PGPSecretKey) keys.next(); - if (!secretKey.isPrivateKeyEmpty()) { - foundValidKey = true; - break; - } - } - if(!foundValidKey) { - return false; - } - - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider("SC").build("".toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - return testKey == null; - } catch(PGPException e) { - // this means the crc check failed -> passphrase required - return true; - } - } - - public UncachedSecretKeyRing changeSecretKeyPassphrase(String oldPassphrase, - String newPassphrase) - throws IOException, PGPException, NoSuchProviderException { - - if (oldPassphrase == null) { - oldPassphrase = ""; - } - if (newPassphrase == null) { - newPassphrase = ""; - } - - PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword( - mRing, - new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()), - new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey() - .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray())); - - return new UncachedSecretKeyRing(newKeyRing); - - } - - public IterableIterator iterator() { - final Iterator it = mRing.getSecretKeys(); - return new IterableIterator(new Iterator() { - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public CachedSecretKey next() { - return new CachedSecretKey(CachedSecretKeyRing.this, it.next()); - } - - @Override - public void remove() { - it.remove(); - } - }); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java new file mode 100644 index 000000000..cfbad89b7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java @@ -0,0 +1,25 @@ +package org.sufficientlysecure.keychain.pgp; + +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; + +public abstract class KeyRing { + + abstract public long getMasterKeyId() throws PgpGeneralException; + + abstract public String getPrimaryUserId() throws PgpGeneralException; + + abstract public boolean isRevoked() throws PgpGeneralException; + + abstract public boolean canCertify() throws PgpGeneralException; + + abstract public long getEncryptId() throws PgpGeneralException; + + abstract public boolean hasEncrypt() throws PgpGeneralException; + + abstract public long getSignId() throws PgpGeneralException; + + abstract public boolean hasSign() throws PgpGeneralException; + + abstract public int getVerified() throws PgpGeneralException; + +} 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 27e9e8ebd..21a7c20e3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -233,7 +233,7 @@ public class PgpDecryptVerify { PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; PGPPBEEncryptedData encryptedDataSymmetric = null; - CachedSecretKey secretEncryptionKey = null; + WrappedSecretKey secretEncryptionKey = null; Iterator it = enc.getEncryptedDataObjects(); boolean asymmetricPacketFound = false; boolean symmetricPacketFound = false; @@ -245,10 +245,10 @@ public class PgpDecryptVerify { PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; - CachedSecretKeyRing secretKeyRing; + WrappedSecretKeyRing secretKeyRing; try { // get actual keyring object based on master key id - secretKeyRing = mProviderHelper.getCachedSecretKeyRing( + secretKeyRing = mProviderHelper.getWrappedSecretKeyRing( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( Long.toString(encData.getKeyID())) ); @@ -368,8 +368,8 @@ public class PgpDecryptVerify { Object dataChunk = plainFact.nextObject(); OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); int signatureIndex = -1; - CachedPublicKeyRing signingRing = null; - CachedPublicKey signingKey = null; + WrappedPublicKeyRing signingRing = null; + WrappedPublicKey signingKey = null; if (dataChunk instanceof PGPCompressedData) { updateProgress(R.string.progress_decompressing_data, currentProgress, 100); @@ -393,7 +393,7 @@ public class PgpDecryptVerify { for (int i = 0; i < sigList.size(); ++i) { try { long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCachedPublicKeyRing( + signingRing = mProviderHelper.getWrappedPublicKeyRing( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( Long.toString(sigKeyId) ) @@ -413,7 +413,11 @@ public class PgpDecryptVerify { signatureResultBuilder.signatureAvailable(true); signatureResultBuilder.knownKey(true); signatureResultBuilder.keyId(signingRing.getMasterKeyId()); - signatureResultBuilder.userId(signingRing.getPrimaryUserId()); + try { + signatureResultBuilder.userId(signingRing.getPrimaryUserId()); + } catch(PgpGeneralException e) { + Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId()); + } signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0); signingKey.initSignature(signature); @@ -567,8 +571,8 @@ public class PgpDecryptVerify { throw new InvalidDataException(); } - CachedPublicKeyRing signingRing = null; - CachedPublicKey signingKey = null; + WrappedPublicKeyRing signingRing = null; + WrappedPublicKey signingKey = null; int signatureIndex = -1; // go through all signatures @@ -576,7 +580,7 @@ public class PgpDecryptVerify { for (int i = 0; i < sigList.size(); ++i) { try { long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCachedPublicKeyRing( + signingRing = mProviderHelper.getWrappedPublicKeyRing( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( Long.toString(sigKeyId) ) @@ -598,7 +602,11 @@ public class PgpDecryptVerify { signatureResultBuilder.signatureAvailable(true); signatureResultBuilder.knownKey(true); signatureResultBuilder.keyId(signingRing.getMasterKeyId()); - signatureResultBuilder.userId(signingRing.getPrimaryUserId()); + try { + signatureResultBuilder.userId(signingRing.getPrimaryUserId()); + } catch(PgpGeneralException e) { + Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId()); + } signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0); signingKey.initSignature(signature); 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 a0d2d5cea..5c4604647 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -27,7 +27,6 @@ import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.sufficientlysecure.keychain.Constants; @@ -101,7 +100,7 @@ public class PgpImportExport { } } - public boolean uploadKeyRingToServer(HkpKeyServer server, CachedPublicKeyRing keyring) { + public boolean uploadKeyRingToServer(HkpKeyServer server, WrappedPublicKeyRing keyring) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; try { @@ -229,7 +228,7 @@ public class PgpImportExport { updateProgress(progress * 100 / masterKeyIdsSize, 100); try { - CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing( + WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing( KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId) ); 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 59a48e7eb..b25c38f1a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -72,7 +72,6 @@ public class PgpKeyHelper { } @SuppressWarnings("unchecked") - @Deprecated public static boolean isEncryptionKey(PGPPublicKey key) { if (!key.isEncryptionKey()) { return false; @@ -114,7 +113,6 @@ public class PgpKeyHelper { } @SuppressWarnings("unchecked") - @Deprecated public static boolean isSigningKey(PGPPublicKey key) { if (key.getVersion() <= 3) { return true; @@ -146,7 +144,6 @@ public class PgpKeyHelper { } @SuppressWarnings("unchecked") - @Deprecated public static boolean isCertificationKey(PGPPublicKey key) { if (key.getVersion() <= 3) { return true; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index d0ca77da7..b9634c34a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -337,8 +337,8 @@ public class PgpKeyOperation { } - public UncachedKeyRing buildSecretKey(CachedSecretKeyRing wmKR, - CachedPublicKeyRing wpKR, + public UncachedKeyRing buildSecretKey(WrappedSecretKeyRing wmKR, + WrappedPublicKeyRing wpKR, SaveKeyringParcel saveParcel) throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { 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 96ee3f10a..24d090d04 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -266,11 +266,11 @@ public class PgpSignEncrypt { } /* Get keys for signature generation for later usage */ - CachedSecretKey signingKey = null; + WrappedSecretKey signingKey = null; if (enableSignature) { - CachedSecretKeyRing signingKeyRing; + WrappedSecretKeyRing signingKeyRing; try { - signingKeyRing = mProviderHelper.getCachedSecretKeyRing(mSignatureMasterKeyId); + signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId); } catch (ProviderHelper.NotFoundException e) { throw new NoSigningKeyException(); } @@ -316,9 +316,9 @@ public class PgpSignEncrypt { // Asymmetric encryption for (long id : mEncryptionMasterKeyIds) { try { - CachedPublicKeyRing keyRing = mProviderHelper.getCachedPublicKeyRing( + WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing( KeyRings.buildUnifiedKeyRingUri(Long.toString(id))); - CachedPublicKey key = keyRing.getEncryptionSubKey(); + WrappedPublicKey key = keyRing.getEncryptionSubKey(); cPk.addMethod(key.getPubKeyEncryptionGenerator()); } catch (PgpGeneralException e) { Log.e(Constants.TAG, "key not found!", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java new file mode 100644 index 000000000..94eb91420 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java @@ -0,0 +1,81 @@ +package org.sufficientlysecure.keychain.pgp; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.util.IterableIterator; + +public abstract class WrappedKeyRing extends KeyRing { + + private final boolean mHasAnySecret; + private final int mVerified; + + WrappedKeyRing(boolean hasAnySecret, int verified) { + mHasAnySecret = hasAnySecret; + mVerified = verified; + } + + public long getMasterKeyId() { + return getRing().getPublicKey().getKeyID(); + } + + public boolean hasAnySecret() { + return mHasAnySecret; + } + + public int getVerified() { + return mVerified; + } + + public String getPrimaryUserId() throws PgpGeneralException { + return (String) getRing().getPublicKey().getUserIDs().next(); + }; + + public boolean isRevoked() throws PgpGeneralException { + // Is the master key revoked? + return getRing().getPublicKey().isRevoked(); + } + + public boolean canCertify() throws PgpGeneralException { + return getRing().getPublicKey().isEncryptionKey(); + } + + public long getEncryptId() throws PgpGeneralException { + for(PGPPublicKey key : new IterableIterator(getRing().getPublicKeys())) { + if(PgpKeyHelper.isEncryptionKey(key)) { + return key.getKeyID(); + } + } + throw new PgpGeneralException("No valid encryption key found!"); + } + + public boolean hasEncrypt() throws PgpGeneralException { + try { + getEncryptId(); + return true; + } catch(PgpGeneralException e) { + return false; + } + } + + public long getSignId() throws PgpGeneralException { + for(PGPPublicKey key : new IterableIterator(getRing().getPublicKeys())) { + if(PgpKeyHelper.isSigningKey(key)) { + return key.getKeyID(); + } + } + throw new PgpGeneralException("No valid signing key found!"); + } + + public boolean hasSign() throws PgpGeneralException { + try { + getSignId(); + return true; + } catch (PgpGeneralException e) { + return false; + } + } + + abstract PGPKeyRing getRing(); + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java new file mode 100644 index 000000000..72352a451 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java @@ -0,0 +1,62 @@ +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; +import org.sufficientlysecure.keychain.util.IterableIterator; + +import java.security.SignatureException; + +public class WrappedPublicKey extends UncachedPublicKey { + + // this is the parent key ring + final KeyRing mRing; + + WrappedPublicKey(KeyRing ring, PGPPublicKey key) { + super(key); + mRing = ring; + } + + public IterableIterator getUserIds() { + return new IterableIterator(mPublicKey.getUserIDs()); + } + + public KeyRing getKeyRing() { + return mRing; + } + + JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() { + return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey); + } + + public void initSignature(PGPSignature sig) throws PGPException { + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + sig.init(contentVerifierBuilderProvider, mPublicKey); + } + + public void initSignature(PGPOnePassSignature sig) throws PGPException { + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + sig.init(contentVerifierBuilderProvider, mPublicKey); + } + + /** Verify a signature for this pubkey, after it has been initialized by the signer using + * initSignature(). This method should probably move into a wrapped PGPSignature class + * at some point. + */ + public boolean verifySignature(PGPSignature sig, String uid) throws PGPException { + try { + return sig.verifyCertification(uid, mPublicKey); + } catch (SignatureException e) { + throw new PGPException("Error!", e); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java new file mode 100644 index 000000000..624382165 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java @@ -0,0 +1,179 @@ +package org.sufficientlysecure.keychain.pgp; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.SignatureSubpacketTags; +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.Iterator; + +public class WrappedPublicKeyRing extends WrappedKeyRing { + + private PGPPublicKeyRing mRing; + private final byte[] mPubKey; + + public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) { + super(hasAnySecret, verified); + mPubKey = blob; + } + + PGPPublicKeyRing getRing() { + if(mRing == null) { + mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey); + } + return mRing; + } + + public void encode(ArmoredOutputStream stream) throws IOException { + getRing().encode(stream); + } + + public WrappedPublicKey getSubkey() { + return new WrappedPublicKey(this, getRing().getPublicKey()); + } + + public WrappedPublicKey getSubkey(long id) { + return new WrappedPublicKey(this, getRing().getPublicKey(id)); + } + + /** Getter that returns the subkey that should be used for signing. */ + WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException { + PGPPublicKey key = getRing().getPublicKey(getEncryptId()); + if(key != null) { + WrappedPublicKey cKey = new WrappedPublicKey(this, key); + if(!cKey.canEncrypt()) { + throw new PgpGeneralException("key error"); + } + return cKey; + } + // TODO handle with proper exception + throw new PgpGeneralException("no encryption key available"); + } + + public boolean verifySubkeyBinding(WrappedPublicKey cachedSubkey) { + boolean validSubkeyBinding = false; + boolean validTempSubkeyBinding = false; + boolean validPrimaryKeyBinding = false; + + PGPPublicKey masterKey = getRing().getPublicKey(); + PGPPublicKey subKey = cachedSubkey.getPublicKey(); + + // 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 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 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; + } + + public IterableIterator iterator() { + final Iterator it = getRing().getPublicKeys(); + return new IterableIterator(new Iterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public WrappedPublicKey next() { + return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next()); + } + + @Override + public void remove() { + it.remove(); + } + }); + } + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java new file mode 100644 index 000000000..a03e10098 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java @@ -0,0 +1,189 @@ +package org.sufficientlysecure.keychain.pgp; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +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; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; +import org.sufficientlysecure.keychain.util.IterableIterator; + +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.util.List; + +public class WrappedSecretKey extends WrappedPublicKey { + + private final PGPSecretKey mSecretKey; + private PGPPrivateKey mPrivateKey = null; + + WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) { + super(ring, key.getPublicKey()); + mSecretKey = key; + } + + public WrappedSecretKeyRing getRing() { + return (WrappedSecretKeyRing) mRing; + } + + /** Returns the wrapped PGPSecretKeyRing. + * This function is for compatibility only, should not be used anymore and will be removed + */ + @Deprecated + public PGPSecretKey getKeyExternal() { + return mSecretKey; + } + + public boolean unlock(String passphrase) throws PgpGeneralException { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + return false; + } + if(mPrivateKey == null) { + throw new PgpGeneralException("error extracting key"); + } + return true; + } + + 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( + mSecretKey.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( + mSecretKey.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); + } + + /** + * Certify the given pubkeyid with the given masterkeyid. + * + * @param publicKeyRing Keyring to add certification to. + * @param userIds User IDs to certify, must not be null or empty + * @return A keyring with added certifications + */ + public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List userIds) + throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, + PGPException, SignatureException { + + if(mPrivateKey == null) { + throw new PrivateKeyNotUnlockedException(); + } + + // create a signatureGenerator from the supplied masterKeyId and passphrase + PGPSignatureGenerator signatureGenerator; + { + // TODO: SHA256 fixed? + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); + } + + { // supply signatureGenerator with a SubpacketVector + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + PGPSignatureSubpacketVector packetVector = spGen.generate(); + signatureGenerator.setHashedSubpackets(packetVector); + } + + // get the master subkey (which we certify for) + PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey(); + + // fetch public key ring, add the certification and return it + for (String userId : new IterableIterator(userIds.iterator())) { + PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); + publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); + } + + PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey); + + return new UncachedKeyRing(ring); + } + + 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() + } + + public UncachedSecretKey getUncached() { + return new UncachedSecretKey(mSecretKey); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java new file mode 100644 index 000000000..c94b7dfba --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java @@ -0,0 +1,123 @@ +package org.sufficientlysecure.keychain.pgp; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +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.NoSuchProviderException; +import java.util.Iterator; + +public class WrappedSecretKeyRing extends WrappedKeyRing { + + private PGPSecretKeyRing mRing; + + public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified) + { + super(isRevoked, verified); + mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob); + } + + PGPSecretKeyRing getRing() { + return mRing; + } + + public WrappedSecretKey getSubKey() { + return new WrappedSecretKey(this, mRing.getSecretKey()); + } + + public WrappedSecretKey getSubKey(long id) { + return new WrappedSecretKey(this, mRing.getSecretKey(id)); + } + + /** Getter that returns the subkey that should be used for signing. */ + WrappedSecretKey getSigningSubKey() throws PgpGeneralException { + PGPSecretKey key = mRing.getSecretKey(getSignId()); + if(key != null) { + WrappedSecretKey cKey = new WrappedSecretKey(this, key); + if(!cKey.canSign()) { + throw new PgpGeneralException("key error"); + } + return cKey; + } + // TODO handle with proper exception + throw new PgpGeneralException("no signing key available"); + } + + public boolean hasPassphrase() { + PGPSecretKey secretKey = null; + boolean foundValidKey = false; + for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) { + secretKey = (PGPSecretKey) keys.next(); + if (!secretKey.isPrivateKeyEmpty()) { + foundValidKey = true; + break; + } + } + if(!foundValidKey) { + return false; + } + + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider("SC").build("".toCharArray()); + PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); + return testKey == null; + } catch(PGPException e) { + // this means the crc check failed -> passphrase required + return true; + } + } + + public UncachedSecretKeyRing changeSecretKeyPassphrase(String oldPassphrase, + String newPassphrase) + throws IOException, PGPException, NoSuchProviderException { + + if (oldPassphrase == null) { + oldPassphrase = ""; + } + if (newPassphrase == null) { + newPassphrase = ""; + } + + PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword( + mRing, + new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()), + new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey() + .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray())); + + return new UncachedSecretKeyRing(newKeyRing); + + } + + public IterableIterator iterator() { + final Iterator it = mRing.getSecretKeys(); + return new IterableIterator(new Iterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public WrappedSecretKey next() { + return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next()); + } + + @Override + public void remove() { + it.remove(); + } + }); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java index 37d21eea4..f37a61852 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java @@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception { public PgpGeneralException(String message, Throwable cause) { super(message, cause); } + public PgpGeneralException(Throwable cause) { + super(cause); + } } -- cgit v1.2.3