From e222989dd622a979e031e447b6ceb969e3ef70b7 Mon Sep 17 00:00:00 2001 From: Miguel Aranda Date: Thu, 20 Aug 2026 19:26:38 +0000 Subject: [PATCH] Project import generated by Copybara. PiperOrigin-RevId: 967982777 --- .../jni/main/cpp/conscrypt/native_crypto.cc | 98 +++++++++++++ .../main/java/org/conscrypt/NativeCrypto.java | 8 ++ .../java/org/conscrypt/OpenSSLProvider.java | 1 + .../conscrypt/OpenSslSignatureHashSlhDsa.java | 133 +++++++++++++++++ .../test/java/org/conscrypt/SlhDsaTest.java | 136 +++++++++++++++++- .../java/security/SignatureTest.java | 1 + constants/src/gen/cpp/generate_constants.cc | 2 + .../java/org/conscrypt/NativeCryptoTest.java | 96 +++++++++++++ 8 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 common/src/main/java/org/conscrypt/OpenSslSignatureHashSlhDsa.java diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc index 3a6f0778d..3f4bd5eaf 100644 --- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc +++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc @@ -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 resultRef( + env, env->NewByteArray(static_cast(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(resultArray.get()), + reinterpret_cast(privateKeyArray.get()), + reinterpret_cast(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; @@ -3563,6 +3616,49 @@ static jint NativeCrypto_SLHDSA_SHA2_128S_verify(JNIEnv* env, jclass, jbyteArray return static_cast(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(sigArray.get()), sigArray.size(), + reinterpret_cast(publicKeyArray.get()), + reinterpret_cast(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(result); +} + static jboolean NativeCrypto_X25519(JNIEnv* env, jclass, jbyteArray outArray, jbyteArray privkeyArray, jbyteArray pubkeyArray) { CHECK_ERROR_QUEUE_ON_RETURN; @@ -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"), diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java index a7aadc4b9..0e28ec501 100644 --- a/common/src/main/java/org/conscrypt/NativeCrypto.java +++ b/common/src/main/java/org/conscrypt/NativeCrypto.java @@ -315,10 +315,18 @@ static native int ECDH_compute_key(byte[] out, int outOffset, NativeRef.EVP_PKEY // 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 diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java index c0d94ac0a..7bb249ebe 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java +++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java @@ -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 === */ /* diff --git a/common/src/main/java/org/conscrypt/OpenSslSignatureHashSlhDsa.java b/common/src/main/java/org/conscrypt/OpenSslSignatureHashSlhDsa.java new file mode 100644 index 000000000..b1ecb5a9a --- /dev/null +++ b/common/src/main/java/org/conscrypt/OpenSslSignatureHashSlhDsa.java @@ -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; + } +} diff --git a/common/src/test/java/org/conscrypt/SlhDsaTest.java b/common/src/test/java/org/conscrypt/SlhDsaTest.java index 728c7220b..f3641e65e 100644 --- a/common/src/test/java/org/conscrypt/SlhDsaTest.java +++ b/common/src/test/java/org/conscrypt/SlhDsaTest.java @@ -20,6 +20,7 @@ import static org.conscrypt.TestUtils.decodeHex; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -120,6 +121,136 @@ public void emptyMessage_works() throws Exception { assertTrue(signature.verify(sig2)); } + @Test + public void prehashSignAndVerify_works() throws Exception { + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); + KeyPair keyPair = keyGen.generateKeyPair(); + PrivateKey privateKey = keyPair.getPrivate(); + PublicKey publicKey = keyPair.getPublic(); + + byte[] msg = new byte[123]; + Signature ss = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + ss.initSign(privateKey); + ss.update(msg); + byte[] sig = ss.sign(); + assertEquals(7856, sig.length); + + Signature sv = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + sv.initVerify(publicKey); + sv.update(msg); + boolean verified = sv.verify(sig); + assertTrue(verified); + + // Modified message fails verification + byte[] modifiedMsg = msg.clone(); + modifiedMsg[0] ^= 1; + sv.initVerify(publicKey); + sv.update(modifiedMsg); + assertFalse(sv.verify(sig)); + + // Modified signature fails verification + byte[] modifiedSig = sig.clone(); + modifiedSig[0] ^= 1; + sv.initVerify(publicKey); + sv.update(msg); + assertFalse(sv.verify(modifiedSig)); + + // Wrong public key fails verification + KeyPair otherKeyPair = keyGen.generateKeyPair(); + sv.initVerify(otherKeyPair.getPublic()); + sv.update(msg); + assertFalse(sv.verify(sig)); + + // Raw SLH-DSA signature fails verification with prehash verifier + Signature rawSigner = Signature.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); + rawSigner.initSign(privateKey); + rawSigner.update(msg); + byte[] rawSig = rawSigner.sign(); + sv.initVerify(publicKey); + sv.update(msg); + assertFalse(sv.verify(rawSig)); + + // Prehash signature fails verification with raw verifier + Signature rawVerifier = Signature.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); + rawVerifier.initVerify(publicKey); + rawVerifier.update(msg); + assertFalse(rawVerifier.verify(sig)); + } + + @Test + public void prehashEmptyMessage_works() throws Exception { + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] emptyMessage = new byte[0]; + + Signature signature = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + + signature.initSign(keyPair.getPrivate()); + signature.update(emptyMessage); + byte[] sig = signature.sign(); + + signature.initVerify(keyPair.getPublic()); + signature.update(emptyMessage); + assertTrue(signature.verify(sig)); + + // Create a signature without calling update. + signature.initSign(keyPair.getPrivate()); + byte[] sig2 = signature.sign(); + + signature.initVerify(keyPair.getPublic()); + assertTrue(signature.verify(sig2)); + + signature.initVerify(keyPair.getPublic()); + signature.update(emptyMessage); + assertTrue(signature.verify(sig2)); + } + + @Test + public void prehashUpdateChunks_works() throws Exception { + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] data = new byte[256]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; + } + + // Sign all at once + Signature signer = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + signer.initSign(keyPair.getPrivate()); + signer.update(data); + byte[] sig = signer.sign(); + + // Sign in chunks + Signature chunkSigner = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + chunkSigner.initSign(keyPair.getPrivate()); + chunkSigner.update(data, 0, 100); + chunkSigner.update(data, 100, 100); + chunkSigner.update(data, 200, 56); + byte[] chunkSig = chunkSigner.sign(); + + // Verify in chunks + Signature verifier = + Signature.getInstance("SLH-DSA-SHA2-128S-WITH-SHA384", conscryptProvider); + verifier.initVerify(keyPair.getPublic()); + verifier.update(data, 0, 50); + verifier.update(data, 50, 206); + assertTrue(verifier.verify(sig)); + + verifier.initVerify(keyPair.getPublic()); + verifier.update(data); + assertTrue(verifier.verify(chunkSig)); + } + @Test public void plainSlhDsa_isNotSupported() throws Exception { assertThrows(NoSuchAlgorithmException.class, @@ -323,11 +454,6 @@ public void testVectors() throws Exception { throw new IllegalArgumentException("Unexpected algorithm: " + algorithm); } - if (algorithm.equals("SLH-DSA-SHA2-128S-WITH-SHA384")) { - // not yet implemented. - continue; - } - KeyFactory keyFactory = KeyFactory.getInstance("SLH-DSA-SHA2-128S", conscryptProvider); Signature signer = Signature.getInstance(algorithm, conscryptProvider); diff --git a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java index 8291b196a..8afa2d5e9 100644 --- a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java +++ b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java @@ -110,6 +110,7 @@ public void test_getInstance() throws Exception { // SLH-DSA-SHA2-128S is skipped because it doesn't yet support getFormat() and // getEncoded(). .skipAlgorithm("SLH-DSA-SHA2-128S") + .skipAlgorithm("SLH-DSA-SHA2-128S-WITH-SHA384") .skipAlgorithm("HSS/LMS") .run((provider, algorithm) -> { KeyPair kp = keyPair(algorithm); diff --git a/constants/src/gen/cpp/generate_constants.cc b/constants/src/gen/cpp/generate_constants.cc index 7ea811031..6beea101d 100644 --- a/constants/src/gen/cpp/generate_constants.cc +++ b/constants/src/gen/cpp/generate_constants.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ int main(int argc, char** argv) { CONST(NID_secp521r1); CONST(NID_X25519MLKEM768); CONST(NID_ML_KEM_1024); + CONST(NID_sha384); CONST(RSA_PKCS1_PADDING); CONST(RSA_NO_PADDING); diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java index 3d013fb33..027667897 100644 --- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java +++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java @@ -76,6 +76,7 @@ import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; +import java.security.MessageDigest; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -4933,6 +4934,101 @@ public void test_slhdsa_sha2_128s_works() throws Exception { () -> NativeCrypto.SLHDSA_SHA2_128S_generate_key(publicKeyTooLong, privateKey)); } + @Test + public void test_slhdsa_sha2_128s_prehash_works() throws Exception { + byte[] publicKey = new byte[32]; + byte[] privateKey = new byte[64]; + NativeCrypto.SLHDSA_SHA2_128S_generate_key(publicKey, privateKey); + + byte[] msg = decodeHex("AB"); + MessageDigest md = MessageDigest.getInstance("SHA-384"); + byte[] digest = md.digest(msg); + assertEquals(48, digest.length); + int hashNid = NativeConstants.NID_sha384; + + byte[] signature = + NativeCrypto.SLHDSA_SHA2_128S_prehash_sign(digest, digest.length, hashNid, privateKey); + assertEquals(7856, signature.length); + + int result = + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, signature, hashNid, publicKey); + assertEquals(1, result); + + // digest buffer is larger than digest + byte[] digestBuffer = Arrays.copyOf(digest, digest.length + 42); + assertEquals( + 1, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digestBuffer, digest.length, signature, hashNid, publicKey)); + + // digest too short + assertEquals( + 0, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length - 1, signature, hashNid, publicKey)); + + byte[] signatureTooShort = Arrays.copyOf(signature, signature.length - 1); + assertEquals( + 0, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, signatureTooShort, hashNid, publicKey)); + + byte[] signatureTooLong = Arrays.copyOf(signature, signature.length + 1); + assertEquals( + 0, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, signatureTooLong, hashNid, publicKey)); + + byte[] modifiedSignature = signature.clone(); + modifiedSignature[0] = (byte) (modifiedSignature[0] ^ 0x01); + assertEquals( + 0, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, modifiedSignature, hashNid, publicKey)); + + byte[] modifiedDigest = digest.clone(); + modifiedDigest[0] = (byte) (modifiedDigest[0] ^ 0x01); + assertEquals( + 0, + NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + modifiedDigest, modifiedDigest.length, signature, hashNid, publicKey)); + + int invalidDigestLen = digest.length + 1; + assertThrows( + RuntimeException.class, + () -> NativeCrypto.SLHDSA_SHA2_128S_prehash_sign(digest, invalidDigestLen, hashNid, privateKey)); + assertThrows( + RuntimeException.class, + () + -> NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, invalidDigestLen, signature, hashNid, publicKey)); + + byte[] privateKeyTooShort = Arrays.copyOf(privateKey, privateKey.length - 1); + assertThrows( + RuntimeException.class, + () -> NativeCrypto.SLHDSA_SHA2_128S_prehash_sign(digest, digest.length, hashNid, privateKeyTooShort)); + + byte[] privateKeyTooLong = Arrays.copyOf(privateKey, privateKey.length + 1); + assertThrows( + RuntimeException.class, + () -> NativeCrypto.SLHDSA_SHA2_128S_prehash_sign(digest, digest.length, hashNid, privateKeyTooLong)); + + byte[] publicKeyTooShort = Arrays.copyOf(publicKey, publicKey.length - 1); + assertThrows( + RuntimeException.class, + () + -> NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, signature, hashNid, publicKeyTooShort)); + + byte[] publicKeyTooLong = Arrays.copyOf(publicKey, publicKey.length + 1); + assertThrows( + RuntimeException.class, + () + -> NativeCrypto.SLHDSA_SHA2_128S_prehash_verify( + digest, digest.length, signature, hashNid, publicKeyTooLong)); + } + @Test public void x25519_testVector1FromRfc7748_works() throws Exception { // Test vector from RFC 7748, Section 6.1 (Alice's side)