From 2176e1ef1c29ca4549b256745c26c871503fe5ce Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 3 May 2014 19:04:23 +0200 Subject: [PATCH] wrapped-key-ring: move more helper methods into keys --- .../keychain/pgp/CachedPublicKey.java | 108 +++++++++++++++++- .../keychain/pgp/CachedSecretKey.java | 22 ++-- .../keychain/pgp/CachedSecretKeyRing.java | 35 ++++++ .../keychain/pgp/PgpKeyHelper.java | 1 + .../service/PassphraseCacheService.java | 2 + 5 files changed, 153 insertions(+), 15 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java index fb065c85f..08b9d5a0c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedPublicKey.java @@ -1,26 +1,128 @@ package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPOnePassSignature; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; 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.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; public class CachedPublicKey { // this is the parent key ring - private final CachedPublicKeyRing mRing; + final CachedKeyRing mRing; private final PGPPublicKey mKey; - CachedPublicKey(CachedPublicKeyRing ring, PGPPublicKey key) { + CachedPublicKey(CachedKeyRing ring, PGPPublicKey key) { mRing = ring; mKey = key; } - public CachedPublicKeyRing getKeyRing() { + public long getKeyId() { + return mKey.getKeyID(); + } + + public Date getCreationTime() { + return mKey.getCreationTime(); + } + + public Date getExpiryTime() { + Date creationDate = getCreationTime(); + if (mKey.getValidDays() == 0) { + // no expiry + return null; + } + Calendar calendar = GregorianCalendar.getInstance(); + calendar.setTime(creationDate); + calendar.add(Calendar.DATE, mKey.getValidDays()); + + return calendar.getTime(); + } + + public boolean isMasterKey() { + return mKey.isMasterKey(); + } + + public int getAlgorithm() { + return mKey.getAlgorithm(); + } + + public IterableIterator getUserIds() { + return new IterableIterator(mKey.getUserIDs()); + } + + private Integer mCacheUsage = null; + @SuppressWarnings("unchecked") + public int getKeyUsage() { + if(mCacheUsage == null) { + mCacheUsage = 0; + if (mKey.getVersion() >= 4) { + for (PGPSignature sig : new IterableIterator(mKey.getSignatures())) { + if (mKey.isMasterKey() && sig.getKeyID() != mKey.getKeyID()) { + continue; + } + + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + if (hashed != null) { + mCacheUsage |= hashed.getKeyFlags(); + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + if (unhashed != null) { + mCacheUsage |= unhashed.getKeyFlags(); + } + } + } + } + return mCacheUsage; + } + + public boolean canAuthenticate() { + return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0; + } + + public boolean canCertify() { + return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0; + } + + public boolean canEncrypt() { + if (!mKey.isEncryptionKey()) { + return false; + } + + // special cases + if (mKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) { + return true; + } + + if (mKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) { + return true; + } + + return mKey.getVersion() <= 3 || + (getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0; + + } + + public boolean canSign() { + // special case + if (mKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) { + return true; + } + + return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0; + } + + public CachedKeyRing getKeyRing() { return mRing; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java index ea302ea0b..d0b18d757 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKey.java @@ -1,5 +1,6 @@ package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; @@ -26,28 +27,25 @@ import java.security.NoSuchProviderException; import java.security.SignatureException; import java.util.List; -public class CachedSecretKey { +public class CachedSecretKey extends CachedPublicKey { - // this is the parent key ring - private final CachedSecretKeyRing mRing; - - private final PGPSecretKey mKey; + private final PGPSecretKey mSecretKey; private PGPPrivateKey mPrivateKey = null; CachedSecretKey(CachedSecretKeyRing ring, PGPSecretKey key) { - mRing = ring; - mKey = key; + super(ring, key.getPublicKey()); + mSecretKey = key; } public CachedSecretKeyRing getRing() { - return mRing; + return (CachedSecretKeyRing) 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); + mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); } catch (PGPException e) { throw new PgpGeneralException("error extracting key!", e); } @@ -64,7 +62,7 @@ public class CachedSecretKey { // content signer based on signing key algorithm and chosen hash algorithm JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - mKey.getPublicKey().getAlgorithm(), hashAlgo) + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); int signatureType; @@ -96,7 +94,7 @@ public class CachedSecretKey { // content signer based on signing key algorithm and chosen hash algorithm JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - mKey.getPublicKey().getAlgorithm(), hashAlgo) + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); int signatureType; @@ -144,7 +142,7 @@ public class CachedSecretKey { { // TODO: SHA256 fixed? JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - mKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) + mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java index 5403e1510..097f530fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CachedSecretKeyRing.java @@ -1,13 +1,19 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; 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.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.sufficientlysecure.keychain.util.IterableIterator; +import java.util.Iterator; + public class CachedSecretKeyRing extends CachedKeyRing { private PGPSecretKeyRing mRing; @@ -31,6 +37,35 @@ public class CachedSecretKeyRing extends CachedKeyRing { return new CachedSecretKey(this, mRing.getSecretKey(id)); } + public IterableIterator iterator() { + return new IterableIterator(mRing.getSecretKeys()); + } + + 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; + } + } + /** This returns the subkey that should be used for signing. * At this point, this is simply the first suitable subkey. */ 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 e884c0e2f..5e78a5764 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -143,6 +143,7 @@ public class PgpKeyHelper { return usableKeys; } + @Deprecated public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) { Vector signingKeys = getUsableSigningKeys(keyRing); if (signingKeys.size() == 0) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index db4fecef0..88e974288 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -197,6 +197,7 @@ public class PassphraseCacheService extends Service { return cachedPassphrase; } + @Deprecated public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) { PGPSecretKey secretKey = null; boolean foundValidKey = false; @@ -228,6 +229,7 @@ public class PassphraseCacheService extends Service { * @param secretKeyId * @return true if it has a passphrase */ + @Deprecated public static boolean hasPassphrase(Context context, long secretKeyId) { // check if the key has no passphrase try {