Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions common/src/jni/main/cpp/conscrypt/native_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3096,16 +3096,6 @@ static jbyteArray NativeCrypto_SLHDSA_SHA2_128S_sign(JNIEnv* env, jclass, jbyteA
return nullptr;
}

uint8_t result[SLHDSA_SHA2_128S_SIGNATURE_BYTES];
if (!SLHDSA_SHA2_128S_sign(result,
reinterpret_cast<const unsigned char*>(privateKeyArray.get()),
reinterpret_cast<const unsigned char*>(dataArray.get()), dataLen,
/* context */ NULL, /* context_len */ 0)) {
JNI_TRACE("SLHDSA_SHA2_128S_sign failed");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SLHDSA_SHA2_128S_sign");
return nullptr;
}

ScopedLocalRef<jbyteArray> resultRef(
env, env->NewByteArray(static_cast<jsize>(SLHDSA_SHA2_128S_SIGNATURE_BYTES)));
if (resultRef.get() == nullptr) {
Expand All @@ -3116,7 +3106,16 @@ static jbyteArray NativeCrypto_SLHDSA_SHA2_128S_sign(JNIEnv* env, jclass, jbyteA
if (resultArray.get() == nullptr) {
return nullptr;
}
memcpy(resultArray.get(), result, SLHDSA_SHA2_128S_SIGNATURE_BYTES);

if (!SLHDSA_SHA2_128S_sign(reinterpret_cast<uint8_t*>(resultArray.get()),
reinterpret_cast<const unsigned char*>(privateKeyArray.get()),
reinterpret_cast<const unsigned char*>(dataArray.get()), dataLen,
/* context */ NULL, /* context_len */ 0)) {
Copy link
Copy Markdown
Member

@morambro morambro May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: /*context=*/ and /*context_len=*/

JNI_TRACE("SLHDSA_SHA2_128S_sign failed");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SLHDSA_SHA2_128S_sign");
return nullptr;
}

return resultRef.release();
}

Expand Down Expand Up @@ -3174,18 +3173,33 @@ static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray,
pubkeyArray);
return JNI_FALSE;
}
if (out.size() != 32) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"Output array length != 32");
return JNI_FALSE;
}

ScopedByteArrayRO privkey(env, privkeyArray);
if (privkey.get() == nullptr) {
JNI_TRACE("X25519(%p) => privkey == null", outArray);
return JNI_FALSE;
}
if (privkey.size() != 32) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"Private key array length != 32");
return JNI_FALSE;
}

ScopedByteArrayRO pubkey(env, pubkeyArray);
if (pubkey.get() == nullptr) {
JNI_TRACE("X25519(%p) => pubkey == null", outArray);
return JNI_FALSE;
}
if (pubkey.size() != 32) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"Public key array length != 32");
return JNI_FALSE;
}

if (X25519(reinterpret_cast<uint8_t*>(out.get()),
reinterpret_cast<const uint8_t*>(privkey.get()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ public class OpenSslXwingPrivateKey implements PrivateKey {
public OpenSslXwingPrivateKey(EncodedKeySpec keySpec) throws InvalidKeySpecException {
byte[] encoded = keySpec.getEncoded();
if (keySpec.getFormat().equals("PKCS#8")) {
byte[] preamble = Arrays.copyOf(encoded, pkcs8Preamble.length);
if (!Arrays.equals(preamble, pkcs8Preamble)) {
if (!ArrayUtils.startsWith(encoded, pkcs8Preamble)) {
throw new InvalidKeySpecException("Invalid X-Wing PKCS8 key preamble");
}
raw = Arrays.copyOfRange(encoded, pkcs8Preamble.length, encoded.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,6 @@ private TestKeyStore getTestKeyStore() throws Exception {
return testKeyStore;
}

// Remove legacy EC_EC type, which Jdk now considers invalid. (may or may not be a bug:
// https://bugs.openjdk.org/browse/JDK-8379191)
private static String[] removeLegacyEcTypes(String[] input) {
List<String> list = new ArrayList<>();
for (String s : input) {
if (s != null && !s.equals("EC_EC")) {
list.add(s);
}
}
return list.toArray(new String[0]);
}

@Test
public void test_KeyManagerFactory_getDefaultAlgorithm() throws Exception {
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
Expand Down Expand Up @@ -207,7 +195,7 @@ private void test_KeyManagerFactory_getKeyManagers(KeyManagerFactory kmf, boolea

private void test_X509KeyManager(X509KeyManager km, boolean empty, String algorithm)
throws Exception {
String[] keyTypes = removeLegacyEcTypes(keyTypes(algorithm));
String[] keyTypes = keyTypes(algorithm);
for (String keyType : keyTypes) {
String[] aliases = km.getClientAliases(keyType, null);
if (empty || keyType == null || keyType.isEmpty()) {
Expand Down Expand Up @@ -253,7 +241,7 @@ private void test_X509KeyManager(X509KeyManager km, boolean empty, String algori

private void test_X509ExtendedKeyManager(X509ExtendedKeyManager km, boolean empty,
String algorithm) throws Exception {
String[] keyTypes = removeLegacyEcTypes(keyTypes(algorithm));
String[] keyTypes = keyTypes(algorithm);
String[][] rotatedTypes = rotate(nonEmpty(keyTypes));
for (String[] keyList : rotatedTypes) {
String alias = km.chooseEngineClientAlias(keyList, null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public void test_SSLSocket_setEnabledProtocols() throws Exception {
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
// Check that setEnabledProtocols affects getEnabledProtocols
for (String protocol : ssl.getSupportedProtocols()) {
if ("SSLv2Hello".equals(protocol)) {
if (protocol.equals("SSLv2Hello")) {
// Should fail when SSLv2Hello is set by itself
assertThrows(IllegalArgumentException.class,
() -> ssl.setEnabledProtocols(new String[] {protocol}));
Expand Down Expand Up @@ -977,8 +977,13 @@ public void handshake_noNamedGroupsProperty_usesDefaultGroups() throws Exception
c.get();
// By default, BoringSSL uses X25519, P-256, and P-384, in this order.
// So X25519 gets priority.
assertEquals("X25519", getCurveName(client));
assertEquals("X25519", getCurveName(server));
// We also allow for X25519MLKEM768, as that may be the default in the future.
String clientCurve = getCurveName(client);
String serverCurve = getCurveName(server);
assertTrue("Unexpected client curve: " + clientCurve,
clientCurve.equals("X25519") || clientCurve.equals("X25519MLKEM768"));
assertTrue("Unexpected server curve: " + serverCurve,
serverCurve.equals("X25519") || serverCurve.equals("X25519MLKEM768"));
client.close();
server.close();
context.close();
Expand Down
9 changes: 7 additions & 2 deletions openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,13 @@ public void test_handshake() throws Exception {

// By default, BoringSSL supports curves "X25519", "P-256" and "P-384".
// X25519 gets priority, so that curve will be used in the handshake here.
assertEquals("X25519", connection.server.getCurveNameForTesting());
assertEquals("X25519", connection.client.getCurveNameForTesting());
// We also allow for X25519MLKEM768, as that may be the default in the future.
String serverCurve = connection.server.getCurveNameForTesting();
String clientCurve = connection.client.getCurveNameForTesting();
assertTrue("Unexpected server curve: " + serverCurve,
serverCurve.equals("X25519") || serverCurve.equals("X25519MLKEM768"));
assertTrue("Unexpected client curve: " + clientCurve,
clientCurve.equals("X25519") || clientCurve.equals("X25519MLKEM768"));
}

@Test
Expand Down
58 changes: 58 additions & 0 deletions openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4595,4 +4595,62 @@ public void test_slhdsa_sha2_128s_works() throws Exception {
RuntimeException.class,
() -> NativeCrypto.SLHDSA_SHA2_128S_generate_key(publicKeyTooLong, privateKey));
}

@Test
public void x25519_testVector1FromRfc7748_works() throws Exception {
// Test vector from RFC 7748, Section 6.1 (Alice's side)
byte[] privateKey =
decodeHex("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
byte[] expectedOutput =
decodeHex("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
byte[] base = new byte[32];
base[0] = 9;

byte[] out = new byte[32];
boolean success = NativeCrypto.X25519(out, privateKey, base);

assertTrue(success);
assertArrayEquals(expectedOutput, out);
}

@Test
public void x25519_testVector2FromRfc7748_works() throws Exception {
// Test vector from RFC 7748, Section 6.1 (Bob's side)
byte[] privateKey =
decodeHex("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
byte[] expectedOutput =
decodeHex("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
byte[] base = new byte[32];
base[0] = 9;

byte[] out = new byte[32];
boolean success = NativeCrypto.X25519(out, privateKey, base);

assertTrue(success);
assertArrayEquals(expectedOutput, out);
}

@Test
public void x25519_invalidInputSize_throwsIllegalArgumentException() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> NativeCrypto.X25519(new byte[31], new byte[32], new byte[32]));

assertThrows(IllegalArgumentException.class,
() -> NativeCrypto.X25519(new byte[32], new byte[31], new byte[32]));

assertThrows(IllegalArgumentException.class,
() -> NativeCrypto.X25519(new byte[32], new byte[32], new byte[31]));
}

@Test
public void x25519_nullInput_throwsIllegalArgumentException() throws Exception {
assertThrows(NullPointerException.class,
() -> NativeCrypto.X25519(null, new byte[32], new byte[32]));

assertThrows(NullPointerException.class,
() -> NativeCrypto.X25519(new byte[32], null, new byte[32]));

assertThrows(NullPointerException.class,
() -> NativeCrypto.X25519(new byte[32], new byte[32], null));
}
}
54 changes: 54 additions & 0 deletions platform/src/main/java/org/conscrypt/AndroidHpkeSpi.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,58 @@ public X25519_CHACHA20() {
super(new HpkeImpl.X25519_CHACHA20());
}
}

public static class MlKem768HkdfSha256Aes128Gcm extends AndroidHpkeSpi {
public MlKem768HkdfSha256Aes128Gcm() {
super(new HpkeImpl.MlKem768HkdfSha256Aes128Gcm());
}
}

public static class MlKem768HkdfSha256Aes256Gcm extends AndroidHpkeSpi {
public MlKem768HkdfSha256Aes256Gcm() {
super(new HpkeImpl.MlKem768HkdfSha256Aes256Gcm());
}
}

public static class MlKem768HkdfSha256ChaCha20Poly1305 extends AndroidHpkeSpi {
public MlKem768HkdfSha256ChaCha20Poly1305() {
super(new HpkeImpl.MlKem768HkdfSha256ChaCha20Poly1305());
}
}

public static class MlKem1024HkdfSha256Aes128Gcm extends AndroidHpkeSpi {
public MlKem1024HkdfSha256Aes128Gcm() {
super(new HpkeImpl.MlKem1024HkdfSha256Aes128Gcm());
}
}

public static class MlKem1024HkdfSha256Aes256Gcm extends AndroidHpkeSpi {
public MlKem1024HkdfSha256Aes256Gcm() {
super(new HpkeImpl.MlKem1024HkdfSha256Aes256Gcm());
}
}

public static class MlKem1024HkdfSha256ChaCha20Poly1305 extends AndroidHpkeSpi {
public MlKem1024HkdfSha256ChaCha20Poly1305() {
super(new HpkeImpl.MlKem1024HkdfSha256ChaCha20Poly1305());
}
}

public static class XwingHkdfSha256Aes128Gcm extends AndroidHpkeSpi {
public XwingHkdfSha256Aes128Gcm() {
super(new HpkeImpl.XwingHkdfSha256Aes128Gcm());
}
}

public static class XwingHkdfSha256Aes256Gcm extends AndroidHpkeSpi {
public XwingHkdfSha256Aes256Gcm() {
super(new HpkeImpl.XwingHkdfSha256Aes256Gcm());
}
}

public static class XwingHkdfSha256ChaCha20Poly1305 extends AndroidHpkeSpi {
public XwingHkdfSha256ChaCha20Poly1305() {
super(new HpkeImpl.XwingHkdfSha256ChaCha20Poly1305());
}
}
}
11 changes: 10 additions & 1 deletion platform/src/test/java/org/conscrypt/AndroidHpkeSpiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@ public class AndroidHpkeSpiTest {
private static final String[] HPKE_NAMES =
new String[] {"DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/AES_128_GCM",
"DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/AES_256_GCM",
"DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/CHACHA20POLY1305"};
"DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/CHACHA20POLY1305",
"MLKEM_768/HKDF_SHA256/AES_128_GCM",
"MLKEM_768/HKDF_SHA256/AES_256_GCM",
"MLKEM_768/HKDF_SHA256/CHACHA20POLY1305",
"MLKEM_1024/HKDF_SHA256/AES_128_GCM",
"MLKEM_1024/HKDF_SHA256/AES_256_GCM",
"MLKEM_1024/HKDF_SHA256/CHACHA20POLY1305",
"XWING/HKDF_SHA256/AES_128_GCM",
"XWING/HKDF_SHA256/AES_256_GCM",
"XWING/HKDF_SHA256/CHACHA20POLY1305"};

// This only needs to test the wrapper functionality as the implementation and client
// APIs are tested elsewhere. What we're looking for is that HPKE SPI instances returned
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ private static void addOpenSsl(String cipherSuite) {
// https://boringssl.googlesource.com/boringssl/+/main/ssl/extensions.cc#215
private static final List<String> ELLIPTIC_CURVES_DEFAULT =
Arrays.asList("x25519 (29)", "secp256r1 (23)", "secp384r1 (24)");
private static final List<String> ELLIPTIC_CURVES_DEFAULT_PQC = Arrays.asList(
"x25519mlkem768 (4588)", "x25519 (29)", "secp256r1 (23)", "secp384r1 (24)");

/**
* Asserts that the cipher suites array is non-null and that it
Expand Down Expand Up @@ -441,7 +443,10 @@ public static void assertDefaultCipherSuites(String[] cipherSuites) {
}

public static void assertDefaultEllipticCurves(String[] curves) {
assertEquals(ELLIPTIC_CURVES_DEFAULT, Arrays.asList(curves));
List<String> actualList = Arrays.asList(curves);
assertTrue("Unexpected default curves list: " + actualList,
actualList.equals(ELLIPTIC_CURVES_DEFAULT)
|| actualList.equals(ELLIPTIC_CURVES_DEFAULT_PQC));
}

public static void assertSSLContextEnabledProtocols(String version, String[] protocols) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public enum EllipticCurve {
BRAINPOOLP521R1(28, "brainpoolP521r1"),
X25519(29, "x25519"),
X448(30, "x448"),
MLKEM1024(514, "mlkem1024"),
X25519MLKEM768(4588, "x25519mlkem768"),
X25519KYBER768DRAFT00(25055, "x25519kyber768draft00"),
ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves"),
ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves");
public final int identifier;
Expand Down
Loading