From e24a0ce31de7476a4352795151dce2897faa9f69 Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Mon, 7 Aug 2023 16:23:30 -0500 Subject: [PATCH] Add support for X25519 algorithm alias to XDH * Added X25519 aliases for KeyPairGenerator, KeyFactory, and KeyAgreement in OpenSSLProvider --- .../java/org/conscrypt/OpenSSLProvider.java | 8 +++++ .../org/conscrypt/OpenSSLXDHKeyFactory.java | 5 +-- .../java/security/KeyFactoryTestX25519.java | 35 +++++++++++++++++++ .../java/security/KeyFactoryTestXDH.java | 16 +++++---- .../java/security/KeyPairGeneratorTest.java | 17 +++++++-- .../javax/crypto/X25519KeyAgreementTest.java | 28 +++++++++++++++ .../javax/crypto/XDHKeyAgreementTest.java | 11 ++++-- 7 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 common/src/test/java/org/conscrypt/java/security/KeyFactoryTestX25519.java create mode 100644 common/src/test/java/org/conscrypt/javax/crypto/X25519KeyAgreementTest.java diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java index 138f388ed..a17b7380a 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java +++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java @@ -196,6 +196,7 @@ public OpenSSLProvider(String providerName) { put("Alg.Alias.KeyPairGenerator.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyPairGenerator.1.3.133.16.840.63.0.2", "EC"); + put("KeyPairGenerator.X25519", PREFIX + "OpenSSLXDHKeyPairGenerator"); put("KeyPairGenerator.XDH", PREFIX + "OpenSSLXDHKeyPairGenerator"); put("Alg.Alias.KeyPairGenerator.1.3.101.110", "XDH"); @@ -209,6 +210,7 @@ public OpenSSLProvider(String providerName) { put("Alg.Alias.KeyFactory.1.2.840.10045.2.1", "EC"); put("Alg.Alias.KeyFactory.1.3.133.16.840.63.0.2", "EC"); + put("KeyFactory.X25519", PREFIX + "OpenSSLXDHKeyFactory"); put("KeyFactory.XDH", PREFIX + "OpenSSLXDHKeyFactory"); put("Alg.Alias.KeyFactory.1.3.101.110", "XDH"); @@ -624,6 +626,12 @@ private void putXDHKeyAgreementImplClass(String className) { PREFIX + className, supportedKeyClasses, supportedKeyFormats); + + putImplClassWithKeyConstraints( + "KeyAgreement.X25519", + PREFIX + className, + supportedKeyClasses, + supportedKeyFormats); } private void putImplClassWithKeyConstraints(String typeAndAlgName, diff --git a/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java index e4af80258..53a8a0c65 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java +++ b/common/src/main/java/org/conscrypt/OpenSSLXDHKeyFactory.java @@ -75,8 +75,9 @@ protected T engineGetKeySpec(Key key, Class keySpec) throw new InvalidKeySpecException("keySpec == null"); } - if (!"XDH".equals(key.getAlgorithm())) { - throw new InvalidKeySpecException("Key must be an XDH key"); + // Support XDH or X25519 algorithm names per JEP 324 + if (!"XDH".equals(key.getAlgorithm()) || !"X25519".equals(key.getAlgorithm()) ) { + throw new InvalidKeySpecException("Key must be an XDH or X25519 key"); } Class publicKeySpec = getJavaPublicKeySpec(); diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestX25519.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestX25519.java new file mode 100644 index 000000000..c66b0137e --- /dev/null +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestX25519.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.conscrypt.java.security; + +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import tests.util.ServiceTester; + +@RunWith(JUnit4.class) +public class KeyFactoryTestX25519 extends KeyFactoryTestXDH { + + public KeyFactoryTestX25519() { + super("X25519"); + } +} diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java index 1f6b5fd7c..35ce3738e 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestXDH.java @@ -31,12 +31,16 @@ public class KeyFactoryTestXDH extends AbstractKeyFactoryTest { public KeyFactoryTestXDH() { - super("XDH", X509EncodedKeySpec.class, PKCS8EncodedKeySpec.class); + this("XDH"); + } + + public KeyFactoryTestXDH(final String algorithmName) { + super(algorithmName, X509EncodedKeySpec.class, PKCS8EncodedKeySpec.class); } @Override protected void check(KeyPair keyPair) throws Exception { - new KeyAgreementHelper("XDH").test(keyPair); + new KeyAgreementHelper(algorithmName).test(keyPair); } @Override @@ -49,12 +53,12 @@ protected ServiceTester customizeTester(ServiceTester tester) { protected List getKeys() throws NoSuchAlgorithmException, InvalidKeySpecException { return Arrays.asList( new KeyPair( - DefaultKeys.getPublicKey("XDH"), - DefaultKeys.getPrivateKey("XDH") + DefaultKeys.getPublicKey(algorithmName), + DefaultKeys.getPrivateKey(algorithmName) ), new KeyPair( - new TestPublicKey(DefaultKeys.getPublicKey("XDH")), - new TestPrivateKey(DefaultKeys.getPrivateKey("XDH")) + new TestPublicKey(DefaultKeys.getPublicKey(algorithmName)), + new TestPrivateKey(DefaultKeys.getPrivateKey(algorithmName)) ) ); } diff --git a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java index 3ef73a2b7..0c7dbafbc 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java @@ -89,7 +89,7 @@ public void test(Provider provider, String algorithm) throws Exception { } // KeyPairGenerator.getInstance(String) KeyPairGenerator kpg1 = KeyPairGenerator.getInstance(algorithm); - assertEquals(algorithm, kpg1.getAlgorithm()); + assertAlgorithmMatched(algorithm, kpg1); if (params != null) { kpg1.initialize(params); } @@ -97,7 +97,7 @@ public void test(Provider provider, String algorithm) throws Exception { // KeyPairGenerator.getInstance(String, Provider) KeyPairGenerator kpg2 = KeyPairGenerator.getInstance(algorithm, provider); - assertEquals(algorithm, kpg2.getAlgorithm()); + assertAlgorithmMatched(algorithm, kpg2); assertEquals(provider, kpg2.getProvider()); if (params != null) { kpg2.initialize(params); @@ -107,7 +107,7 @@ public void test(Provider provider, String algorithm) throws Exception { // KeyPairGenerator.getInstance(String, String) KeyPairGenerator kpg3 = KeyPairGenerator.getInstance(algorithm, provider.getName()); - assertEquals(algorithm, kpg3.getAlgorithm()); + assertAlgorithmMatched(algorithm, kpg3); assertEquals(provider, kpg3.getProvider()); if (params != null) { kpg3.initialize(params); @@ -361,6 +361,17 @@ private static void assertECParametersEquals(ECParameterSpec expected, ECParamet assertEquals(expected.getCofactor(), actual.getCofactor()); } + private static void assertAlgorithmMatched(final String algorithm, final KeyPairGenerator keyPairGenerator) { + final String expectedAlgorithm; + // X25519 KeyPairGenerator is an alias for XDH requiring this alternative expected algorithm + if ("X25519".equals(algorithm)) { + expectedAlgorithm = "XDH"; + } else { + expectedAlgorithm = algorithm; + } + assertEquals(expectedAlgorithm, keyPairGenerator.getAlgorithm()); + } + /** * DH parameters pre-generated so that the test doesn't take too long. * These parameters were generated with: diff --git a/common/src/test/java/org/conscrypt/javax/crypto/X25519KeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/X25519KeyAgreementTest.java new file mode 100644 index 000000000..a235a2b82 --- /dev/null +++ b/common/src/test/java/org/conscrypt/javax/crypto/X25519KeyAgreementTest.java @@ -0,0 +1,28 @@ +package org.conscrypt.javax.crypto; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.KeyAgreement; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +/** + * Tests for all registered X25519 {@link KeyAgreement} providers. + */ +@RunWith(JUnit4.class) +public class X25519KeyAgreementTest extends XDHKeyAgreementTest { + + @Override + protected String getAlgorithm() { + return "X25519"; + } +} diff --git a/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java index 4cd084365..e92ae6065 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/XDHKeyAgreementTest.java @@ -59,7 +59,7 @@ public class XDHKeyAgreementTest { private PublicKey rfc7748X25519PublicKey; private void setupKeys(Provider p) throws Exception { - KeyFactory kf = KeyFactory.getInstance("XDH", p); + KeyFactory kf = KeyFactory.getInstance(getAlgorithm(), p); byte[] privateKey; if ("SunEC".equalsIgnoreCase(p.getName()) @@ -84,15 +84,20 @@ private void setupKeys(Provider p) throws Exception { @Test public void test_XDHKeyAgreement() throws Exception { - for (Provider p : Security.getProviders("KeyAgreement.XDH")) { + final String keyAgreementAlgorithm = String.format("KeyAgreement.%s", getAlgorithm()); + for (Provider p : Security.getProviders(keyAgreementAlgorithm)) { setupKeys(p); - KeyAgreement ka = KeyAgreement.getInstance("XDH", p); + KeyAgreement ka = KeyAgreement.getInstance(getAlgorithm(), p); test_x25519_keyAgreement_rfc7748_kat_success(ka); } } + protected String getAlgorithm() { + return "XDH"; + } + private void test_x25519_keyAgreement_rfc7748_kat_success(KeyAgreement ka) throws Exception { ka.init(rfc7748X25519PrivateKey); ka.doPhase(rfc7748X25519PublicKey, true);