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 48974ef17..66b1af50a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -26,6 +26,7 @@ import java.security.interfaces.RSAPrivateCrtKey; import java.util.Date; import java.util.Map; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.S2K; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.openpgp.AuthenticationSignatureGenerator; @@ -37,13 +38,7 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; -import org.bouncycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; -import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; -import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder; -import org.bouncycastle.openpgp.operator.jcajce.SessionKeySecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.*; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; @@ -219,8 +214,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return true; } - private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, - Map signedHashes) { + private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, Map signedHashes) { if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { // use synchronous "NFC based" SignerBuilder return new NfcSyncPGPContentSignerBuilder( @@ -253,6 +247,20 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } } + + private PGPContentSignerBuilder getAuthenticationContentSignerBuilder(int hashAlgorithm, Map signedHashes) { + if (getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) { + // content signer feeding the input directly into the signature engine, + // since EdDSA hashes the input anyway + return new EdDsaAuthenticationContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), hashAlgorithm) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + } else { + return getContentSignerBuilder(hashAlgorithm, signedHashes); + } + } + public AuthenticationSignatureGenerator getAuthenticationSignatureGenerator(int hashAlgorithm, Map signedHashes) throws PgpGeneralException { @@ -260,10 +268,12 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { throw new PrivateKeyNotUnlockedException(); } - PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgorithm, signedHashes); + PGPContentSignerBuilder contentSignerBuilder = + getAuthenticationContentSignerBuilder(hashAlgorithm, signedHashes); try { - AuthenticationSignatureGenerator signatureGenerator = new AuthenticationSignatureGenerator(contentSignerBuilder); + AuthenticationSignatureGenerator signatureGenerator = + new AuthenticationSignatureGenerator(contentSignerBuilder); signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, mPrivateKey); return signatureGenerator; diff --git a/libkeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/EdDsaAuthenticationContentSignerBuilder.java b/libkeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/EdDsaAuthenticationContentSignerBuilder.java new file mode 100644 index 000000000..91289b478 --- /dev/null +++ b/libkeychain/src/main/java/org/bouncycastle/openpgp/operator/jcajce/EdDsaAuthenticationContentSignerBuilder.java @@ -0,0 +1,101 @@ +package org.bouncycastle.openpgp.operator.jcajce; + +import org.bouncycastle.jcajce.provider.asymmetric.eddsa.EdDSAEngine; +import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveTable; +import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSAParameterSpec; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPRuntimeOperationException; +import org.bouncycastle.openpgp.operator.PGPContentSigner; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.security.*; + +public class EdDsaAuthenticationContentSignerBuilder implements PGPContentSignerBuilder { + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + private int hashAlgorithm; + private int keyAlgorithm; + + public EdDsaAuthenticationContentSignerBuilder(int keyAlgorithm, int hashAlgorithm) { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public EdDsaAuthenticationContentSignerBuilder setProvider(Provider provider) { + keyConverter.setProvider(provider); + return this; + } + + public EdDsaAuthenticationContentSignerBuilder setProvider(String providerName) { + keyConverter.setProvider(providerName); + return this; + } + + private Signature createSignature() throws NoSuchAlgorithmException { + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519"); + return new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); + } + + public PGPContentSigner build(final int signatureType, final long keyID, final PrivateKey privateKey) + throws PGPException { + Signature signatureEdDsa; + try { + signatureEdDsa = createSignature(); + } catch (NoSuchAlgorithmException e) { + throw new PGPException("unable to create Signature.", e); + } + final Signature signature = signatureEdDsa; + + final ByteArrayOutputStream dataOutputStream = new ByteArrayOutputStream(); + + try { + signature.initSign(privateKey); + } catch (InvalidKeyException e) { + throw new PGPException("invalid key.", e); + } + + 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 new SignatureOutputStream(signature); + } + + public byte[] getSignature() { + try { + return signature.sign(); + } catch (SignatureException e) { + throw new PGPRuntimeOperationException("Unable to create signature: " + e.getMessage(), e); + } + } + + public byte[] getDigest() { + return null; + } + }; + } + + public PGPContentSigner build(final int signatureType, PGPPrivateKey privateKey) throws PGPException { + if (privateKey instanceof JcaPGPPrivateKey) { + return build(signatureType, privateKey.getKeyID(), ((JcaPGPPrivateKey) privateKey).getPrivateKey()); + } else { + return build(signatureType, privateKey.getKeyID(), keyConverter.getPrivateKey(privateKey)); + } + } +}