Skip to content
Open
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
98 changes: 98 additions & 0 deletions common/src/jni/main/cpp/conscrypt/native_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3520,6 +3520,59 @@ static jbyteArray NativeCrypto_SLHDSA_SHA2_128S_sign(JNIEnv* env, jclass, jbyteA
return resultRef.release();
}

static jbyteArray NativeCrypto_SLHDSA_SHA2_128S_prehash_sign(JNIEnv* env, jclass, jbyteArray data,
jint dataLen, jint hashNid, jbyteArray privateKey) {
CHECK_ERROR_QUEUE_ON_RETURN;

ScopedByteArrayRO privateKeyArray(env, privateKey);
if (privateKeyArray.get() == nullptr) {
JNI_TRACE("NativeCrypto_SLHDSA_SHA2_128S_prehash_sign => privateKey == null");
return nullptr;
}

if (privateKeyArray.size() != SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"Private key array length != 64");
return nullptr;
}

ScopedByteArrayRO dataArray(env, data);
if (dataArray.get() == nullptr) {
return nullptr;
}

if (ARRAY_OFFSET_LENGTH_INVALID(dataArray, 0, dataLen)) {
conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException",
"dataLen");
return nullptr;
}

ScopedLocalRef<jbyteArray> resultRef(
env, env->NewByteArray(static_cast<jsize>(SLHDSA_SHA2_128S_SIGNATURE_BYTES)));
if (resultRef.get() == nullptr) {
return nullptr;
}

ScopedByteArrayRW resultArray(env, resultRef.get());
if (resultArray.get() == nullptr) {
return nullptr;
}

int success = SLHDSA_SHA2_128S_prehash_warning_nonstandard_sign(
reinterpret_cast<uint8_t*>(resultArray.get()),
reinterpret_cast<const unsigned char*>(privateKeyArray.get()),
reinterpret_cast<const unsigned char*>(dataArray.get()), dataLen,
hashNid, /* context */ NULL, /* context_len */ 0);

if (!success) {
JNI_TRACE("SLHDSA_SHA2_128S_prehash_sign failed");
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SLHDSA_SHA2_128S_prehash_sign");
return nullptr;
}

return resultRef.release();
}

static jint NativeCrypto_SLHDSA_SHA2_128S_verify(JNIEnv* env, jclass, jbyteArray data, jint dataLen,
jbyteArray sig, jbyteArray publicKey) {
CHECK_ERROR_QUEUE_ON_RETURN;
Expand Down Expand Up @@ -3563,6 +3616,49 @@ static jint NativeCrypto_SLHDSA_SHA2_128S_verify(JNIEnv* env, jclass, jbyteArray
return static_cast<jint>(result);
}

static jint NativeCrypto_SLHDSA_SHA2_128S_prehash_verify(JNIEnv* env, jclass, jbyteArray data, jint dataLen,
jbyteArray sig, jint hashNid, jbyteArray publicKey) {
CHECK_ERROR_QUEUE_ON_RETURN;

ScopedByteArrayRO publicKeyArray(env, publicKey);
if (publicKeyArray.get() == nullptr) {
JNI_TRACE("NativeCrypto_SLHDSA_SHA2_128S_prehash_verify => publicKey == null");
return -1;
}

if (publicKeyArray.size() != SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES) {
conscrypt::jniutil::throwException(env, "java/lang/IllegalArgumentException",
"Public key array length != 32");
return -1;
}

ScopedByteArrayRO dataArray(env, data);
if (dataArray.get() == nullptr) {
return -1;
}

if (ARRAY_OFFSET_LENGTH_INVALID(dataArray, 0, dataLen)) {
conscrypt::jniutil::throwException(env, "java/lang/ArrayIndexOutOfBoundsException",
"dataLen");
return -1;
}

ScopedByteArrayRO sigArray(env, sig);
if (sigArray.get() == nullptr) {
return -1;
}

int result = SLHDSA_SHA2_128S_prehash_warning_nonstandard_verify(
reinterpret_cast<const unsigned char*>(sigArray.get()), sigArray.size(),
reinterpret_cast<const unsigned char*>(publicKeyArray.get()),
reinterpret_cast<const unsigned char*>(dataArray.get()), dataLen,
hashNid, /*context=*/NULL, /*context_len=*/0);

JNI_TRACE("NativeCrypto_SLHDSA_SHA2_128S_prehash_verify(%p, %p, %p) => %d", publicKey, sig, data,
result);
return static_cast<jint>(result);
}

static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray,
jbyteArray privkeyArray, jbyteArray pubkeyArray) {
CHECK_ERROR_QUEUE_ON_RETURN;
Expand Down Expand Up @@ -12176,7 +12272,9 @@ static JNINativeMethod sNativeCryptoMethods[] = {
CONSCRYPT_NATIVE_METHOD(MLDSA87_public_key_from_seed, "([B)[B"),
CONSCRYPT_NATIVE_METHOD(SLHDSA_SHA2_128S_generate_key, "([B[B)V"),
CONSCRYPT_NATIVE_METHOD(SLHDSA_SHA2_128S_sign, "([BI[B)[B"),
CONSCRYPT_NATIVE_METHOD(SLHDSA_SHA2_128S_prehash_sign, "([BII[B)[B"),
CONSCRYPT_NATIVE_METHOD(SLHDSA_SHA2_128S_verify, "([BI[B[B)I"),
CONSCRYPT_NATIVE_METHOD(SLHDSA_SHA2_128S_prehash_verify, "([BI[BI[B)I"),
CONSCRYPT_NATIVE_METHOD(X25519, "([B[B[B)Z"),
CONSCRYPT_NATIVE_METHOD(X25519_keypair, "([B[B)V"),
CONSCRYPT_NATIVE_METHOD(ED25519_keypair, "([B[B)V"),
Expand Down
8 changes: 8 additions & 0 deletions common/src/main/java/org/conscrypt/NativeCrypto.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,18 @@
// android-add: @FastNative
static native byte[] SLHDSA_SHA2_128S_sign(byte[] data, int dataLen, byte[] privateKey);

// android-add: @FastNative
static native byte[] SLHDSA_SHA2_128S_prehash_sign(byte[] data, int dataLen, int hashNid, byte[] privateKey);

// android-add: @FastNative
static native int SLHDSA_SHA2_128S_verify(byte[] data, int dataLen, byte[] sig,
byte[] publicKey);

// android-add: @FastNative
static native int SLHDSA_SHA2_128S_prehash_verify(byte[] data, int dataLen, byte[] sig,
int hashNid, byte[] publicKey);


// --- Curve25519 --------------

// android-add: @FastNative
Expand Down Expand Up @@ -1876,7 +1884,7 @@
static native byte[] getApplicationProtocol(long ssl, NativeSsl ssl_holder);

/**
* Variant of the {@link #SSL_do_handshake} used by {@link ConscryptEngine}. This differs

Check warning on line 1887 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (macos-latest)

[InvalidLink] The reference `#SSL_do_handshake` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.

Check warning on line 1887 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

[InvalidLink] The reference `#SSL_do_handshake` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.
* slightly from the raw BoringSSL API in that it returns the SSL error code from the operation,
* rather than the return value from {@code SSL_do_handshake}. This is done in order to allow to
* properly handle SSL errors and propagate useful exceptions.
Expand All @@ -1889,7 +1897,7 @@
SSLHandshakeCallbacks shc) throws IOException;

/**
* Variant of the {@link #SSL_read} for a direct {@link java.nio.ByteBuffer} used by {@link

Check warning on line 1900 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (macos-latest)

[InvalidLink] The reference `#SSL_read` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.

Check warning on line 1900 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

[InvalidLink] The reference `#SSL_read` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.
* ConscryptEngine}.
*
* @return if positive, represents the number of bytes read into the given buffer. Returns
Expand All @@ -1906,7 +1914,7 @@
throws IOException, CertificateException;

/**
* Variant of the {@link #SSL_write} for a direct {@link java.nio.ByteBuffer} used by {@link

Check warning on line 1917 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (macos-latest)

[InvalidLink] The reference `#SSL_write` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.

Check warning on line 1917 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

[InvalidLink] The reference `#SSL_write` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.
* ConscryptEngine}. This version does not lock or and does no error pre-processing.
*/
static native int ENGINE_SSL_write_direct(long ssl, NativeSsl ssl_holder, long address,
Expand Down Expand Up @@ -1934,7 +1942,7 @@
SSLHandshakeCallbacks shc) throws IOException;

/**
* Variant of the {@link #SSL_shutdown} used by {@link ConscryptEngine}. This version does not

Check warning on line 1945 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (macos-latest)

[InvalidLink] The reference `#SSL_shutdown` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.

Check warning on line 1945 in common/src/main/java/org/conscrypt/NativeCrypto.java

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

[InvalidLink] The reference `#SSL_shutdown` to a method doesn't resolve to anything. Is it misspelt, or is the parameter list not correct? See https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#JSSOR654 for documentation on how to form method links.
* lock.
*/
static native void ENGINE_SSL_shutdown(long ssl, NativeSsl ssl_holder,
Expand Down
1 change: 1 addition & 0 deletions common/src/main/java/org/conscrypt/OpenSSLProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ public OpenSSLProvider(String providerName) {

// We don't support SLH-DSA, because it's not clear which algorithm to use.
putSignatureImplClass("SLH-DSA-SHA2-128S", "OpenSslSignatureSlhDsa");
putSignatureImplClass("SLH-DSA-SHA2-128S-WITH-SHA384", "OpenSslSignatureHashSlhDsa$Sha384");

/* === SecureRandom === */
/*
Expand Down
133 changes: 133 additions & 0 deletions common/src/main/java/org/conscrypt/OpenSslSignatureHashSlhDsa.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (C) 2026 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;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;

/**
* Implements the JDK Signature interface needed for HashSLH-DSA-SHA2-128S signature generation and
* verification using BoringSSL.
*/
@Internal
public class OpenSslSignatureHashSlhDsa extends SignatureSpi {
private final int hashNid;
private OpenSSLMessageDigestJDK messageDigest;

/** The current OpenSSL key we're operating on. */
private OpenSslSlhDsaPrivateKey privateKey;

private OpenSslSlhDsaPublicKey publicKey;

protected OpenSslSignatureHashSlhDsa(int hashNid) {
this.hashNid = hashNid;
}

/** SHA-384 prehash SLH-DSA signature implementation. */
public static final class Sha384 extends OpenSslSignatureHashSlhDsa {
public Sha384() {
super(NativeConstants.NID_sha384);
}
}

private void resetDigest() {
try {
if (hashNid == NativeConstants.NID_sha384) {
messageDigest = new OpenSSLMessageDigestJDK.SHA384();
} else {
throw new IllegalStateException("Unsupported hash NID: " + hashNid);
}
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("Failed to create message digest", e);
}
}

@Override
protected void engineUpdate(byte input) throws SignatureException {
if (messageDigest == null) {
throw new SignatureException("Not initialized");
}
messageDigest.engineUpdate(input);
}

@Override
protected void engineUpdate(byte[] input, int offset, int len) throws SignatureException {
if (messageDigest == null) {
throw new SignatureException("Not initialized");
}
messageDigest.engineUpdate(input, offset, len);
}

@Override
// Deprecated in Java 9, but still required by SignatureSpi.
@SuppressWarnings("deprecation")
protected Object engineGetParameter(String param) {
return null;
}

@Override
@SuppressWarnings("PatternMatchingInstanceof")
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
if (!(privateKey instanceof OpenSslSlhDsaPrivateKey)) {
throw new InvalidKeyException("Must be OpenSslSlhDsaPrivateKey");
}
this.privateKey = (OpenSslSlhDsaPrivateKey) privateKey;
this.publicKey = null;
resetDigest();
}

@Override
@SuppressWarnings("PatternMatchingInstanceof")
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
if (!(publicKey instanceof OpenSslSlhDsaPublicKey)) {
throw new InvalidKeyException("Must be OpenSslSlhDsaPublicKey");
}
this.publicKey = (OpenSslSlhDsaPublicKey) publicKey;
this.privateKey = null;
resetDigest();
}

@Override
// Deprecated in Java 9, but still required by SignatureSpi.
@SuppressWarnings("deprecation")
protected void engineSetParameter(String param, Object value) {}

@Override
protected byte[] engineSign() throws SignatureException {
if (privateKey == null || messageDigest == null) {
throw new SignatureException("Not initialized for signing");
}
byte[] digest = messageDigest.engineDigest();
return NativeCrypto.SLHDSA_SHA2_128S_prehash_sign(
digest, digest.length, hashNid, privateKey.getRaw());
}

@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
if (publicKey == null || messageDigest == null) {
throw new SignatureException("Not initialized for verification");
}
byte[] digest = messageDigest.engineDigest();
int result =
NativeCrypto.SLHDSA_SHA2_128S_prehash_verify(
digest, digest.length, sigBytes, hashNid, publicKey.getRaw());
return result == 1;
}
}
Loading
Loading