Skip to content

Commit

Permalink
Make Ed25519 KeyFactory registration Opt-in (#410)
Browse files Browse the repository at this point in the history
+ The keys generated by KeyFactory of ACCP for Ed25519 do not implemet
  EdEcKey interface from JCA since this interface is not present to JDKs
  prior to 15.
  • Loading branch information
amirhosv authored Nov 13, 2024
1 parent f2d2e31 commit c1709e7
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 5 deletions.
15 changes: 14 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,18 @@ add_custom_target(check-install-via-properties-with-debug

DEPENDS accp-jar tests-jar)

add_custom_target(check-junit-edKeyFactory
COMMAND ${TEST_JAVA_EXECUTABLE}
-Dcom.amazon.corretto.crypto.provider.registerEdKeyFactory=true
${TEST_RUNNER_ARGUMENTS}
--select-class=com.amazon.corretto.crypto.provider.test.EdDSATest
--select-class=com.amazon.corretto.crypto.provider.test.EvpKeyFactoryTest
--select-class=com.amazon.corretto.crypto.provider.test.EvpSignatureSpecificTest
--select-class=com.amazon.corretto.crypto.provider.test.EvpSignatureTest
--select-class=com.amazon.corretto.crypto.provider.test.KeyReuseThreadStormTest

DEPENDS accp-jar tests-jar)

set(check_targets check-recursive-init
check-install-via-properties
check-install-via-properties-with-debug
Expand All @@ -832,7 +844,8 @@ set(check_targets check-recursive-init
check-external-lib
check-junit-AesLazy
check-junit-AesEager
check-junit-DifferentTempDir)
check-junit-DifferentTempDir
check-junit-edKeyFactory)

if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(check_targets ${check_targets} check-with-jni-flag)
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ SecureRandom:
KeyFactory:
* EC
* RSA
* ED25519 (JDK 15+)
* ED25519 (JDK 15+). Please refer to [system properties](https://github.com/corretto/amazon-corretto-crypto-provider#other-system-properties) for more information.

AlgorithmParameters:
* EC. Please refer to [system properties](https://github.com/corretto/amazon-corretto-crypto-provider#other-system-properties) for more information.
Expand Down Expand Up @@ -384,6 +384,14 @@ Thus, these should all be set on the JVM command line using `-D`.
* `com.amazon.corretto.crypto.provider.tmpdir`
Allows one to set the temporary directory used by ACCP when loading native libraries.
If this system property is not defined, the system property `java.io.tmpdir` is used.
* `com.amazon.corretto.crypto.provider.registerEdKeyFactory`
Takes in `true` or `false` (defaults to `false`).
If `true` and JDK version is 15+, then ACCP will register its Ed25519 related KeyFactory classes.
The keys produced by ACCP's KeyFactory services for Ed25519 do not implement [EdECKey](https://docs.oracle.com/en/java/javase/17/docs//api/java.base/java/security/interfaces/EdECKey.html)
interface, and as a result, they cannot be used by other providers. Consider setting this property
to `true` if the keys are only used by other ACCP services AND they are not type cast to `EdECKey`.
It is worth noting that the key generated by KeyFactory service of SunEC can be used by ACCP services
such as Signature.

# License
This library is licensed under the Apache 2.0 license although portions of this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public final class AmazonCorrettoCryptoProvider extends java.security.Provider {
private static final String PROPERTY_CACHE_SELF_TEST_RESULTS = "cacheselftestresults";
private static final String PROPERTY_REGISTER_EC_PARAMS = "registerEcParams";
private static final String PROPERTY_REGISTER_SECURE_RANDOM = "registerSecureRandom";
private static final String PROPERTY_REGISTER_ED_KEYFACTORY = "registerEdKeyFactory";

private static final long serialVersionUID = 1L;

Expand All @@ -61,6 +62,7 @@ public final class AmazonCorrettoCryptoProvider extends java.security.Provider {
private final boolean shouldRegisterEcParams;
private final boolean shouldRegisterSecureRandom;
private final boolean shouldRegisterEdDSA;
private final boolean shouldRegisterEdKeyFactory;
private final Utils.NativeContextReleaseStrategy nativeContextReleaseStrategy;

private transient SelfTestSuite selfTestSuite = new SelfTestSuite();
Expand Down Expand Up @@ -91,8 +93,15 @@ private void buildServiceMap() {
addService("KeyFactory", "EC", "EvpKeyFactory$EC");

if (shouldRegisterEdDSA) {
addService("KeyFactory", "EdDSA", "EvpKeyFactory$EdDSA");
addService("KeyFactory", "Ed25519", "EvpKeyFactory$EdDSA");
// KeyFactories are used to convert key encodings to Java Key objects. ACCP's KeyFactory for
// Ed25519 returns keys of type EvpEdPublicKey and EvpEdPrivateKey that do not implement
// EdECKey interface. One should register the KeyFactories from ACCP if they only use the
// output of the factories with ACCP's services.
// Once ACCP supports multi-release jar, then this option can be removed.
if (shouldRegisterEdKeyFactory) {
addService("KeyFactory", "EdDSA", "EvpKeyFactory$EdDSA");
addService("KeyFactory", "Ed25519", "EvpKeyFactory$EdDSA");
}
addService("KeyPairGenerator", "EdDSA", "EdGen");
addService("KeyPairGenerator", "Ed25519", "EdGen");
}
Expand Down Expand Up @@ -492,6 +501,9 @@ public AmazonCorrettoCryptoProvider() {
// successfully on older versions of Java we can only register EdDSA if JDK version >= 15.
this.shouldRegisterEdDSA = Utils.getJavaVersion() >= 15;

this.shouldRegisterEdKeyFactory =
Utils.getBooleanProperty(PROPERTY_REGISTER_ED_KEYFACTORY, false);

this.nativeContextReleaseStrategy = Utils.getNativeContextReleaseStrategyProperty();

Utils.optionsFromProperty(ExtraCheck.class, extraChecks, "extrachecks");
Expand Down Expand Up @@ -721,7 +733,7 @@ KeyFactory getKeyFactory(EvpKeyType keyType) {
return ecFactory;
case EdDSA:
if (edFactory == null) {
edFactory = KeyFactory.getInstance(keyType.jceName, this);
edFactory = new EdKeyFactory(this);
}
return edFactory;
default:
Expand All @@ -745,4 +757,12 @@ EvpKey translateKey(Key key, EvpKeyType keyType) throws InvalidKeyException {
return (EvpKey) getKeyFactory(keyType).translateKey(key);
}
}

// In case the user does not register Ed25519 KeyFactories by ACCP, we still need one to be used
// internally.
private static class EdKeyFactory extends KeyFactory {
EdKeyFactory(final AmazonCorrettoCryptoProvider provider) {
super(new EvpKeyFactory.EdDSA(provider), provider, "Ed25519");
}
}
}
44 changes: 44 additions & 0 deletions tst/com/amazon/corretto/crypto/provider/test/EdDSATest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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 static org.junit.jupiter.api.Assumptions.assumeTrue;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
Expand Down Expand Up @@ -96,6 +97,7 @@ public void keyGenValidation() throws GeneralSecurityException {

@Test
public void keyFactoryValidation() throws GeneralSecurityException {
assumeTrue(TestUtil.edKeyFactoryRegistered());
final KeyPair keyPair = jceGen.generateKeyPair();

final byte[] privateKeyJCE = keyPair.getPrivate().getEncoded();
Expand Down Expand Up @@ -210,6 +212,47 @@ public void bcKeyValidation() throws GeneralSecurityException {
assertArrayEquals(pbkACCP, pbkBC);
}

@Test
public void jceKeyValidation() throws Exception {
// Generate keys with ACCP and use JCE KeyFactory to get equivalent Keys
final KeyPair kp = nativeGen.generateKeyPair();
final Class<?> edPrivateKeyCls = Class.forName("java.security.interfaces.EdECPrivateKey");
final Class<?> edPPublicKeyCls = Class.forName("java.security.interfaces.EdECPublicKey");
assertTrue(edPrivateKeyCls.isAssignableFrom(kp.getPrivate().getClass()));
assertTrue(edPPublicKeyCls.isAssignableFrom(kp.getPublic().getClass()));
final byte[] privateKeyAccpEncoding = kp.getPrivate().getEncoded();
final byte[] publicKeyAccpEncoding = kp.getPublic().getEncoded();

final PKCS8EncodedKeySpec privateKeyPkcs8 = new PKCS8EncodedKeySpec(privateKeyAccpEncoding);
final X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(publicKeyAccpEncoding);

final KeyFactory kf = KeyFactory.getInstance("Ed25519", "SunEC");

final PrivateKey privateKeyJce = kf.generatePrivate(privateKeyPkcs8);
final PublicKey publicKeyJce = kf.generatePublic(publicKeyX509);

// Confirm that ACCP & SunJCE keys are equivalent
assertArrayEquals(privateKeyAccpEncoding, privateKeyJce.getEncoded());
assertArrayEquals(publicKeyAccpEncoding, publicKeyJce.getEncoded());

// SunEC keys produced by its KeyFactory should be usable by EdDSA from ACCP
final Signature sigService = Signature.getInstance("EdDSA", NATIVE_PROVIDER);

for (int messageLength = 1; messageLength <= 1024; messageLength++) {
final byte[] message = new byte[messageLength];
final Random rand = new Random(messageLength);
rand.nextBytes(message);

sigService.initSign(privateKeyJce);
sigService.update(message);
final byte[] signature = sigService.sign();

sigService.initVerify(publicKeyJce);
sigService.update(message);
assertTrue(sigService.verify(signature));
}
}

@Test
public void eddsaValidation() throws GeneralSecurityException {
// Generate keys, sign, & verify with ACCP
Expand Down Expand Up @@ -251,6 +294,7 @@ public void mismatchSignature() throws GeneralSecurityException {

@Test
public void testInvalidKey() throws GeneralSecurityException {
assumeTrue(TestUtil.edKeyFactoryRegistered());
byte[] invalidKeyBytes = new byte[] {};
PKCS8EncodedKeySpec invalidPrivateKeySpec = new PKCS8EncodedKeySpec(invalidKeyBytes);
X509EncodedKeySpec invalidPublicKeySpec = new X509EncodedKeySpec(invalidKeyBytes);
Expand Down
5 changes: 5 additions & 0 deletions tst/com/amazon/corretto/crypto/provider/test/TestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -840,4 +840,9 @@ static Digest bcDigest(final String digest) {
return null;
}
}

static boolean edKeyFactoryRegistered() {
return "true"
.equals(System.getProperty("com.amazon.corretto.crypto.provider.registerEdKeyFactory"));
}
}

0 comments on commit c1709e7

Please sign in to comment.