From b373c866e74adb2ee5c27479b6fac75d9e6f76fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 24 Sep 2014 02:01:53 +0200 Subject: Merge openpgp-card lib into OpenKeychain, it was already depending on OK, no need to separate --- .../jcajce/NfcSyncPGPContentSignerBuilder.java | 144 +++++++++++ ...fcSyncPublicKeyDataDecryptorFactoryBuilder.java | 275 +++++++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java create mode 100644 OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java (limited to 'OpenKeychain/src/main/java/org/spongycastle/openpgp') diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java new file mode 100644 index 000000000..e0286ec15 --- /dev/null +++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann + * Copyright (c) 2000-2013 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + * + * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. + */ + +package org.spongycastle.openpgp.operator.jcajce; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +import java.io.OutputStream; +import java.security.Provider; +import java.util.Date; + +/** + * This class is based on JcaPGPContentSignerBuilder. + * + * Instead of using a Signature object based on a privateKey, this class only calculates the digest + * of the output stream and gives the result back using a RuntimeException. + */ +public class NfcSyncPGPContentSignerBuilder + implements PGPContentSignerBuilder +{ + private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + private int hashAlgorithm; + private int keyAlgorithm; + private long keyID; + + private byte[] signedHash; + private Date creationTimestamp; + + public static class NfcInteractionNeeded extends RuntimeException + { + public byte[] hashToSign; + public Date creationTimestamp; + public int hashAlgo; + + public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo, Date creationTimestamp) + { + super("NFC interaction required!"); + this.hashToSign = hashToSign; + this.hashAlgo = hashAlgo; + this.creationTimestamp = creationTimestamp; + } + } + + public NfcSyncPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm, long keyID, byte[] signedHash, Date creationTimestamp) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + this.keyID = keyID; + this.signedHash = signedHash; + this.creationTimestamp = creationTimestamp; + } + + public NfcSyncPGPContentSignerBuilder setProvider(Provider provider) + { + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public NfcSyncPGPContentSignerBuilder setProvider(String providerName) + { + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public NfcSyncPGPContentSignerBuilder setDigestProvider(Provider provider) + { + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public NfcSyncPGPContentSignerBuilder setDigestProvider(String providerName) + { + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public PGPContentSigner build(final int signatureType, PGPPrivateKey privateKey) + throws PGPException { + // NOTE: privateKey is null in this case! + return build(signatureType, keyID); + } + + public PGPContentSigner build(final int signatureType, final long keyID) + throws PGPException + { + final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm); + + return new PGPContentSigner() + { + public int getType() + { + return signatureType; + } + + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return keyID; + } + + public OutputStream getOutputStream() + { + return digestCalculator.getOutputStream(); + } + + public byte[] getSignature() { + if (signedHash != null) { + // we already have the signed hash from a previous execution, return this! + return signedHash; + } else { + // catch this when signatureGenerator.generate() is executed and divert digest to card, + // when doing the operation again reuse creationTimestamp (this will be hashed) + throw new NfcInteractionNeeded(digestCalculator.getDigest(), getHashAlgorithm(), creationTimestamp); + } + } + + public byte[] getDigest() + { + return digestCalculator.getDigest(); + } + }; + } +} diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java new file mode 100644 index 000000000..ffa154876 --- /dev/null +++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java @@ -0,0 +1,275 @@ +/** + * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann + * + * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. + */ + +package org.spongycastle.openpgp.operator.jcajce; + +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; + +import java.security.Provider; + +/** + * This class is based on JcePublicKeyDataDecryptorFactoryBuilder + * + */ +public class NfcSyncPublicKeyDataDecryptorFactoryBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private OperatorHelper contentHelper = new OperatorHelper(new DefaultJcaJceHelper()); + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); +// private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); +// private JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator(); + + public static class NfcInteractionNeeded extends RuntimeException + { + public byte[] encryptedSessionKey; + + public NfcInteractionNeeded(byte[] encryptedSessionKey) + { + super("NFC interaction required!"); + this.encryptedSessionKey = encryptedSessionKey; + } + } + + public NfcSyncPublicKeyDataDecryptorFactoryBuilder() + { + } + + /** + * Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param provider provider object for cryptographic primitives. + * @return the current builder. + */ + public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + keyConverter.setProvider(provider); + this.contentHelper = helper; + + return this; + } + + /** + * Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param providerName the name of the provider to reference for cryptographic primitives. + * @return the current builder. + */ + public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + keyConverter.setProvider(providerName); + this.contentHelper = helper; + + return this; + } + + public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(Provider provider) + { + this.contentHelper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(String providerName) + { + this.contentHelper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PublicKeyDataDecryptorFactory build(final byte[] nfcDecrypted) { + return new PublicKeyDataDecryptorFactory() + { + public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) + throws PGPException + { + if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) + { + throw new PGPException("ECDH not supported!"); + } + + return decryptSessionData(keyAlgorithm, secKeyData, nfcDecrypted); + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); + } + }; + } + +// public PublicKeyDataDecryptorFactory build(final PrivateKey privKey) +// { +// return new PublicKeyDataDecryptorFactory() +// { +// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) +// throws PGPException +// { +// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) +// { +// throw new PGPException("ECDH requires use of PGPPrivateKey for decryption"); +// } +// return decryptSessionData(keyAlgorithm, privKey, secKeyData); +// } +// +// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) +// throws PGPException +// { +// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); +// } +// }; +// } + +// public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey, final byte[] nfcDecrypted) +// { +// return new PublicKeyDataDecryptorFactory() +// { +// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) +// throws PGPException +// { +// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) +// { +// return decryptSessionData(privKey.getPrivateKeyDataPacket(), privKey.getPublicKeyPacket(), secKeyData); +// } +// +// return decryptSessionData(keyAlgorithm, keyConverter.getPrivateKey(privKey), secKeyData, nfcDecrypted); +// } +// +// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) +// throws PGPException +// { +// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); +// } +// }; +// } + +// private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData) +// throws PGPException +// { +// ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey(); +// X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID()); +// +// byte[] enc = secKeyData[0]; +// +// int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8; +// byte[] pEnc = new byte[pLen]; +// +// System.arraycopy(enc, 2, pEnc, 0, pLen); +// +// byte[] keyEnc = new byte[enc[pLen + 2]]; +// +// System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length); +// +// Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm()); +// +// ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize(); +// +// RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm()); +// Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap"); +// +// try +// { +// c.init(Cipher.UNWRAP_MODE, key); +// +// Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY); +// +// return PGPPad.unpadSessionData(paddedSessionKey.getEncoded()); +// } +// catch (InvalidKeyException e) +// { +// throw new PGPException("error setting asymmetric cipher", e); +// } +// catch (NoSuchAlgorithmException e) +// { +// throw new PGPException("error setting asymmetric cipher", e); +// } +// } + + private byte[] decryptSessionData(int keyAlgorithm, byte[][] secKeyData, byte[] nfcDecrypted) + throws PGPException + { +// Cipher c1 = helper.createPublicKeyCipher(keyAlgorithm); +// +// try +// { +// c1.init(Cipher.DECRYPT_MODE, privKey); +// } +// catch (InvalidKeyException e) +// { +// throw new PGPException("error setting asymmetric cipher", e); +// } + + if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT + || keyAlgorithm == PGPPublicKey.RSA_GENERAL) + { + byte[] bi = secKeyData[0]; // encoded MPI + + if (nfcDecrypted != null) { + // we already have the decrypted bytes from a previous execution, return this! + return nfcDecrypted; + } else { + // catch this when decryptSessionData() is executed and divert digest to card, + // when doing the operation again reuse nfcDecrypted + throw new NfcInteractionNeeded(bi); + } + +// c1.update(bi, 2, bi.length - 2); + } + else + { + throw new PGPException("ElGamal not supported!"); + +// ElGamalKey k = (ElGamalKey)privKey; +// int size = (k.getParameters().getP().bitLength() + 7) / 8; +// byte[] tmp = new byte[size]; +// +// byte[] bi = secKeyData[0]; // encoded MPI +// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... +// { +// c1.update(bi, 3, bi.length - 3); +// } +// else +// { +// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); +// c1.update(tmp); +// } +// +// bi = secKeyData[1]; // encoded MPI +// for (int i = 0; i != tmp.length; i++) +// { +// tmp[i] = 0; +// } +// +// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... +// { +// c1.update(bi, 3, bi.length - 3); +// } +// else +// { +// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); +// c1.update(tmp); +// } + } + +// try +// { +// return c1.doFinal(); +// } +// catch (Exception e) +// { +// throw new PGPException("exception decrypting session data", e); +// } + } +} -- cgit v1.2.3