From c052cb493fbfeb781bbf1cae0009a8ea86dc69aa Mon Sep 17 00:00:00 2001 From: sp717 Date: Wed, 7 Aug 2024 14:21:43 -0700 Subject: [PATCH 01/14] Add Ed25519 --- README.md | 3 + csrc/ed_gen.cpp | 35 +++ csrc/java_evp_keys.cpp | 38 ++- csrc/keyutils.cpp | 21 +- csrc/keyutils.h | 7 +- csrc/sign.cpp | 35 ++- csrc/test_keyutils.cpp | 8 +- .../AmazonCorrettoCryptoProvider.java | 21 ++ .../corretto/crypto/provider/EdGen.java | 38 +++ .../corretto/crypto/provider/EvpEdEcKey.java | 11 + .../crypto/provider/EvpEdEcPrivateKey.java | 44 +++ .../crypto/provider/EvpEdEcPublicKey.java | 21 ++ .../crypto/provider/EvpKeyFactory.java | 23 ++ .../corretto/crypto/provider/EvpKeyType.java | 7 +- .../crypto/provider/EvpSignatureRaw.java | 6 + .../crypto/provider/test/EdDSATest.java | 289 ++++++++++++++++++ .../provider/test/EvpKeyFactoryTest.java | 1 + .../test/EvpSignatureSpecificTest.java | 3 + .../test/KeyReuseThreadStormTest.java | 32 ++ 19 files changed, 619 insertions(+), 24 deletions(-) create mode 100644 csrc/ed_gen.cpp create mode 100644 src/com/amazon/corretto/crypto/provider/EdGen.java create mode 100644 src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java create mode 100644 src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java create mode 100644 src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java create mode 100644 tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java diff --git a/README.md b/README.md index 6929da94..61363b0a 100644 --- a/README.md +++ b/README.md @@ -64,10 +64,12 @@ Signature algorithms: * SHA512withECDSA * SHA512withECDSAinP1363Format * RSASSA-PSS +* ED25519 KeyPairGenerator: * EC * RSA +* ED25519 KeyGenerator: * AES @@ -92,6 +94,7 @@ SecureRandom: KeyFactory: * EC * RSA +* ED25519 AlgorithmParameters: * EC. Please refer to [system properties](https://github.com/corretto/amazon-corretto-crypto-provider#other-system-properties) for more information. diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp new file mode 100644 index 00000000..c8db1cc6 --- /dev/null +++ b/csrc/ed_gen.cpp @@ -0,0 +1,35 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#include "auto_free.h" +#include "bn.h" +#include "buffer.h" +#include "env.h" +#include "generated-headers.h" +#include "keyutils.h" +#include "util.h" +#include +#include +#include + +using namespace AmazonCorrettoCryptoProvider; + +void generateEdEcKey(raii_env* env, EVP_PKEY_auto& key) +{ + EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr)); + CHECK_OPENSSL(ctx.isInitialized()); + CHECK_OPENSSL(EVP_PKEY_keygen_init(ctx) > 0); + CHECK_OPENSSL(EVP_PKEY_keygen(ctx, key.getAddressOfPtr())); +} + +JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EdGen_generateEvpEdEcKey(JNIEnv* pEnv, jclass) +{ + try { + raii_env env(pEnv); + EVP_PKEY_auto key; + generateEdEcKey(&env, key); + return reinterpret_cast(key.take()); + } catch (java_ex& ex) { + ex.throw_to_java(pEnv); + } + return 0; +} \ No newline at end of file diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index 44417df0..a518e2e5 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -104,7 +104,7 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_p { jni_borrow borrow = jni_borrow(env, pkcs8Buff, "pkcs8Buff"); - result.set(der2EvpPrivateKey(borrow, derLen, shouldCheckPrivate, EX_INVALID_KEY_SPEC)); + result.set(der2EvpPrivateKey(borrow, derLen, nativeValue, shouldCheckPrivate, EX_INVALID_KEY_SPEC)); if (EVP_PKEY_base_id(result) != nativeValue) { throw_java_ex(EX_INVALID_KEY_SPEC, "Incorrect key type"); } @@ -328,6 +328,42 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEcPriva } } +/* + * Class: com_amazon_corretto_crypto_provider_EvpEdEcPrivateKey + * Method: getPrivateKey + */ +JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdEcPrivateKey_getPrivateKey( + JNIEnv* pEnv, jclass, jlong keyHandle) +{ + jbyteArray result = NULL; + + try { + raii_env env(pEnv); + + EVP_PKEY* key = reinterpret_cast(keyHandle); + + EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new(key, nullptr)); + CHECK_OPENSSL(ctx.isInitialized()); + + OPENSSL_buffer_auto privateKeyBuffer; + PKCS8_PRIV_KEY_INFO_auto pkcs8 = PKCS8_PRIV_KEY_INFO_auto::from(EVP_PKEY2PKCS8(key)); + CHECK_OPENSSL(pkcs8.isInitialized()); + + // This next line allocates memory + int bufLen = i2d_PKCS8_PRIV_KEY_INFO(pkcs8, &privateKeyBuffer); + CHECK_OPENSSL(bufLen > 0); + + size_t bufLen2 = (size_t)bufLen; + CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufLen2) > 0); + + result = env->NewByteArray(bufLen2); + env->SetByteArrayRegion(result, 0, bufLen2, privateKeyBuffer); + } catch (java_ex& ex) { + ex.throw_to_java(pEnv); + } + return result; +} + JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpRsaKey_getModulus( JNIEnv* pEnv, jclass, jlong keyHandle) { diff --git a/csrc/keyutils.cpp b/csrc/keyutils.cpp index 0ac1e7e5..40c9a6f7 100644 --- a/csrc/keyutils.cpp +++ b/csrc/keyutils.cpp @@ -7,26 +7,27 @@ #include #include #include +#include namespace AmazonCorrettoCryptoProvider { -EVP_PKEY* der2EvpPrivateKey( - const unsigned char* der, const int derLen, bool shouldCheckPrivate, const char* javaExceptionClass) +EVP_PKEY* der2EvpPrivateKey(const unsigned char* der, + const int derLen, + const int nativeValue, + bool shouldCheckPrivate, + const char* javaExceptionClass) { const unsigned char* der_mutable_ptr = der; // openssl modifies the input pointer - PKCS8_PRIV_KEY_INFO* pkcs8Key = d2i_PKCS8_PRIV_KEY_INFO(NULL, &der_mutable_ptr, derLen); + EVP_PKEY* result = d2i_PrivateKey(nativeValue, NULL, &der_mutable_ptr, derLen); + if (der + derLen != der_mutable_ptr) { - if (pkcs8Key) { - PKCS8_PRIV_KEY_INFO_free(pkcs8Key); + if (result) { + EVP_PKEY_free(result); } throw_openssl(javaExceptionClass, "Extra key information"); } - if (!pkcs8Key) { - throw_openssl(javaExceptionClass, "Unable to parse DER key into PKCS8_PRIV_KEY_INFO"); - } - EVP_PKEY* result = EVP_PKCS82PKEY(pkcs8Key); - PKCS8_PRIV_KEY_INFO_free(pkcs8Key); + if (!result) { throw_openssl(javaExceptionClass, "Unable to convert PKCS8_PRIV_KEY_INFO to EVP_PKEY"); } diff --git a/csrc/keyutils.h b/csrc/keyutils.h index ff551461..e596c737 100644 --- a/csrc/keyutils.h +++ b/csrc/keyutils.h @@ -79,8 +79,11 @@ class EvpKeyContext { EvpKeyContext& operator=(const EvpKeyContext&) DELETE_IMPLICIT; }; -EVP_PKEY* der2EvpPrivateKey( - const unsigned char* der, const int derLen, const bool checkPrivateKey, const char* javaExceptionClass); +EVP_PKEY* der2EvpPrivateKey(const unsigned char* der, + const int derLen, + const int nativeValue, + const bool checkPrivateKey, + const char* javaExceptionClass); EVP_PKEY* der2EvpPublicKey(const unsigned char* der, const int derLen, const char* javaExceptionClass); bool checkKey(const EVP_PKEY* key); static bool inline BN_null_or_zero(const BIGNUM* bn) { return nullptr == bn || BN_is_zero(bn); } diff --git a/csrc/sign.cpp b/csrc/sign.cpp index f6b6408e..8be32193 100644 --- a/csrc/sign.cpp +++ b/csrc/sign.cpp @@ -66,7 +66,7 @@ bool initializeContext(raii_env& env, EVP_PKEY_up_ref(pKey); ctx->setKey(pKey); - if (md != nullptr) { + if (md != nullptr || EVP_PKEY_id(pKey) == EVP_PKEY_ED25519) { if (!ctx->setDigestCtx(EVP_MD_CTX_create())) { throw_openssl("Unable to create MD_CTX"); } @@ -168,8 +168,8 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignature_si true, // true->sign reinterpret_cast(pKey), reinterpret_cast(mdPtr), paddingType, reinterpret_cast(mgfMdPtr), pssSaltLen); - update(env, &ctx, digestSignUpdate, java_buffer::from_array(env, message, offset, length)); + update(env, &ctx, digestSignUpdate, java_buffer::from_array(env, message, offset, length)); return reinterpret_cast(ctx.moveToHeap()); } catch (java_ex& ex) { ex.throw_to_java(pEnv); @@ -452,8 +452,25 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignatu paddingType, reinterpret_cast(mgfMdPtr), pssSaltLen); std::vector > signature; - { - size_t sigLength; + size_t sigLength; + + int keyType = EVP_PKEY_id(reinterpret_cast(pKey)); + + if (keyType == EVP_PKEY_ED25519) { + jni_borrow message(env, messageBuf, "message"); + + if (!EVP_DigestSign(ctx.getDigestCtx(), NULL, &sigLength, message.data(), message.len())) { + throw_openssl("Signature failed"); + } + + signature.resize(sigLength); + + if (!EVP_DigestSign(ctx.getDigestCtx(), &signature[0], &sigLength, message.data(), message.len())) { + throw_openssl("Signature failed"); + } + + signature.resize(sigLength); + } else { jni_borrow message(env, messageBuf, "message"); if (EVP_PKEY_sign(ctx.getKeyCtx(), NULL, &sigLength, message.data(), message.len()) <= 0) { @@ -472,7 +489,6 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignatu signature.resize(sigLength); } - return vecToArray(env, signature); } catch (java_ex& ex) { ex.throw_to_java(pEnv); @@ -508,7 +524,14 @@ JNIEXPORT jboolean JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignature jni_borrow message(env, messageBuf, "message"); jni_borrow signature(env, signatureBuf, "signature"); - int ret = EVP_PKEY_verify(ctx.getKeyCtx(), signature.data(), signature.len(), message.data(), message.len()); + int ret; + int keyType = EVP_PKEY_id(reinterpret_cast(pKey)); + if (keyType == EVP_PKEY_ED25519) { + ret = EVP_DigestVerify( + ctx.getDigestCtx(), signature.data(), signature.len(), message.data(), message.len()); + } else { + ret = EVP_PKEY_verify(ctx.getKeyCtx(), signature.data(), signature.len(), message.data(), message.len()); + } if (likely(ret == 1)) { return true; diff --git a/csrc/test_keyutils.cpp b/csrc/test_keyutils.cpp index ae00b9da..b0e9f74a 100644 --- a/csrc/test_keyutils.cpp +++ b/csrc/test_keyutils.cpp @@ -165,7 +165,7 @@ static const uint8_t validPKCS8ECKey[DER_LENGTH] = { void test_deserialize_valid_key() { - EVP_PKEY* result = der2EvpPrivateKey(validPKCS8ECKey, DER_LENGTH, false, EX_INVALID_KEY); + EVP_PKEY* result = der2EvpPrivateKey(validPKCS8ECKey, DER_LENGTH, EVP_PKEY_EC, false, EX_INVALID_KEY); TEST_ASSERT(result); EVP_PKEY_free(result); } @@ -178,7 +178,7 @@ void test_deserialize_invalid_der() // This makes the private key invalid invalidKey[34] = 0; try { - der2EvpPrivateKey(invalidKey, DER_LENGTH, false, EX_INVALID_KEY); + der2EvpPrivateKey(invalidKey, DER_LENGTH, EVP_PKEY_EC, false, EX_INVALID_KEY); FAIL(); } catch (...) { // Expected @@ -190,7 +190,7 @@ void test_deserialize_empty() // This makes the DER encoding invalid for a PKCS8 key but still the right length uint8_t emptyKey[1] = { 0x00 }; try { - der2EvpPrivateKey(emptyKey, 0, false, EX_INVALID_KEY); + der2EvpPrivateKey(emptyKey, 0, EVP_PKEY_EC, false, EX_INVALID_KEY); FAIL(); } catch (...) { // Expected @@ -205,7 +205,7 @@ void test_deserialize_extra_data() // This sets the der to have 0 elements but still has data and will hit the extra key info invalidKey[0] = 0; try { - der2EvpPrivateKey(invalidKey, DER_LENGTH, false, EX_INVALID_KEY); + der2EvpPrivateKey(invalidKey, DER_LENGTH, EVP_PKEY_EC, false, EX_INVALID_KEY); FAIL(); } catch (...) { // Expected diff --git a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java index 060bc929..9ed657bc 100644 --- a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java +++ b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java @@ -57,6 +57,7 @@ public final class AmazonCorrettoCryptoProvider extends java.security.Provider { private final boolean relyOnCachedSelfTestResults; private final boolean shouldRegisterEcParams; private final boolean shouldRegisterSecureRandom; + private final boolean shouldRegisterEdDSA; private final Utils.NativeContextReleaseStrategy nativeContextReleaseStrategy; private transient SelfTestSuite selfTestSuite = new SelfTestSuite(); @@ -86,6 +87,13 @@ private void buildServiceMap() { addService("KeyFactory", "RSA", "EvpKeyFactory$RSA"); addService("KeyFactory", "EC", "EvpKeyFactory$EC"); + if (shouldRegisterEdDSA) { + addService("KeyFactory", "EdDSA", "EvpKeyFactory$EdDSA"); + addService("KeyFactory", "Ed25519", "EvpKeyFactory$EdDSA"); + addService("KeyPairGenerator", "EdDSA", "EdGen"); + addService("KeyPairGenerator", "Ed25519", "EdGen"); + } + final String hkdfSpi = "HkdfSecretKeyFactorySpi"; addService("SecretKeyFactory", HKDF_WITH_SHA1, hkdfSpi, false); addService("SecretKeyFactory", HKDF_WITH_SHA256, hkdfSpi, false); @@ -187,6 +195,10 @@ private void addSignatures() { addService("Signature", "RSASSA-PSS", "EvpSignature$RSASSA_PSS"); addService("Signature", "NONEwithECDSA", "EvpSignatureRaw$NONEwithECDSA"); + if (shouldRegisterEdDSA) { + addService("Signature", "EdDSA", "EvpSignatureRaw$Ed25519"); + addService("Signature", "Ed25519", "EvpSignatureRaw$Ed25519"); + } } private ACCPService addService( @@ -462,6 +474,9 @@ public AmazonCorrettoCryptoProvider() { this.shouldRegisterSecureRandom = Utils.getBooleanProperty(PROPERTY_REGISTER_SECURE_RANDOM, true); + // Register EdDSA if JDK version >= 15 + this.shouldRegisterEdDSA = Utils.getJavaVersion() >= 15; + this.nativeContextReleaseStrategy = Utils.getNativeContextReleaseStrategyProperty(); Utils.optionsFromProperty(ExtraCheck.class, extraChecks, "extrachecks"); @@ -663,6 +678,7 @@ private void readObjectNoData() { // Provider.getService logic. private transient volatile KeyFactory rsaFactory; private transient volatile KeyFactory ecFactory; + private transient volatile KeyFactory edFactory; KeyFactory getKeyFactory(EvpKeyType keyType) { try { @@ -677,6 +693,11 @@ KeyFactory getKeyFactory(EvpKeyType keyType) { ecFactory = KeyFactory.getInstance(keyType.jceName, this); } return ecFactory; + case Ed25519: + if (edFactory == null) { + edFactory = KeyFactory.getInstance(keyType.jceName, this); + } + return edFactory; default: throw new AssertionError("Unsupported key type"); } diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java new file mode 100644 index 00000000..2fc75d0e --- /dev/null +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -0,0 +1,38 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazon.corretto.crypto.provider; + +import java.security.KeyPair; +import java.security.KeyPairGeneratorSpi; +import java.security.SecureRandom; + +class EdGen extends KeyPairGeneratorSpi { + /** + * Generates a new Ed25519 key and returns a pointer to it. + * + * @param params a native pointer created by {@link #buildEcParams(int)} + * @param checkConsistency Run additional consistency checks on the generated keypair + */ + private static native long generateEvpEdEcKey(); + + private final AmazonCorrettoCryptoProvider provider_; + + EdGen(AmazonCorrettoCryptoProvider provider) { + Loader.checkNativeLibraryAvailability(); + provider_ = provider; + } + + public void initialize(int keysize, SecureRandom random) { + // Has some behavior in Java, but throws error as placeholder for now. + throw new UnsupportedOperationException(); + } + + @Override + public KeyPair generateKeyPair() { + final EvpEdEcPrivateKey privateKey; + final EvpEdEcPublicKey publicKey; + privateKey = new EvpEdEcPrivateKey(generateEvpEdEcKey()); + publicKey = privateKey.getPublicKey(); + return new KeyPair(publicKey, privateKey); + } +} diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java new file mode 100644 index 00000000..4984737b --- /dev/null +++ b/src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java @@ -0,0 +1,11 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazon.corretto.crypto.provider; + +abstract class EvpEdEcKey extends EvpKey { + private static final long serialVersionUID = 1; + + EvpEdEcKey(final InternalKey key, final boolean isPublicKey) { + super(key, EvpKeyType.Ed25519, isPublicKey); + } +} diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java new file mode 100644 index 00000000..2129e794 --- /dev/null +++ b/src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java @@ -0,0 +1,44 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazon.corretto.crypto.provider; + +import java.security.PrivateKey; +import java.util.Optional; + +class EvpEdEcPrivateKey extends EvpEdEcKey implements PrivateKey { + private static final long serialVersionUID = 1; + + private static native byte[] getPrivateKey(long ptr); + + private volatile byte[] privateKey; + + EvpEdEcPrivateKey(final long ptr) { + this(new InternalKey(ptr)); + } + + EvpEdEcPrivateKey(final InternalKey key) { + super(key, false); + } + + public EvpEdEcPublicKey getPublicKey() { + ephemeral = false; + sharedKey = true; + final EvpEdEcPublicKey result = new EvpEdEcPublicKey(internalKey); + result.sharedKey = true; + return result; + } + + public Optional getBytes() { + byte[] bytes = privateKey; + if (bytes == null) { + synchronized (this) { + bytes = privateKey; + if (bytes == null) { + bytes = use(EvpEdEcPrivateKey::getPrivateKey); + privateKey = bytes; + } + } + } + return Optional.ofNullable(bytes); + } +} diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java new file mode 100644 index 00000000..6c5ccecf --- /dev/null +++ b/src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java @@ -0,0 +1,21 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazon.corretto.crypto.provider; + +import java.security.PublicKey; + +class EvpEdEcPublicKey extends EvpEdEcKey implements PublicKey { + private static final long serialVersionUID = 1; + + private static native byte[] getPublicKey(long ptr); + + private volatile byte[] publicKey; + + EvpEdEcPublicKey(final long ptr) { + this(new InternalKey(ptr)); + } + + EvpEdEcPublicKey(final InternalKey key) { + super(key, true); + } +} diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java index ae2413e8..be6cb1d5 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java @@ -302,4 +302,27 @@ protected T engineGetKeySpec(Key key, Class keySpec) return super.engineGetKeySpec(key, keySpec); } } + + static class EdDSA extends EvpKeyFactory { + + EdDSA(AmazonCorrettoCryptoProvider provider) { + super(EvpKeyType.Ed25519, provider); + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { + return super.engineGeneratePrivate(keySpec); + } + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { + return super.engineGeneratePublic(keySpec); + } + + @Override + protected T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + return super.engineGetKeySpec(key, keySpec); + } + } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java index c5fe919a..27b1b0c3 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java @@ -17,7 +17,8 @@ /** Corresponds to native constants in OpenSSL which represent keytypes. */ enum EvpKeyType { RSA("RSA", 6, RSAPublicKey.class, RSAPrivateKey.class), - EC("EC", 408, ECPublicKey.class, ECPrivateKey.class); + EC("EC", 408, ECPublicKey.class, ECPrivateKey.class), + Ed25519("EdDSA", 949, PublicKey.class, PrivateKey.class); final String jceName; final int nativeValue; @@ -55,6 +56,8 @@ PrivateKey buildPrivateKey( return EvpRsaPrivateCrtKey.buildProperKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case EC: return new EvpEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); + case Ed25519: + return new EvpEdEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); } @@ -68,6 +71,8 @@ PublicKey buildPublicKey( return new EvpRsaPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case EC: return new EvpEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); + case Ed25519: + return new EvpEdEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); } diff --git a/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java b/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java index 4d0f715c..ddf08b27 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java +++ b/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java @@ -108,4 +108,10 @@ static final class NONEwithECDSA extends EvpSignatureRaw { super(provider, EvpKeyType.EC, 0); } } + + static final class Ed25519 extends EvpSignatureRaw { + Ed25519(AmazonCorrettoCryptoProvider provider) { + super(provider, EvpKeyType.Ed25519, 0); + } + } } diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java new file mode 100644 index 00000000..9ee52fa2 --- /dev/null +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -0,0 +1,289 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package com.amazon.corretto.crypto.provider.test; + +import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.security.*; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.api.parallel.ResourceAccessMode; +import org.junit.jupiter.api.parallel.ResourceLock; + +@ExtendWith(TestResultLogger.class) +@Execution(ExecutionMode.CONCURRENT) +@ResourceLock(value = TestUtil.RESOURCE_GLOBAL, mode = ResourceAccessMode.READ) +@EnabledForJreRange(min = JRE.JAVA_15) +public class EdDSATest { + + private KeyPairGenerator nativeGen; + private KeyPairGenerator jceGen; + private KeyPairGenerator bcGen; + private static final BouncyCastleProvider BOUNCYCASTLE_PROVIDER = new BouncyCastleProvider(); + + @BeforeEach + public void setup() throws GeneralSecurityException { + nativeGen = KeyPairGenerator.getInstance("Ed25519", NATIVE_PROVIDER); + jceGen = KeyPairGenerator.getInstance("Ed25519", "SunEC"); + bcGen = KeyPairGenerator.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + } + + @AfterEach + public void teardown() { + // It is unclear if JUnit always properly releases references to classes and thus we may have + // memory leaks + // if we do not properly null our references + nativeGen = null; + jceGen = null; + bcGen = null; + } + + @Test + public void uniqueKeyGen() { + final KeyPair kp1 = nativeGen.generateKeyPair(); + final KeyPair kp2 = nativeGen.generateKeyPair(); + + assertTrue(!kp1.equals(kp2)); + } + + @Test + public void keyGenValidation() throws GeneralSecurityException { + // Generate Keys with ACCP & Sign/Verify with SunEC + final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final KeyPair keyPair = nativeGen.generateKeyPair(); + + final PKCS8EncodedKeySpec privateKeyPkcs8 = + new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); + final X509EncodedKeySpec publicKeyX509 = + new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); + + final KeyFactory kf = KeyFactory.getInstance("EdDSA", "SunEC"); + + final PrivateKey privateKey = kf.generatePrivate(privateKeyPkcs8); + final PublicKey publicKey = kf.generatePublic(publicKeyX509); + + final Signature eddsa = Signature.getInstance("Ed25519", "SunEC"); + + eddsa.initSign(privateKey); + eddsa.update(message, 0, message.length); + final byte[] signature = eddsa.sign(); + eddsa.initVerify(publicKey); + eddsa.update(message); + assertTrue(eddsa.verify(signature)); + } + + @Test + public void keyFactoryValidation() throws GeneralSecurityException { + final KeyPair keyPair = jceGen.generateKeyPair(); + + final byte[] privateKeyJCE = keyPair.getPrivate().getEncoded(); + final byte[] publicKeyJCE = keyPair.getPublic().getEncoded(); + + final PKCS8EncodedKeySpec privateKeyPkcs8 = new PKCS8EncodedKeySpec(privateKeyJCE); + final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(publicKeyJCE); + + final KeyFactory kf = KeyFactory.getInstance("Ed25519", NATIVE_PROVIDER); + + final byte[] privateKeyACCP = kf.generatePrivate(privateKeyPkcs8).getEncoded(); + final byte[] publicKeyACCP = kf.generatePublic(publicKeyX509).getEncoded(); + + assertTrue(privateKeyACCP.length == privateKeyJCE.length); + for (int i = 0; i < privateKeyACCP.length; i++) { + assertTrue(privateKeyACCP[i] == privateKeyJCE[i]); + } + + assertTrue(publicKeyACCP.length == publicKeyJCE.length); + for (int i = 0; i < publicKeyACCP.length; i++) { + assertTrue(publicKeyACCP[i] == publicKeyJCE[i]); + } + } + + @Test + public void jceInteropValidation() throws GeneralSecurityException { + // Generate keys with ACCP and use JCE KeyFactory to get equivalent JCE Keys + final KeyPair keyPair = nativeGen.generateKeyPair(); + + final PKCS8EncodedKeySpec privateKeyPkcs8 = + new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); + final X509EncodedKeySpec publicKeyX509 = + new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); + + final KeyFactory kf = KeyFactory.getInstance("Ed25519", "SunEC"); + final PrivateKey privateKey = kf.generatePrivate(privateKeyPkcs8); + final PublicKey publicKey = kf.generatePublic(publicKeyX509); + + // Set up ACCP and JCE Signature Instances + final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + final Signature jceSig = Signature.getInstance("Ed25519", "SunEC"); + + // Sign with ACCP and verify with SunEC + final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + nativeSig.initSign(keyPair.getPrivate()); + nativeSig.update(message, 0, message.length); + byte[] signature = nativeSig.sign(); + jceSig.initVerify(publicKey); + jceSig.update(message); + assertTrue(jceSig.verify(signature), "Native->JCE: Ed25519"); + + // Sign with SunEC and verify with ACCP + jceSig.initSign(privateKey); + jceSig.update(message, 0, message.length); + signature = jceSig.sign(); + nativeSig.initVerify(keyPair.getPublic()); + nativeSig.update(message); + assertTrue(nativeSig.verify(signature), "JCE->Native: Ed25519"); + } + + @Test + public void bcInteropValidation() throws GeneralSecurityException { + // Generate keys with ACCP and use BC KeyFactory to get equivalent JCE Keys + final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + final Signature bcSig = Signature.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + final KeyPair keyPair = nativeGen.generateKeyPair(); + + final PKCS8EncodedKeySpec privateKeyPkcs8 = + new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); + final X509EncodedKeySpec publicKeyX509 = + new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); + + final KeyFactory kf = KeyFactory.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + + final PrivateKey privateKey = kf.generatePrivate(privateKeyPkcs8); + final PublicKey publicKey = kf.generatePublic(publicKeyX509); + + // Sign with ACCP, Verify with BouncyCastle + nativeSig.initSign(keyPair.getPrivate()); + nativeSig.update(message, 0, message.length); + byte[] signature = nativeSig.sign(); + bcSig.initVerify(publicKey); + bcSig.update(message); + assertTrue(bcSig.verify(signature), "Native->BC: Ed25519"); + + bcSig.initSign(privateKey); + bcSig.update(message, 0, message.length); + signature = bcSig.sign(); + nativeSig.initVerify(keyPair.getPublic()); + nativeSig.update(message); + assertTrue(nativeSig.verify(signature), "BC->Native: Ed25519"); + } + + @Test + public void bcKeyValidation() throws GeneralSecurityException { + // Generate keys with ACCP and use BC KeyFactory to get equivalent JCE Keys + final KeyPair kp = nativeGen.generateKeyPair(); + final byte[] pkACCP = kp.getPrivate().getEncoded(); + final byte[] pbkACCP = kp.getPublic().getEncoded(); + + final PKCS8EncodedKeySpec privateKeyPkcs8 = new PKCS8EncodedKeySpec(pkACCP); + final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(pbkACCP); + + final KeyFactory kf = KeyFactory.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + + final byte[] pkBC = kf.generatePrivate(privateKeyPkcs8).getEncoded(); + final byte[] pbkBC = kf.generatePublic(publicKeyX509).getEncoded(); + + // Confirm that ACCP & BC keys are equivalent + assertTrue(pkACCP.length == pkBC.length); + for (int i = 0; i < pkACCP.length; i++) { + assertTrue(pkACCP[i] == pkBC[i]); + } + + assertTrue(pbkACCP.length == pbkBC.length); + for (int i = 0; i < pbkACCP.length; i++) { + assertTrue(pbkACCP[i] == pbkBC[i]); + } + } + + @Test + public void eddsaValidation() throws GeneralSecurityException { + // Generate keys, sign, & verify with ACCP + final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final Signature eddsa = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + final KeyPair keyPair = nativeGen.generateKeyPair(); + + eddsa.initSign(keyPair.getPrivate()); + eddsa.update(message, 0, message.length); + final byte[] signature = eddsa.sign(); + + eddsa.initVerify(keyPair.getPublic()); + eddsa.update(message, 0, message.length); + assertTrue(eddsa.verify(signature)); + } + + @Test + public void mismatchSignature() throws GeneralSecurityException { + final byte[] message1 = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final byte[] message2 = new byte[] {5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; + + final KeyPair kp = nativeGen.generateKeyPair(); + final Signature eddsa = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + + eddsa.initSign(kp.getPrivate()); + eddsa.update(message1, 0, message1.length); + final byte[] signature = eddsa.sign(); + + eddsa.initVerify(kp.getPublic()); + eddsa.update(message2, 0, message2.length); + TestUtil.assertThrows(SignatureException.class, () -> eddsa.verify(signature)); + } + + @Test + public void testInvalidKey() throws GeneralSecurityException { + byte[] invalidKeyBytes = new byte[] {}; + PKCS8EncodedKeySpec invalidPrivateKeySpec = new PKCS8EncodedKeySpec(invalidKeyBytes); + X509EncodedKeySpec invalidPublicKeySpec = new X509EncodedKeySpec(invalidKeyBytes); + + final KeyFactory kf = KeyFactory.getInstance("Ed25519", NATIVE_PROVIDER); + + TestUtil.assertThrows( + InvalidKeySpecException.class, () -> kf.generatePrivate(invalidPrivateKeySpec)); + TestUtil.assertThrows( + InvalidKeySpecException.class, () -> kf.generatePublic(invalidPublicKeySpec)); + } + + @Test + public void testNullInputs() throws GeneralSecurityException { + // Test SunEC behavior + KeyPair keyPair = jceGen.generateKeyPair(); + Signature jceSig = Signature.getInstance("Ed25519", "SunEC"); + // Test with null message + jceSig.initSign(keyPair.getPrivate()); + TestUtil.assertThrows(NullPointerException.class, () -> jceSig.update((byte[]) null)); + // Test with null signature + jceSig.initVerify(keyPair.getPublic()); + assertTrue(!jceSig.verify(null)); + + // Test BouncyCastle behavior + KeyPair keyPair2 = bcGen.generateKeyPair(); + Signature bcSig = Signature.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + // Test with null message + bcSig.initSign(keyPair2.getPrivate()); + TestUtil.assertThrows(NullPointerException.class, () -> bcSig.update((byte[]) null)); + // Test with null signature + bcSig.initVerify(keyPair2.getPublic()); + TestUtil.assertThrows(NullPointerException.class, () -> bcSig.verify(null)); + + // Test ACCP behavior + KeyPair keyPair3 = nativeGen.generateKeyPair(); + Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + // Test with null message + nativeSig.initSign(keyPair3.getPrivate()); + TestUtil.assertThrows(NullPointerException.class, () -> nativeSig.update((byte[]) null)); + // Test with null signature + nativeSig.initVerify(keyPair3.getPublic()); + TestUtil.assertThrows(NullPointerException.class, () -> nativeSig.verify(null)); + } +} diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java index 4c6ccaeb..e96d1891 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java @@ -190,6 +190,7 @@ static long getRawPointer(final Object evpKey) throws Exception { public void keysSerialize(final KeyPair keyPair, final String testName) throws Exception { final KeyFactory kf = KeyFactory.getInstance(keyPair.getPrivate().getAlgorithm(), NATIVE_PROVIDER); + final Key privateKey = kf.translateKey(keyPair.getPrivate()); final Key publicKey = kf.translateKey(keyPair.getPublic()); diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java index 7742983a..19bbe138 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java @@ -659,6 +659,9 @@ public void simpleCorrectnessAllAlgorithms() throws Throwable { if (!service.getType().equals("Signature") || "RSASSA-PSS".equals(algorithm)) { continue; } + if (algorithm.equals("Ed25519") || algorithm.equals("EdDSA")) { + return; + } String bcAlgorithm = algorithm; AlgorithmParameterSpec keyGenSpec = null; String keyGenAlgorithm = null; diff --git a/tst/com/amazon/corretto/crypto/provider/test/KeyReuseThreadStormTest.java b/tst/com/amazon/corretto/crypto/provider/test/KeyReuseThreadStormTest.java index f39375bb..4fbca5af 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/KeyReuseThreadStormTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/KeyReuseThreadStormTest.java @@ -24,6 +24,8 @@ import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -37,12 +39,14 @@ public class KeyReuseThreadStormTest { private static final KeyPairGenerator RSA_KEY_GEN; private static final KeyPairGenerator EC_KEY_GEN; + private static final KeyPairGenerator ED_KEY_GEN; private static final KeyPair PAIR_RSA_1024_OR_DEFAULT; private static final KeyPair PAIR_RSA_2048; private static final KeyPair PAIR_RSA_4096; private static final KeyPair PAIR_EC_P256; private static final KeyPair PAIR_EC_P384; private static final KeyPair PAIR_EC_P521; + private static final KeyPair PAIR_ED25519; static { try { @@ -62,6 +66,11 @@ public class KeyReuseThreadStormTest { PAIR_EC_P384 = EC_KEY_GEN.generateKeyPair(); EC_KEY_GEN.initialize(new ECGenParameterSpec("NIST P-521")); PAIR_EC_P521 = EC_KEY_GEN.generateKeyPair(); + ED_KEY_GEN = + TestUtil.getJavaVersion() >= 15 + ? KeyPairGenerator.getInstance("Ed25519", NATIVE_PROVIDER) + : null; + PAIR_ED25519 = TestUtil.getJavaVersion() >= 15 ? ED_KEY_GEN.generateKeyPair() : null; } catch (final GeneralSecurityException ex) { throw new AssertionError(ex); } @@ -203,6 +212,29 @@ public void ecThreadStorm() throws Throwable { executeThreads(threads); } + @Test + @EnabledForJreRange(min = JRE.JAVA_15) + public void edThreadStorm() throws Throwable { + final byte[] rngSeed = TestUtil.getRandomBytes(20); + System.out.println("RNG Seed: " + Arrays.toString(rngSeed)); + final SecureRandom rng = SecureRandom.getInstance("SHA1PRNG"); + rng.setSeed(rngSeed); + final int iterations = 500; + final int threadCount = 48; + + final List threads = new ArrayList<>(); + for (int x = 0; x < threadCount; x++) { + final List keys = new ArrayList(); + while (keys.size() < 2) { + keys.add(PAIR_ED25519); + } + final TestThread t; + t = new SignatureTestThread("EddsaThread-" + x, rng, iterations, "Ed25519", keys); + threads.add(t); + } + executeThreads(threads); + } + private abstract static class TestThread extends Thread { public volatile Throwable result = null; protected final SecureRandom rnd_; From b7976bbbfb4833a05e1305a8f2b9c51460a40932 Mon Sep 17 00:00:00 2001 From: sp717 Date: Thu, 8 Aug 2024 10:06:58 -0700 Subject: [PATCH 02/14] Change naming from EdEc to Ed & update comments --- csrc/ed_gen.cpp | 6 +++--- csrc/java_evp_keys.cpp | 4 ++-- .../provider/AmazonCorrettoCryptoProvider.java | 3 ++- src/com/amazon/corretto/crypto/provider/EdGen.java | 11 ++++------- .../provider/{EvpEdEcKey.java => EvpEdKey.java} | 4 ++-- .../{EvpEdEcPrivateKey.java => EvpEdPrivateKey.java} | 12 ++++++------ .../{EvpEdEcPublicKey.java => EvpEdPublicKey.java} | 6 +++--- .../amazon/corretto/crypto/provider/EvpKeyType.java | 4 ++-- 8 files changed, 24 insertions(+), 26 deletions(-) rename src/com/amazon/corretto/crypto/provider/{EvpEdEcKey.java => EvpEdKey.java} (70%) rename src/com/amazon/corretto/crypto/provider/{EvpEdEcPrivateKey.java => EvpEdPrivateKey.java} (71%) rename src/com/amazon/corretto/crypto/provider/{EvpEdEcPublicKey.java => EvpEdPublicKey.java} (73%) diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index c8db1cc6..b05c0a08 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -13,7 +13,7 @@ using namespace AmazonCorrettoCryptoProvider; -void generateEdEcKey(raii_env* env, EVP_PKEY_auto& key) +void generateEdKey(raii_env* env, EVP_PKEY_auto& key) { EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr)); CHECK_OPENSSL(ctx.isInitialized()); @@ -21,12 +21,12 @@ void generateEdEcKey(raii_env* env, EVP_PKEY_auto& key) CHECK_OPENSSL(EVP_PKEY_keygen(ctx, key.getAddressOfPtr())); } -JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EdGen_generateEvpEdEcKey(JNIEnv* pEnv, jclass) +JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EdGen_generateEvpEdKey(JNIEnv* pEnv, jclass) { try { raii_env env(pEnv); EVP_PKEY_auto key; - generateEdEcKey(&env, key); + generateEdKey(&env, key); return reinterpret_cast(key.take()); } catch (java_ex& ex) { ex.throw_to_java(pEnv); diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index a518e2e5..ad92a294 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -329,10 +329,10 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEcPriva } /* - * Class: com_amazon_corretto_crypto_provider_EvpEdEcPrivateKey + * Class: com_amazon_corretto_crypto_provider_EvpEdPrivateKey * Method: getPrivateKey */ -JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdEcPrivateKey_getPrivateKey( +JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPrivateKey_getPrivateKey( JNIEnv* pEnv, jclass, jlong keyHandle) { jbyteArray result = NULL; diff --git a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java index 9ed657bc..e9af45d7 100644 --- a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java +++ b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java @@ -474,7 +474,8 @@ public AmazonCorrettoCryptoProvider() { this.shouldRegisterSecureRandom = Utils.getBooleanProperty(PROPERTY_REGISTER_SECURE_RANDOM, true); - // Register EdDSA if JDK version >= 15 + // The Java classes necessary for EdDSA are not included in Java versions < 15, so to compile + // successfully on older versions of Java we can only register EdDSA if JDK version >= 15. this.shouldRegisterEdDSA = Utils.getJavaVersion() >= 15; this.nativeContextReleaseStrategy = Utils.getNativeContextReleaseStrategyProperty(); diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index 2fc75d0e..7178485f 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -9,11 +9,8 @@ class EdGen extends KeyPairGeneratorSpi { /** * Generates a new Ed25519 key and returns a pointer to it. - * - * @param params a native pointer created by {@link #buildEcParams(int)} - * @param checkConsistency Run additional consistency checks on the generated keypair */ - private static native long generateEvpEdEcKey(); + private static native long generateEvpEdKey(); private final AmazonCorrettoCryptoProvider provider_; @@ -29,9 +26,9 @@ public void initialize(int keysize, SecureRandom random) { @Override public KeyPair generateKeyPair() { - final EvpEdEcPrivateKey privateKey; - final EvpEdEcPublicKey publicKey; - privateKey = new EvpEdEcPrivateKey(generateEvpEdEcKey()); + final EvpEdPrivateKey privateKey; + final EvpEdPublicKey publicKey; + privateKey = new EvpEdPrivateKey(generateEvpEdKey()); publicKey = privateKey.getPublicKey(); return new KeyPair(publicKey, privateKey); } diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdKey.java similarity index 70% rename from src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java rename to src/com/amazon/corretto/crypto/provider/EvpEdKey.java index 4984737b..d33bca91 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpEdEcKey.java +++ b/src/com/amazon/corretto/crypto/provider/EvpEdKey.java @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazon.corretto.crypto.provider; -abstract class EvpEdEcKey extends EvpKey { +abstract class EvpEdKey extends EvpKey { private static final long serialVersionUID = 1; - EvpEdEcKey(final InternalKey key, final boolean isPublicKey) { + EvpEdKey(final InternalKey key, final boolean isPublicKey) { super(key, EvpKeyType.Ed25519, isPublicKey); } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdPrivateKey.java similarity index 71% rename from src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java rename to src/com/amazon/corretto/crypto/provider/EvpEdPrivateKey.java index 2129e794..8654c0cf 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpEdEcPrivateKey.java +++ b/src/com/amazon/corretto/crypto/provider/EvpEdPrivateKey.java @@ -5,25 +5,25 @@ import java.security.PrivateKey; import java.util.Optional; -class EvpEdEcPrivateKey extends EvpEdEcKey implements PrivateKey { +class EvpEdPrivateKey extends EvpEdKey implements PrivateKey { private static final long serialVersionUID = 1; private static native byte[] getPrivateKey(long ptr); private volatile byte[] privateKey; - EvpEdEcPrivateKey(final long ptr) { + EvpEdPrivateKey(final long ptr) { this(new InternalKey(ptr)); } - EvpEdEcPrivateKey(final InternalKey key) { + EvpEdPrivateKey(final InternalKey key) { super(key, false); } - public EvpEdEcPublicKey getPublicKey() { + public EvpEdPublicKey getPublicKey() { ephemeral = false; sharedKey = true; - final EvpEdEcPublicKey result = new EvpEdEcPublicKey(internalKey); + final EvpEdPublicKey result = new EvpEdPublicKey(internalKey); result.sharedKey = true; return result; } @@ -34,7 +34,7 @@ public Optional getBytes() { synchronized (this) { bytes = privateKey; if (bytes == null) { - bytes = use(EvpEdEcPrivateKey::getPrivateKey); + bytes = use(EvpEdPrivateKey::getPrivateKey); privateKey = bytes; } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdPublicKey.java similarity index 73% rename from src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java rename to src/com/amazon/corretto/crypto/provider/EvpEdPublicKey.java index 6c5ccecf..e8e5f64f 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpEdEcPublicKey.java +++ b/src/com/amazon/corretto/crypto/provider/EvpEdPublicKey.java @@ -4,18 +4,18 @@ import java.security.PublicKey; -class EvpEdEcPublicKey extends EvpEdEcKey implements PublicKey { +class EvpEdPublicKey extends EvpEdKey implements PublicKey { private static final long serialVersionUID = 1; private static native byte[] getPublicKey(long ptr); private volatile byte[] publicKey; - EvpEdEcPublicKey(final long ptr) { + EvpEdPublicKey(final long ptr) { this(new InternalKey(ptr)); } - EvpEdEcPublicKey(final InternalKey key) { + EvpEdPublicKey(final InternalKey key) { super(key, true); } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java index 27b1b0c3..f5a0b5ec 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java @@ -57,7 +57,7 @@ PrivateKey buildPrivateKey( case EC: return new EvpEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case Ed25519: - return new EvpEdEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); + return new EvpEdPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); } @@ -72,7 +72,7 @@ PublicKey buildPublicKey( case EC: return new EvpEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case Ed25519: - return new EvpEdEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); + return new EvpEdPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); } From 3700f51fc6ca3ac41436c71ff802b1494e7dfd44 Mon Sep 17 00:00:00 2001 From: sp717 Date: Thu, 8 Aug 2024 11:56:37 -0700 Subject: [PATCH 03/14] Update variable naming, comments, and tests --- csrc/java_evp_keys.cpp | 13 ++++---- csrc/keyutils.cpp | 4 +-- csrc/keyutils.h | 2 +- .../crypto/provider/test/EdDSATest.java | 33 ++++++++----------- .../provider/test/EvpKeyFactoryTest.java | 1 - 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index ad92a294..3678ebe9 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -350,14 +350,15 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPriva CHECK_OPENSSL(pkcs8.isInitialized()); // This next line allocates memory - int bufLen = i2d_PKCS8_PRIV_KEY_INFO(pkcs8, &privateKeyBuffer); - CHECK_OPENSSL(bufLen > 0); + int bufInt = i2d_PKCS8_PRIV_KEY_INFO(pkcs8, &privateKeyBuffer); + CHECK_OPENSSL(bufInt > 0); - size_t bufLen2 = (size_t)bufLen; - CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufLen2) > 0); + size_t bufSize = (size_t)bufInt; + CHECK_OPENSSL(bufSize == (size_t)bufInt); + CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) > 0); - result = env->NewByteArray(bufLen2); - env->SetByteArrayRegion(result, 0, bufLen2, privateKeyBuffer); + result = env->NewByteArray(bufSize); + env->SetByteArrayRegion(result, 0, bufSize, privateKeyBuffer); } catch (java_ex& ex) { ex.throw_to_java(pEnv); } diff --git a/csrc/keyutils.cpp b/csrc/keyutils.cpp index 40c9a6f7..12f3b5bb 100644 --- a/csrc/keyutils.cpp +++ b/csrc/keyutils.cpp @@ -13,13 +13,13 @@ namespace AmazonCorrettoCryptoProvider { EVP_PKEY* der2EvpPrivateKey(const unsigned char* der, const int derLen, - const int nativeValue, + const int evpType, bool shouldCheckPrivate, const char* javaExceptionClass) { const unsigned char* der_mutable_ptr = der; // openssl modifies the input pointer - EVP_PKEY* result = d2i_PrivateKey(nativeValue, NULL, &der_mutable_ptr, derLen); + EVP_PKEY* result = d2i_PrivateKey(evpType, NULL, &der_mutable_ptr, derLen); if (der + derLen != der_mutable_ptr) { if (result) { diff --git a/csrc/keyutils.h b/csrc/keyutils.h index e596c737..1d1cdc49 100644 --- a/csrc/keyutils.h +++ b/csrc/keyutils.h @@ -81,7 +81,7 @@ class EvpKeyContext { EVP_PKEY* der2EvpPrivateKey(const unsigned char* der, const int derLen, - const int nativeValue, + const int evpType, const bool checkPrivateKey, const char* javaExceptionClass); EVP_PKEY* der2EvpPublicKey(const unsigned char* der, const int derLen, const char* javaExceptionClass); diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 9ee52fa2..7490154f 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -5,6 +5,7 @@ import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; import java.security.*; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; @@ -55,7 +56,14 @@ public void uniqueKeyGen() { final KeyPair kp1 = nativeGen.generateKeyPair(); final KeyPair kp2 = nativeGen.generateKeyPair(); - assertTrue(!kp1.equals(kp2)); + final byte [] pk1 = kp1.getPrivate().getEncoded(); + final byte [] pk2 = kp2.getPrivate().getEncoded(); + + final byte [] pbk1 = kp1.getPublic().getEncoded(); + final byte [] pbk2 = kp2.getPublic().getEncoded(); + + assertTrue(!Arrays.equals(pk1, pk2)); + assertTrue(!Arrays.equals(pbk1, pbk2)); } @Test @@ -99,15 +107,9 @@ public void keyFactoryValidation() throws GeneralSecurityException { final byte[] privateKeyACCP = kf.generatePrivate(privateKeyPkcs8).getEncoded(); final byte[] publicKeyACCP = kf.generatePublic(publicKeyX509).getEncoded(); - assertTrue(privateKeyACCP.length == privateKeyJCE.length); - for (int i = 0; i < privateKeyACCP.length; i++) { - assertTrue(privateKeyACCP[i] == privateKeyJCE[i]); - } - - assertTrue(publicKeyACCP.length == publicKeyJCE.length); - for (int i = 0; i < publicKeyACCP.length; i++) { - assertTrue(publicKeyACCP[i] == publicKeyJCE[i]); - } + // Confirm that ACCP & SunEC keys are equivalent + assertTrue(Arrays.equals(privateKeyACCP, privateKeyJCE)); + assertTrue(Arrays.equals(publicKeyACCP, publicKeyJCE)); } @Test @@ -196,15 +198,8 @@ public void bcKeyValidation() throws GeneralSecurityException { final byte[] pbkBC = kf.generatePublic(publicKeyX509).getEncoded(); // Confirm that ACCP & BC keys are equivalent - assertTrue(pkACCP.length == pkBC.length); - for (int i = 0; i < pkACCP.length; i++) { - assertTrue(pkACCP[i] == pkBC[i]); - } - - assertTrue(pbkACCP.length == pbkBC.length); - for (int i = 0; i < pbkACCP.length; i++) { - assertTrue(pbkACCP[i] == pbkBC[i]); - } + assertTrue(Arrays.equals(pkACCP, pkBC)); + assertTrue(Arrays.equals(pbkACCP, pbkBC)); } @Test diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java index e96d1891..4c6ccaeb 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyFactoryTest.java @@ -190,7 +190,6 @@ static long getRawPointer(final Object evpKey) throws Exception { public void keysSerialize(final KeyPair keyPair, final String testName) throws Exception { final KeyFactory kf = KeyFactory.getInstance(keyPair.getPrivate().getAlgorithm(), NATIVE_PROVIDER); - final Key privateKey = kf.translateKey(keyPair.getPrivate()); final Key publicKey = kf.translateKey(keyPair.getPublic()); From 22a0ac36f1f3ae3faa81ad2ce681e8cb61b42064 Mon Sep 17 00:00:00 2001 From: sp717 Date: Thu, 8 Aug 2024 13:55:44 -0700 Subject: [PATCH 04/14] Update formatting and variable naming --- csrc/java_evp_keys.cpp | 11 +++++------ src/com/amazon/corretto/crypto/provider/EdGen.java | 4 +--- .../corretto/crypto/provider/EvpKeyFactory.java | 8 ++++---- .../corretto/crypto/provider/test/EdDSATest.java | 10 +++++----- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index 3678ebe9..583132b6 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -93,7 +93,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpKey_enc * Signature: ([BI)J */ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_pkcs82Evp( - JNIEnv* pEnv, jclass, jbyteArray pkcs8der, jint nativeValue, jboolean shouldCheckPrivate) + JNIEnv* pEnv, jclass, jbyteArray pkcs8der, jint evpType, jboolean shouldCheckPrivate) { try { raii_env env(pEnv); @@ -104,8 +104,8 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_p { jni_borrow borrow = jni_borrow(env, pkcs8Buff, "pkcs8Buff"); - result.set(der2EvpPrivateKey(borrow, derLen, nativeValue, shouldCheckPrivate, EX_INVALID_KEY_SPEC)); - if (EVP_PKEY_base_id(result) != nativeValue) { + result.set(der2EvpPrivateKey(borrow, derLen, evpType, shouldCheckPrivate, EX_INVALID_KEY_SPEC)); + if (EVP_PKEY_base_id(result) != evpType) { throw_java_ex(EX_INVALID_KEY_SPEC, "Incorrect key type"); } } @@ -122,7 +122,7 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_p * Signature: ([BI)J */ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_x5092Evp( - JNIEnv* pEnv, jclass, jbyteArray x509der, jint nativeValue) + JNIEnv* pEnv, jclass, jbyteArray x509der, jint evpType) { try { raii_env env(pEnv); @@ -134,7 +134,7 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EvpKeyFactory_x { jni_borrow borrow = jni_borrow(env, x509Buff, "x509Buff"); result.set(der2EvpPublicKey(borrow, derLen, EX_INVALID_KEY_SPEC)); - if (EVP_PKEY_base_id(result) != nativeValue) { + if (EVP_PKEY_base_id(result) != evpType) { throw_java_ex(EX_INVALID_KEY_SPEC, "Incorrect key type"); } } @@ -354,7 +354,6 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPriva CHECK_OPENSSL(bufInt > 0); size_t bufSize = (size_t)bufInt; - CHECK_OPENSSL(bufSize == (size_t)bufInt); CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) > 0); result = env->NewByteArray(bufSize); diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index 7178485f..d092b0ac 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -7,9 +7,7 @@ import java.security.SecureRandom; class EdGen extends KeyPairGeneratorSpi { - /** - * Generates a new Ed25519 key and returns a pointer to it. - */ + /** Generates a new Ed25519 key and returns a pointer to it. */ private static native long generateEvpEdKey(); private final AmazonCorrettoCryptoProvider provider_; diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java index be6cb1d5..4f5354f2 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java @@ -32,10 +32,10 @@ abstract class EvpKeyFactory extends KeyFactorySpi { private final EvpKeyType type; private final AmazonCorrettoCryptoProvider provider; - private static native long pkcs82Evp(byte[] der, int nativeValue, boolean checkPrivate) + private static native long pkcs82Evp(byte[] der, int evpType, boolean checkPrivate) throws InvalidKeySpecException; - private static native long x5092Evp(byte[] der, int nativeValue) throws InvalidKeySpecException; + private static native long x5092Evp(byte[] der, int evpType) throws InvalidKeySpecException; private static native long rsa2Evp( byte[] modulus, @@ -65,8 +65,8 @@ protected boolean shouldCheckPrivateKey() { return provider.hasExtraCheck(ExtraCheck.PRIVATE_KEY_CONSISTENCY); } - protected long maybeCheckPkcs82Evp(byte[] der, int nativeValue) throws InvalidKeySpecException { - return pkcs82Evp(der, nativeValue, shouldCheckPrivateKey()); + protected long maybeCheckPkcs82Evp(byte[] der, int evpType) throws InvalidKeySpecException { + return pkcs82Evp(der, evpType, shouldCheckPrivateKey()); } @Override diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 7490154f..02bd0a8a 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -5,12 +5,12 @@ import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.Arrays; import java.security.*; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -56,11 +56,11 @@ public void uniqueKeyGen() { final KeyPair kp1 = nativeGen.generateKeyPair(); final KeyPair kp2 = nativeGen.generateKeyPair(); - final byte [] pk1 = kp1.getPrivate().getEncoded(); - final byte [] pk2 = kp2.getPrivate().getEncoded(); + final byte[] pk1 = kp1.getPrivate().getEncoded(); + final byte[] pk2 = kp2.getPrivate().getEncoded(); - final byte [] pbk1 = kp1.getPublic().getEncoded(); - final byte [] pbk2 = kp2.getPublic().getEncoded(); + final byte[] pbk1 = kp1.getPublic().getEncoded(); + final byte[] pbk2 = kp2.getPublic().getEncoded(); assertTrue(!Arrays.equals(pk1, pk2)); assertTrue(!Arrays.equals(pbk1, pbk2)); From b745120f48445a8041bf226c19ca53e793d19a7a Mon Sep 17 00:00:00 2001 From: sp717 Date: Thu, 8 Aug 2024 15:33:10 -0700 Subject: [PATCH 05/14] Changed Ed25519 naming to EdDSA and updated cpp include --- csrc/ed_gen.cpp | 9 --------- .../crypto/provider/AmazonCorrettoCryptoProvider.java | 2 +- src/com/amazon/corretto/crypto/provider/EvpEdKey.java | 2 +- .../amazon/corretto/crypto/provider/EvpKeyFactory.java | 2 +- src/com/amazon/corretto/crypto/provider/EvpKeyType.java | 6 +++--- .../amazon/corretto/crypto/provider/EvpSignatureRaw.java | 2 +- 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index b05c0a08..f50ac382 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -1,15 +1,6 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #include "auto_free.h" -#include "bn.h" -#include "buffer.h" -#include "env.h" -#include "generated-headers.h" -#include "keyutils.h" -#include "util.h" -#include -#include -#include using namespace AmazonCorrettoCryptoProvider; diff --git a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java index e9af45d7..cc7a4091 100644 --- a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java +++ b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java @@ -694,7 +694,7 @@ KeyFactory getKeyFactory(EvpKeyType keyType) { ecFactory = KeyFactory.getInstance(keyType.jceName, this); } return ecFactory; - case Ed25519: + case EdDSA: if (edFactory == null) { edFactory = KeyFactory.getInstance(keyType.jceName, this); } diff --git a/src/com/amazon/corretto/crypto/provider/EvpEdKey.java b/src/com/amazon/corretto/crypto/provider/EvpEdKey.java index d33bca91..cd1b35c6 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpEdKey.java +++ b/src/com/amazon/corretto/crypto/provider/EvpEdKey.java @@ -6,6 +6,6 @@ abstract class EvpEdKey extends EvpKey { private static final long serialVersionUID = 1; EvpEdKey(final InternalKey key, final boolean isPublicKey) { - super(key, EvpKeyType.Ed25519, isPublicKey); + super(key, EvpKeyType.EdDSA, isPublicKey); } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java index 4f5354f2..738040a9 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java @@ -306,7 +306,7 @@ protected T engineGetKeySpec(Key key, Class keySpec) static class EdDSA extends EvpKeyFactory { EdDSA(AmazonCorrettoCryptoProvider provider) { - super(EvpKeyType.Ed25519, provider); + super(EvpKeyType.EdDSA, provider); } @Override diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java index f5a0b5ec..87ceaff8 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyType.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyType.java @@ -18,7 +18,7 @@ enum EvpKeyType { RSA("RSA", 6, RSAPublicKey.class, RSAPrivateKey.class), EC("EC", 408, ECPublicKey.class, ECPrivateKey.class), - Ed25519("EdDSA", 949, PublicKey.class, PrivateKey.class); + EdDSA("EdDSA", 949, PublicKey.class, PrivateKey.class); final String jceName; final int nativeValue; @@ -56,7 +56,7 @@ PrivateKey buildPrivateKey( return EvpRsaPrivateCrtKey.buildProperKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case EC: return new EvpEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); - case Ed25519: + case EdDSA: return new EvpEdPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); @@ -71,7 +71,7 @@ PublicKey buildPublicKey( return new EvpRsaPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); case EC: return new EvpEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); - case Ed25519: + case EdDSA: return new EvpEdPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue)); default: throw new AssertionError("Unsupported key type"); diff --git a/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java b/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java index ddf08b27..a9d352ee 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java +++ b/src/com/amazon/corretto/crypto/provider/EvpSignatureRaw.java @@ -111,7 +111,7 @@ static final class NONEwithECDSA extends EvpSignatureRaw { static final class Ed25519 extends EvpSignatureRaw { Ed25519(AmazonCorrettoCryptoProvider provider) { - super(provider, EvpKeyType.Ed25519, 0); + super(provider, EvpKeyType.EdDSA, 0); } } } From c75ff86b1518eb521e05a86fefb7778ecb059303 Mon Sep 17 00:00:00 2001 From: sp717 Date: Tue, 13 Aug 2024 13:55:50 -0700 Subject: [PATCH 06/14] Update error handling, cpp includes, kpg initialize, and unit tests --- csrc/ed_gen.cpp | 3 ++ csrc/sign.cpp | 3 +- .../corretto/crypto/provider/EdGen.java | 7 ++- .../crypto/provider/test/EdDSATest.java | 49 ++++++++++++------- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index f50ac382..ef7f10ec 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -1,6 +1,9 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +#include #include "auto_free.h" +#include "env.h" +#include "generated-headers.h" using namespace AmazonCorrettoCryptoProvider; diff --git a/csrc/sign.cpp b/csrc/sign.cpp index 8be32193..bb456151 100644 --- a/csrc/sign.cpp +++ b/csrc/sign.cpp @@ -541,7 +541,8 @@ JNIEXPORT jboolean JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignature // Mismatched signatures are not an error case, so return false // instead of throwing per JCA convention. if (ECDSA_R_MISMATCHED_SIGNATURE == (errorCode & ECDSA_R_MISMATCHED_SIGNATURE) - || RSA_R_MISMATCHED_SIGNATURE == (errorCode & RSA_R_MISMATCHED_SIGNATURE)) { + || RSA_R_MISMATCHED_SIGNATURE == (errorCode & RSA_R_MISMATCHED_SIGNATURE) + || EVP_R_INVALID_SIGNATURE == (errorCode & EVP_R_INVALID_SIGNATURE)) { return false; } diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index d092b0ac..ba84c99e 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -5,6 +5,7 @@ import java.security.KeyPair; import java.security.KeyPairGeneratorSpi; import java.security.SecureRandom; +import java.security.InvalidParameterException; class EdGen extends KeyPairGeneratorSpi { /** Generates a new Ed25519 key and returns a pointer to it. */ @@ -18,8 +19,10 @@ class EdGen extends KeyPairGeneratorSpi { } public void initialize(int keysize, SecureRandom random) { - // Has some behavior in Java, but throws error as placeholder for now. - throw new UnsupportedOperationException(); + if (keysize != 255) { + throw new InvalidParameterException("Params must be Ed25519."); + } + return; } @Override diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 02bd0a8a..2eeaf330 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -134,18 +134,20 @@ public void jceInteropValidation() throws GeneralSecurityException { final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; nativeSig.initSign(keyPair.getPrivate()); nativeSig.update(message, 0, message.length); - byte[] signature = nativeSig.sign(); + final byte[] signatureACCP = nativeSig.sign(); jceSig.initVerify(publicKey); jceSig.update(message); - assertTrue(jceSig.verify(signature), "Native->JCE: Ed25519"); + assertTrue(jceSig.verify(signatureACCP), "Native->JCE: Ed25519"); // Sign with SunEC and verify with ACCP jceSig.initSign(privateKey); jceSig.update(message, 0, message.length); - signature = jceSig.sign(); + final byte[] signatureJCE = jceSig.sign(); nativeSig.initVerify(keyPair.getPublic()); nativeSig.update(message); - assertTrue(nativeSig.verify(signature), "JCE->Native: Ed25519"); + assertTrue(nativeSig.verify(signatureJCE), "JCE->Native: Ed25519"); + + assertTrue(Arrays.equals(signatureJCE, signatureACCP)); } @Test @@ -169,17 +171,20 @@ public void bcInteropValidation() throws GeneralSecurityException { // Sign with ACCP, Verify with BouncyCastle nativeSig.initSign(keyPair.getPrivate()); nativeSig.update(message, 0, message.length); - byte[] signature = nativeSig.sign(); + final byte[] signatureACCP = nativeSig.sign(); bcSig.initVerify(publicKey); bcSig.update(message); - assertTrue(bcSig.verify(signature), "Native->BC: Ed25519"); + assertTrue(bcSig.verify(signatureACCP), "Native->BC: Ed25519"); + // Sign with BouncyCastle, Verify with ACCP bcSig.initSign(privateKey); bcSig.update(message, 0, message.length); - signature = bcSig.sign(); + final byte[] signatureBC = bcSig.sign(); nativeSig.initVerify(keyPair.getPublic()); nativeSig.update(message); - assertTrue(nativeSig.verify(signature), "BC->Native: Ed25519"); + assertTrue(nativeSig.verify(signatureBC), "BC->Native: Ed25519"); + + assertTrue(Arrays.equals(signatureBC, signatureACCP)); } @Test @@ -224,15 +229,25 @@ public void mismatchSignature() throws GeneralSecurityException { final byte[] message2 = new byte[] {5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; final KeyPair kp = nativeGen.generateKeyPair(); - final Signature eddsa = Signature.getInstance("Ed25519", NATIVE_PROVIDER); - eddsa.initSign(kp.getPrivate()); - eddsa.update(message1, 0, message1.length); - final byte[] signature = eddsa.sign(); + final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(kp.getPublic().getEncoded()); + final KeyFactory kf = KeyFactory.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); + final PublicKey pbkJCE = kf.generatePublic(publicKeyX509); + + final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); + final Signature jceSig = Signature.getInstance("Ed25519", "SunEC"); + + nativeSig.initSign(kp.getPrivate()); + nativeSig.update(message1, 0, message1.length); + final byte[] signature = nativeSig.sign(); + + nativeSig.initVerify(kp.getPublic()); + nativeSig.update(message2, 0, message2.length); + assertTrue(!nativeSig.verify(signature)); - eddsa.initVerify(kp.getPublic()); - eddsa.update(message2, 0, message2.length); - TestUtil.assertThrows(SignatureException.class, () -> eddsa.verify(signature)); + jceSig.initVerify(pbkJCE); + jceSig.update(message2, 0, message2.length); + assertTrue(!jceSig.verify(signature)); } @Test @@ -244,9 +259,9 @@ public void testInvalidKey() throws GeneralSecurityException { final KeyFactory kf = KeyFactory.getInstance("Ed25519", NATIVE_PROVIDER); TestUtil.assertThrows( - InvalidKeySpecException.class, () -> kf.generatePrivate(invalidPrivateKeySpec)); + InvalidKeySpecException.class, () -> kf.generatePrivate(invalidPrivateKeySpec)); TestUtil.assertThrows( - InvalidKeySpecException.class, () -> kf.generatePublic(invalidPublicKeySpec)); + InvalidKeySpecException.class, () -> kf.generatePublic(invalidPublicKeySpec)); } @Test From 4a52781ba8cbf576a719493c8fb0c841db02fc41 Mon Sep 17 00:00:00 2001 From: sp717 Date: Tue, 13 Aug 2024 13:57:07 -0700 Subject: [PATCH 07/14] Apply spotless formatting --- src/com/amazon/corretto/crypto/provider/EdGen.java | 2 +- tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index ba84c99e..38915e55 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazon.corretto.crypto.provider; +import java.security.InvalidParameterException; import java.security.KeyPair; import java.security.KeyPairGeneratorSpi; import java.security.SecureRandom; -import java.security.InvalidParameterException; class EdGen extends KeyPairGeneratorSpi { /** Generates a new Ed25519 key and returns a pointer to it. */ diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 2eeaf330..12491fcb 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.security.*; -import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @@ -259,9 +258,9 @@ public void testInvalidKey() throws GeneralSecurityException { final KeyFactory kf = KeyFactory.getInstance("Ed25519", NATIVE_PROVIDER); TestUtil.assertThrows( - InvalidKeySpecException.class, () -> kf.generatePrivate(invalidPrivateKeySpec)); + InvalidKeySpecException.class, () -> kf.generatePrivate(invalidPrivateKeySpec)); TestUtil.assertThrows( - InvalidKeySpecException.class, () -> kf.generatePublic(invalidPublicKeySpec)); + InvalidKeySpecException.class, () -> kf.generatePublic(invalidPublicKeySpec)); } @Test From d4d05513e6653f2e8fbf3db84dfaa5f38f22a0aa Mon Sep 17 00:00:00 2001 From: sp717 Date: Thu, 15 Aug 2024 16:28:16 -0700 Subject: [PATCH 08/14] Update README, cpp includes, getPrivateKey impl --- README.md | 6 +++--- csrc/ed_gen.cpp | 2 +- csrc/java_evp_keys.cpp | 15 ++++----------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 61363b0a..483f527a 100644 --- a/README.md +++ b/README.md @@ -64,12 +64,12 @@ Signature algorithms: * SHA512withECDSA * SHA512withECDSAinP1363Format * RSASSA-PSS -* ED25519 +* ED25519 (JDK 15+) KeyPairGenerator: * EC * RSA -* ED25519 +* ED25519 (JDK 15+) KeyGenerator: * AES @@ -94,7 +94,7 @@ SecureRandom: KeyFactory: * EC * RSA -* ED25519 +* ED25519 (JDK 15+) AlgorithmParameters: * EC. Please refer to [system properties](https://github.com/corretto/amazon-corretto-crypto-provider#other-system-properties) for more information. diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index ef7f10ec..0aaf6887 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -1,9 +1,9 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#include #include "auto_free.h" #include "env.h" #include "generated-headers.h" +#include using namespace AmazonCorrettoCryptoProvider; diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index 583132b6..e39ee748 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -342,19 +342,12 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPriva EVP_PKEY* key = reinterpret_cast(keyHandle); - EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new(key, nullptr)); - CHECK_OPENSSL(ctx.isInitialized()); + size_t bufSize; + CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, NULL, &bufSize) == 1); OPENSSL_buffer_auto privateKeyBuffer; - PKCS8_PRIV_KEY_INFO_auto pkcs8 = PKCS8_PRIV_KEY_INFO_auto::from(EVP_PKEY2PKCS8(key)); - CHECK_OPENSSL(pkcs8.isInitialized()); - - // This next line allocates memory - int bufInt = i2d_PKCS8_PRIV_KEY_INFO(pkcs8, &privateKeyBuffer); - CHECK_OPENSSL(bufInt > 0); - - size_t bufSize = (size_t)bufInt; - CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) > 0); + privateKeyBuffer.buf = (uint8_t*)OPENSSL_malloc(bufSize); + CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) == 1); result = env->NewByteArray(bufSize); env->SetByteArrayRegion(result, 0, bufSize, privateKeyBuffer); From f44aa9fdde58942743299d9f85c531d9dd6d4b4f Mon Sep 17 00:00:00 2001 From: sp717 Date: Mon, 19 Aug 2024 10:09:57 -0700 Subject: [PATCH 09/14] Revert kpg initialize behavior --- csrc/ed_gen.cpp | 2 +- src/com/amazon/corretto/crypto/provider/EdGen.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index 0aaf6887..6208e859 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -11,7 +11,7 @@ void generateEdKey(raii_env* env, EVP_PKEY_auto& key) { EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr)); CHECK_OPENSSL(ctx.isInitialized()); - CHECK_OPENSSL(EVP_PKEY_keygen_init(ctx) > 0); + CHECK_OPENSSL(EVP_PKEY_keygen_init(ctx) == 1); CHECK_OPENSSL(EVP_PKEY_keygen(ctx, key.getAddressOfPtr())); } diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index 38915e55..68f4510a 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazon.corretto.crypto.provider; -import java.security.InvalidParameterException; import java.security.KeyPair; import java.security.KeyPairGeneratorSpi; import java.security.SecureRandom; @@ -19,10 +18,7 @@ class EdGen extends KeyPairGeneratorSpi { } public void initialize(int keysize, SecureRandom random) { - if (keysize != 255) { - throw new InvalidParameterException("Params must be Ed25519."); - } - return; + throw new UnsupportedOperationException(); } @Override From 52c1758dc462f553c6793f7b7f97bbb0d33016e8 Mon Sep 17 00:00:00 2001 From: sp717 Date: Tue, 20 Aug 2024 11:02:51 -0700 Subject: [PATCH 10/14] Update kpg behavior and relevant tests --- .../corretto/crypto/provider/EdGen.java | 22 +++++++++++- .../crypto/provider/test/EdDSATest.java | 35 +++++-------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index 68f4510a..ad9c865e 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -2,19 +2,31 @@ // SPDX-License-Identifier: Apache-2.0 package com.amazon.corretto.crypto.provider; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGeneratorSpi; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.SecureRandom; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; class EdGen extends KeyPairGeneratorSpi { /** Generates a new Ed25519 key and returns a pointer to it. */ private static native long generateEvpEdKey(); private final AmazonCorrettoCryptoProvider provider_; + private final KeyFactory kf; EdGen(AmazonCorrettoCryptoProvider provider) { Loader.checkNativeLibraryAvailability(); provider_ = provider; + try { + kf = KeyFactory.getInstance("EdDSA", "SunEC"); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Error setting up KeyPairGenerator", e); + } } public void initialize(int keysize, SecureRandom random) { @@ -27,6 +39,14 @@ public KeyPair generateKeyPair() { final EvpEdPublicKey publicKey; privateKey = new EvpEdPrivateKey(generateEvpEdKey()); publicKey = privateKey.getPublicKey(); - return new KeyPair(publicKey, privateKey); + try { + final PKCS8EncodedKeySpec privateKeyPkcs8 = new PKCS8EncodedKeySpec(privateKey.getEncoded()); + final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(publicKey.getEncoded()); + final PrivateKey jcePrivateKey = kf.generatePrivate(privateKeyPkcs8); + final PublicKey jcePublicKey = kf.generatePublic(publicKeyX509); + return new KeyPair(jcePublicKey, jcePrivateKey); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Error generating key pair", e); + } } } diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 12491fcb..54c2e883 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -116,14 +116,8 @@ public void jceInteropValidation() throws GeneralSecurityException { // Generate keys with ACCP and use JCE KeyFactory to get equivalent JCE Keys final KeyPair keyPair = nativeGen.generateKeyPair(); - final PKCS8EncodedKeySpec privateKeyPkcs8 = - new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); - final X509EncodedKeySpec publicKeyX509 = - new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); - - final KeyFactory kf = KeyFactory.getInstance("Ed25519", "SunEC"); - final PrivateKey privateKey = kf.generatePrivate(privateKeyPkcs8); - final PublicKey publicKey = kf.generatePublic(publicKeyX509); + final PrivateKey privateKey = keyPair.getPrivate(); + final PublicKey publicKey = keyPair.getPublic(); // Set up ACCP and JCE Signature Instances final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); @@ -131,7 +125,7 @@ public void jceInteropValidation() throws GeneralSecurityException { // Sign with ACCP and verify with SunEC final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - nativeSig.initSign(keyPair.getPrivate()); + nativeSig.initSign(privateKey); nativeSig.update(message, 0, message.length); final byte[] signatureACCP = nativeSig.sign(); jceSig.initVerify(publicKey); @@ -142,7 +136,7 @@ public void jceInteropValidation() throws GeneralSecurityException { jceSig.initSign(privateKey); jceSig.update(message, 0, message.length); final byte[] signatureJCE = jceSig.sign(); - nativeSig.initVerify(keyPair.getPublic()); + nativeSig.initVerify(publicKey); nativeSig.update(message); assertTrue(nativeSig.verify(signatureJCE), "JCE->Native: Ed25519"); @@ -157,18 +151,11 @@ public void bcInteropValidation() throws GeneralSecurityException { final Signature bcSig = Signature.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); final KeyPair keyPair = nativeGen.generateKeyPair(); - final PKCS8EncodedKeySpec privateKeyPkcs8 = - new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); - final X509EncodedKeySpec publicKeyX509 = - new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); - - final KeyFactory kf = KeyFactory.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); - - final PrivateKey privateKey = kf.generatePrivate(privateKeyPkcs8); - final PublicKey publicKey = kf.generatePublic(publicKeyX509); + final PrivateKey privateKey = keyPair.getPrivate(); + final PublicKey publicKey = keyPair.getPublic(); // Sign with ACCP, Verify with BouncyCastle - nativeSig.initSign(keyPair.getPrivate()); + nativeSig.initSign(privateKey); nativeSig.update(message, 0, message.length); final byte[] signatureACCP = nativeSig.sign(); bcSig.initVerify(publicKey); @@ -179,7 +166,7 @@ public void bcInteropValidation() throws GeneralSecurityException { bcSig.initSign(privateKey); bcSig.update(message, 0, message.length); final byte[] signatureBC = bcSig.sign(); - nativeSig.initVerify(keyPair.getPublic()); + nativeSig.initVerify(publicKey); nativeSig.update(message); assertTrue(nativeSig.verify(signatureBC), "BC->Native: Ed25519"); @@ -229,10 +216,6 @@ public void mismatchSignature() throws GeneralSecurityException { final KeyPair kp = nativeGen.generateKeyPair(); - final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(kp.getPublic().getEncoded()); - final KeyFactory kf = KeyFactory.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); - final PublicKey pbkJCE = kf.generatePublic(publicKeyX509); - final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); final Signature jceSig = Signature.getInstance("Ed25519", "SunEC"); @@ -244,7 +227,7 @@ public void mismatchSignature() throws GeneralSecurityException { nativeSig.update(message2, 0, message2.length); assertTrue(!nativeSig.verify(signature)); - jceSig.initVerify(pbkJCE); + jceSig.initVerify(kp.getPublic()); jceSig.update(message2, 0, message2.length); assertTrue(!jceSig.verify(signature)); } From ec9dfad5facb1e0ce04fe32d065362b028b9b8d4 Mon Sep 17 00:00:00 2001 From: sp717 Date: Fri, 23 Aug 2024 11:32:07 -0700 Subject: [PATCH 11/14] Update test cases --- .../crypto/provider/test/EdDSATest.java | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 54c2e883..84f4b18f 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -3,6 +3,8 @@ package com.amazon.corretto.crypto.provider.test; import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.security.*; @@ -10,6 +12,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; +import java.util.Random; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -61,8 +64,8 @@ public void uniqueKeyGen() { final byte[] pbk1 = kp1.getPublic().getEncoded(); final byte[] pbk2 = kp2.getPublic().getEncoded(); - assertTrue(!Arrays.equals(pk1, pk2)); - assertTrue(!Arrays.equals(pbk1, pbk2)); + assertFalse(Arrays.equals(pk1, pk2)); + assertFalse(Arrays.equals(pbk1, pbk2)); } @Test @@ -107,8 +110,8 @@ public void keyFactoryValidation() throws GeneralSecurityException { final byte[] publicKeyACCP = kf.generatePublic(publicKeyX509).getEncoded(); // Confirm that ACCP & SunEC keys are equivalent - assertTrue(Arrays.equals(privateKeyACCP, privateKeyJCE)); - assertTrue(Arrays.equals(publicKeyACCP, publicKeyJCE)); + assertArrayEquals(privateKeyACCP, privateKeyJCE); + assertArrayEquals(publicKeyACCP, publicKeyJCE); } @Test @@ -122,55 +125,69 @@ public void jceInteropValidation() throws GeneralSecurityException { // Set up ACCP and JCE Signature Instances final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); final Signature jceSig = Signature.getInstance("Ed25519", "SunEC"); - - // Sign with ACCP and verify with SunEC - final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - nativeSig.initSign(privateKey); - nativeSig.update(message, 0, message.length); - final byte[] signatureACCP = nativeSig.sign(); - jceSig.initVerify(publicKey); - jceSig.update(message); - assertTrue(jceSig.verify(signatureACCP), "Native->JCE: Ed25519"); - - // Sign with SunEC and verify with ACCP - jceSig.initSign(privateKey); - jceSig.update(message, 0, message.length); - final byte[] signatureJCE = jceSig.sign(); - nativeSig.initVerify(publicKey); - nativeSig.update(message); - assertTrue(nativeSig.verify(signatureJCE), "JCE->Native: Ed25519"); - - assertTrue(Arrays.equals(signatureJCE, signatureACCP)); + byte[] message, signatureACCP, signatureJCE; + Random random = new Random(); + + for (int messageLength = 1; messageLength <= 1024; messageLength++) { + message = new byte[messageLength]; + random.nextBytes(message); + // Sign with ACCP and verify with SunEC + nativeSig.initSign(privateKey); + nativeSig.update(message, 0, message.length); + signatureACCP = nativeSig.sign(); + jceSig.initVerify(publicKey); + jceSig.update(message); + assertTrue( + jceSig.verify(signatureACCP), + "Native->JCE: Ed25519 (message length: " + messageLength + ")"); + + // Sign with SunEC and verify with ACCP + jceSig.initSign(privateKey); + jceSig.update(message, 0, message.length); + signatureJCE = jceSig.sign(); + nativeSig.initVerify(publicKey); + nativeSig.update(message); + assertTrue( + nativeSig.verify(signatureJCE), + "JCE->Native: Ed25519 (message length: " + messageLength + ")"); + + assertArrayEquals(signatureJCE, signatureACCP); + } } @Test public void bcInteropValidation() throws GeneralSecurityException { // Generate keys with ACCP and use BC KeyFactory to get equivalent JCE Keys - final byte[] message = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); final Signature bcSig = Signature.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); final KeyPair keyPair = nativeGen.generateKeyPair(); final PrivateKey privateKey = keyPair.getPrivate(); final PublicKey publicKey = keyPair.getPublic(); - - // Sign with ACCP, Verify with BouncyCastle - nativeSig.initSign(privateKey); - nativeSig.update(message, 0, message.length); - final byte[] signatureACCP = nativeSig.sign(); - bcSig.initVerify(publicKey); - bcSig.update(message); - assertTrue(bcSig.verify(signatureACCP), "Native->BC: Ed25519"); - - // Sign with BouncyCastle, Verify with ACCP - bcSig.initSign(privateKey); - bcSig.update(message, 0, message.length); - final byte[] signatureBC = bcSig.sign(); - nativeSig.initVerify(publicKey); - nativeSig.update(message); - assertTrue(nativeSig.verify(signatureBC), "BC->Native: Ed25519"); - - assertTrue(Arrays.equals(signatureBC, signatureACCP)); + byte[] message, signatureACCP, signatureBC; + Random random = new Random(); + + for (int messageLength = 1; messageLength <= 1024; messageLength++) { + message = new byte[messageLength]; + random.nextBytes(message); + // Sign with ACCP, Verify with BouncyCastle + nativeSig.initSign(privateKey); + nativeSig.update(message, 0, message.length); + signatureACCP = nativeSig.sign(); + bcSig.initVerify(publicKey); + bcSig.update(message); + assertTrue(bcSig.verify(signatureACCP), "Native->BC: Ed25519"); + + // Sign with BouncyCastle, Verify with ACCP + bcSig.initSign(privateKey); + bcSig.update(message, 0, message.length); + signatureBC = bcSig.sign(); + nativeSig.initVerify(publicKey); + nativeSig.update(message); + assertTrue(nativeSig.verify(signatureBC), "BC->Native: Ed25519"); + + assertArrayEquals(signatureBC, signatureACCP); + } } @Test @@ -189,8 +206,8 @@ public void bcKeyValidation() throws GeneralSecurityException { final byte[] pbkBC = kf.generatePublic(publicKeyX509).getEncoded(); // Confirm that ACCP & BC keys are equivalent - assertTrue(Arrays.equals(pkACCP, pkBC)); - assertTrue(Arrays.equals(pbkACCP, pbkBC)); + assertArrayEquals(pkACCP, pkBC); + assertArrayEquals(pbkACCP, pbkBC); } @Test @@ -225,11 +242,11 @@ public void mismatchSignature() throws GeneralSecurityException { nativeSig.initVerify(kp.getPublic()); nativeSig.update(message2, 0, message2.length); - assertTrue(!nativeSig.verify(signature)); + assertFalse(nativeSig.verify(signature)); jceSig.initVerify(kp.getPublic()); jceSig.update(message2, 0, message2.length); - assertTrue(!jceSig.verify(signature)); + assertFalse(jceSig.verify(signature)); } @Test @@ -256,7 +273,7 @@ public void testNullInputs() throws GeneralSecurityException { TestUtil.assertThrows(NullPointerException.class, () -> jceSig.update((byte[]) null)); // Test with null signature jceSig.initVerify(keyPair.getPublic()); - assertTrue(!jceSig.verify(null)); + assertFalse(jceSig.verify(null)); // Test BouncyCastle behavior KeyPair keyPair2 = bcGen.generateKeyPair(); From 77d72f6da1af839ac0c775f51638ed5b5ce69723 Mon Sep 17 00:00:00 2001 From: sp717 Date: Tue, 27 Aug 2024 10:50:06 -0700 Subject: [PATCH 12/14] Update minor changes, comments, error handling --- csrc/ed_gen.cpp | 4 ++-- csrc/java_evp_keys.cpp | 3 +++ csrc/keyutils.cpp | 1 - src/com/amazon/corretto/crypto/provider/EdGen.java | 6 +++--- src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java | 7 ++++--- .../amazon/corretto/crypto/provider/test/EdDSATest.java | 4 ++-- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/csrc/ed_gen.cpp b/csrc/ed_gen.cpp index 6208e859..64f2362c 100644 --- a/csrc/ed_gen.cpp +++ b/csrc/ed_gen.cpp @@ -7,7 +7,7 @@ using namespace AmazonCorrettoCryptoProvider; -void generateEdKey(raii_env* env, EVP_PKEY_auto& key) +void generateEdKey(EVP_PKEY_auto& key) { EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr)); CHECK_OPENSSL(ctx.isInitialized()); @@ -20,7 +20,7 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_EdGen_generateE try { raii_env env(pEnv); EVP_PKEY_auto key; - generateEdKey(&env, key); + generateEdKey(key); return reinterpret_cast(key.take()); } catch (java_ex& ex) { ex.throw_to_java(pEnv); diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index e39ee748..0b93deae 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -350,6 +350,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPriva CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) == 1); result = env->NewByteArray(bufSize); + if (!result) { + throw_java_ex(EX_OOM, "Unable to allocate private key array"); + } env->SetByteArrayRegion(result, 0, bufSize, privateKeyBuffer); } catch (java_ex& ex) { ex.throw_to_java(pEnv); diff --git a/csrc/keyutils.cpp b/csrc/keyutils.cpp index 12f3b5bb..c6948323 100644 --- a/csrc/keyutils.cpp +++ b/csrc/keyutils.cpp @@ -7,7 +7,6 @@ #include #include #include -#include namespace AmazonCorrettoCryptoProvider { diff --git a/src/com/amazon/corretto/crypto/provider/EdGen.java b/src/com/amazon/corretto/crypto/provider/EdGen.java index ad9c865e..cce43dc6 100644 --- a/src/com/amazon/corretto/crypto/provider/EdGen.java +++ b/src/com/amazon/corretto/crypto/provider/EdGen.java @@ -24,12 +24,12 @@ class EdGen extends KeyPairGeneratorSpi { provider_ = provider; try { kf = KeyFactory.getInstance("EdDSA", "SunEC"); - } catch (GeneralSecurityException e) { + } catch (final GeneralSecurityException e) { throw new RuntimeException("Error setting up KeyPairGenerator", e); } } - public void initialize(int keysize, SecureRandom random) { + public void initialize(final int keysize, final SecureRandom random) { throw new UnsupportedOperationException(); } @@ -45,7 +45,7 @@ public KeyPair generateKeyPair() { final PrivateKey jcePrivateKey = kf.generatePrivate(privateKeyPkcs8); final PublicKey jcePublicKey = kf.generatePublic(publicKeyX509); return new KeyPair(jcePublicKey, jcePrivateKey); - } catch (GeneralSecurityException e) { + } catch (final GeneralSecurityException e) { throw new RuntimeException("Error generating key pair", e); } } diff --git a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java index 738040a9..fb40f5da 100644 --- a/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java +++ b/src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java @@ -310,17 +310,18 @@ static class EdDSA extends EvpKeyFactory { } @Override - protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { + protected PrivateKey engineGeneratePrivate(final KeySpec keySpec) + throws InvalidKeySpecException { return super.engineGeneratePrivate(keySpec); } @Override - protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { + protected PublicKey engineGeneratePublic(final KeySpec keySpec) throws InvalidKeySpecException { return super.engineGeneratePublic(keySpec); } @Override - protected T engineGetKeySpec(Key key, Class keySpec) + protected T engineGetKeySpec(final Key key, final Class keySpec) throws InvalidKeySpecException { return super.engineGetKeySpec(key, keySpec); } diff --git a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java index 84f4b18f..91a9a196 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java @@ -157,7 +157,7 @@ public void jceInteropValidation() throws GeneralSecurityException { @Test public void bcInteropValidation() throws GeneralSecurityException { - // Generate keys with ACCP and use BC KeyFactory to get equivalent JCE Keys + // Generate keys with ACCP and use BC KeyFactory to get equivalent Keys final Signature nativeSig = Signature.getInstance("Ed25519", NATIVE_PROVIDER); final Signature bcSig = Signature.getInstance("Ed25519", BOUNCYCASTLE_PROVIDER); final KeyPair keyPair = nativeGen.generateKeyPair(); @@ -192,7 +192,7 @@ public void bcInteropValidation() throws GeneralSecurityException { @Test public void bcKeyValidation() throws GeneralSecurityException { - // Generate keys with ACCP and use BC KeyFactory to get equivalent JCE Keys + // Generate keys with ACCP and use BC KeyFactory to get equivalent Keys final KeyPair kp = nativeGen.generateKeyPair(); final byte[] pkACCP = kp.getPrivate().getEncoded(); final byte[] pbkACCP = kp.getPublic().getEncoded(); From 2216462575943e4bf492da3fa184e0a47fed82b0 Mon Sep 17 00:00:00 2001 From: sp717 Date: Tue, 27 Aug 2024 12:50:22 -0700 Subject: [PATCH 13/14] Change buffer type used in getPrivateKey --- csrc/java_evp_keys.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/csrc/java_evp_keys.cpp b/csrc/java_evp_keys.cpp index 0b93deae..288ceb05 100644 --- a/csrc/java_evp_keys.cpp +++ b/csrc/java_evp_keys.cpp @@ -345,15 +345,14 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpEdPriva size_t bufSize; CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, NULL, &bufSize) == 1); - OPENSSL_buffer_auto privateKeyBuffer; - privateKeyBuffer.buf = (uint8_t*)OPENSSL_malloc(bufSize); - CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer, &bufSize) == 1); + SimpleBuffer privateKeyBuffer(bufSize); + CHECK_OPENSSL(EVP_PKEY_get_raw_private_key(key, privateKeyBuffer.get_buffer(), &bufSize) == 1); result = env->NewByteArray(bufSize); if (!result) { throw_java_ex(EX_OOM, "Unable to allocate private key array"); } - env->SetByteArrayRegion(result, 0, bufSize, privateKeyBuffer); + env->SetByteArrayRegion(result, 0, bufSize, (jbyte*)privateKeyBuffer.get_buffer()); } catch (java_ex& ex) { ex.throw_to_java(pEnv); } From ec8b66ead35f8bb086acdb5d28dfddcfa0b534ef Mon Sep 17 00:00:00 2001 From: sp717 Date: Wed, 28 Aug 2024 10:50:21 -0700 Subject: [PATCH 14/14] Resolve cmakelists merge conflict --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 036dde8a..31083958 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,6 +257,7 @@ set(C_SRC csrc/buffer.cpp csrc/ec_gen.cpp csrc/ec_utils.cpp + csrc/ed_gen.cpp csrc/env.cpp csrc/hkdf.cpp csrc/hmac.cpp @@ -873,4 +874,4 @@ add_custom_target(check-integration set_target_properties(check-integration PROPERTIES EXCLUDE_FROM_ALL 1) # Do this at the end, after we finish all our feature tests, or it'll be missing flags -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csrc/config.h.in ${JNI_HEADER_DIR}/config.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csrc/config.h.in ${JNI_HEADER_DIR}/config.h) \ No newline at end of file