diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java | 121 |
1 files changed, 84 insertions, 37 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index af058b618..e39924f7e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.S2K; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; @@ -27,20 +29,24 @@ 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.PGPContentSignerBuilder; 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.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder; 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 org.sufficientlysecure.keychain.util.Log; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SignatureException; +import java.util.Date; +import java.util.LinkedList; import java.util.List; /** Wrapper for a PGPSecretKey. @@ -59,6 +65,11 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { private final PGPSecretKey mSecretKey; private PGPPrivateKey mPrivateKey = null; + private int mPrivateKeyState = PRIVATE_KEY_STATE_LOCKED; + private static int PRIVATE_KEY_STATE_LOCKED = 0; + private static int PRIVATE_KEY_STATE_UNLOCKED = 1; + private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2; + CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) { super(ring, key.getPublicKey()); mSecretKey = key; @@ -68,11 +79,23 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return (CanonicalizedSecretKeyRing) mRing; } + /** + * Returns true on right passphrase + */ public boolean unlock(String passphrase) throws PgpGeneralException { + // handle keys on OpenPGP cards like they were unlocked + if (mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K + && mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) { + mPrivateKeyState = PRIVATE_KEY_STATE_DIVERT_TO_CARD; + return true; + } + + // try to extract keys using the passphrase try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); + mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED; } catch (PGPException e) { return false; } @@ -82,16 +105,56 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return true; } - public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext) + // TODO: just a hack currently + public LinkedList<Integer> getSupportedHashAlgorithms() { + LinkedList<Integer> supported = new LinkedList<Integer>(); + + if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { + // TODO: only works with SHA256 ?! + supported.add(HashAlgorithmTags.SHA256); + } else { + supported.add(HashAlgorithmTags.MD5); + supported.add(HashAlgorithmTags.RIPEMD160); + supported.add(HashAlgorithmTags.SHA1); + supported.add(HashAlgorithmTags.SHA224); + supported.add(HashAlgorithmTags.SHA256); + supported.add(HashAlgorithmTags.SHA384); + supported.add(HashAlgorithmTags.SHA512); // preferred is latest + } + + return supported; + } + + public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, + byte[] nfcSignedHash, Date nfcCreationTimestamp) throws PgpGeneralException { - if(mPrivateKey == null) { + if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { 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); + PGPContentSignerBuilder contentSignerBuilder; + if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { + // to sign using nfc PgpSignEncrypt is executed two times. + // the first time it stops to return the PendingIntent for nfc connection and signing the hash + // the second time the signed hash is used. + // to get the same hash we cache the timestamp for the second round! + if (nfcCreationTimestamp == null) { + nfcCreationTimestamp = new Date(); + } + + // use synchronous "NFC based" SignerBuilder + contentSignerBuilder = new NfcSyncPGPContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo, + mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + + Log.d(Constants.TAG, "mSecretKey.getKeyID() "+ PgpKeyHelper.convertKeyIdToHex(mSecretKey.getKeyID())); + } else { + // content signer based on signing key algorithm and chosen hash algorithm + contentSignerBuilder = new JcaPGPContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + } int signatureType; if (cleartext) { @@ -107,43 +170,21 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback()); + if (nfcCreationTimestamp != null) { + spGen.setSignatureCreationTime(false, nfcCreationTimestamp); + Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); + } 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) { + // TODO: simply throw PGPException! throw new PgpGeneralException("Error initializing signature!", e); } } public PublicKeyDataDecryptorFactory getDecryptorFactory() { - if(mPrivateKey == null) { + // TODO: divert to card missing + if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) { throw new PrivateKeyNotUnlockedException(); } return new JcePublicKeyDataDecryptorFactoryBuilder() @@ -161,7 +202,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException { - if(mPrivateKey == null) { + // TODO: divert to card missing + if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) { throw new PrivateKeyNotUnlockedException(); } @@ -206,4 +248,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return new UncachedSecretKey(mSecretKey); } + // HACK + public PGPSecretKey getSecretKey() { + return mSecretKey; + } + } |