From f1ef8ecac089b2efe3ffec11ffb5eff5fde29f97 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 10:31:52 -0600 Subject: [PATCH 1/8] JCE: fix secp521r1 ECParameterSpec recognition and add regression test --- .../provider/jce/WolfCryptECKeyFactory.java | 7 +- .../jce/WolfCryptECParameterSpec.java | 105 +--------------- .../provider/jce/WolfCryptKeyAgreement.java | 21 +++- .../provider/jce/WolfCryptSignature.java | 24 ++++ src/main/java/com/wolfssl/wolfcrypt/Ecc.java | 113 ++++++++++++++++-- .../com/wolfssl/wolfcrypt/test/EccTest.java | 34 ++++++ 6 files changed, 189 insertions(+), 115 deletions(-) diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptECKeyFactory.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptECKeyFactory.java index 731a26bf..75a2f28d 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptECKeyFactory.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptECKeyFactory.java @@ -34,6 +34,7 @@ import java.security.spec.X509EncodedKeySpec; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; +import java.security.spec.ECParameterSpec; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.InvalidAlgorithmParameterException; @@ -442,7 +443,8 @@ else if (bytes.length < expectedSz) { * Convert BigInteger private key to byte[]. * * @param privateValue private key BigInteger - * @param curveName curve name for size validation + * @param curveName curve name for size validation, used to get + * expected byte array size * * @return properly formatted byte array for wolfCrypt import * @@ -451,7 +453,8 @@ else if (bytes.length < expectedSz) { private byte[] convertPrivateValueToBytes(BigInteger privateValue, String curveName) throws InvalidKeySpecException { - /* Validate private key is within valid range (1 <= d < order) */ + /* Validate private key is positive. Other validation done at time of + * use to match Sun behavior. */ if (privateValue.signum() <= 0) { throw new InvalidKeySpecException( "Private key value must be positive"); diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptECParameterSpec.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptECParameterSpec.java index e9c4d72f..b697646a 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptECParameterSpec.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptECParameterSpec.java @@ -283,15 +283,14 @@ public static String getCurveName(ECParameterSpec paramSpec) return null; } - /* Check if this is our ECParameterSpec with stored curve name */ + /* Check if this is a wolfJCE ECParameterSpec with stored curve name */ if (paramSpec instanceof WolfCryptECParameterSpec) { - WolfCryptECParameterSpec enhanced = + WolfCryptECParameterSpec wolfSpec = (WolfCryptECParameterSpec) paramSpec; - return enhanced.getStoredCurveName(); + return wolfSpec.getStoredCurveName(); } - /* Fall back to parameter-based identification for external - * ECParameterSpec objects */ + /* Use parameters to identify external ECParameterSpec objects */ return Ecc.getCurveName(paramSpec); } @@ -306,99 +305,6 @@ public static boolean hasStoredCurveName(ECParameterSpec paramSpec) { return paramSpec instanceof WolfCryptECParameterSpec; } - /** - * Identify ECC curve name by comparing ECParameterSpec parameters against - * all curves supported by wolfCrypt. - * - * @param paramSpec ECParameterSpec to identify - * - * @return curve name string, or null if no match found - */ - private static String identifyCurveByParameters(ECParameterSpec spec) { - - int targetCofactor; - int cofactor; - - if (spec == null) { - return null; - } - - try { - /* Get all supported curves from wolfCrypt */ - String[] supportedCurves = Ecc.getAllSupportedCurves(); - - /* Extract parameters from the input ECParameterSpec */ - EllipticCurve curve = spec.getCurve(); - if (!(curve.getField() instanceof ECFieldFp)) { - return null; /* Only support prime fields */ - } - - ECFieldFp field = (ECFieldFp) curve.getField(); - BigInteger targetP = field.getP(); - BigInteger targetA = curve.getA(); - BigInteger targetB = curve.getB(); - BigInteger targetN = spec.getOrder(); - ECPoint targetG = spec.getGenerator(); - targetCofactor = spec.getCofactor(); - - /* Compare against each supported curve */ - for (String curveName : supportedCurves) { - try { - String[] curveParams = Ecc.getCurveParameters(curveName); - - /* Parse wolfCrypt curve parameters */ - BigInteger p = new BigInteger(curveParams[0], 16); - - /* Field size check for early exit on curve mismatch */ - if (targetP.bitLength() != p.bitLength()) { - continue; - } - - /* Prime field check for early exit on curve mismatch */ - if (!targetP.equals(p)) { - continue; - } - - BigInteger a = new BigInteger(curveParams[1], 16); - BigInteger b = new BigInteger(curveParams[2], 16); - BigInteger n = new BigInteger(curveParams[3], 16); - - cofactor = Integer.parseInt(curveParams[6]); - if (targetCofactor != cofactor) { - continue; - } - - /* Compare a, b, n, and generator */ - if (targetA.equals(a) && targetB.equals(b) && - targetN.equals(n)) { - - BigInteger gx = new BigInteger(curveParams[4], 16); - BigInteger gy = new BigInteger(curveParams[5], 16); - - if (targetG.getAffineX().equals(gx) && - targetG.getAffineY().equals(gy)) { - - log("identified curve by parameter matching: " + - curveName); - return curveName; - } - } - - } catch (Exception e) { - /* Continue to next curve */ - continue; - } - } - - log("no curve match found for ECParameterSpec with field size " + - targetP.bitLength()); - return null; - - } catch (Exception e) { - log("error during curve parameter matching: " + e.getMessage()); - return null; - } - } /** * Validate if given ECParameterSpec is supported by wolfCrypt. @@ -424,10 +330,11 @@ public static void validateParameters(ECParameterSpec spec) } catch (InvalidAlgorithmParameterException e) { throw new IllegalArgumentException( "ECParameterSpec curve not supported by wolfCrypt: " + - e.getMessage()); + e.getMessage(), e); } } + /** * Normalize wolfCrypt curve names to standard format expected by * system AlgorithmParameters. diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java index 8afa9de3..d8627ff8 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyAgreement.java @@ -22,6 +22,7 @@ package com.wolfssl.provider.jce; import java.util.Arrays; +import java.math.BigInteger; import javax.crypto.KeyAgreementSpi; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; @@ -457,6 +458,9 @@ private void wcInitECDHParams(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { ECPrivateKey ecKey; + BigInteger privateValue = null; + BigInteger order = null; + ECParameterSpec ecParams = null; if (!(key instanceof ECPrivateKey)) { throw new InvalidKeyException( @@ -464,12 +468,27 @@ private void wcInitECDHParams(Key key, AlgorithmParameterSpec params) } ecKey = (ECPrivateKey)key; + /* Validate EC private key range */ + privateValue = ecKey.getS(); + ecParams = ecKey.getParams(); + + if (privateValue.signum() <= 0) { + throw new InvalidKeyException( + "EC private key value must be positive"); + } + + order = ecParams.getOrder(); + if (privateValue.compareTo(order) >= 0) { + throw new InvalidKeyException( + "EC private key value must be less than curve order"); + } + if (params != null) { /* try to extract curve info from AlgorithmParameterSpec */ getCurveFromSpec(params); } else { - /* otherwise, try to import params from key */ + /* try to import params from key */ ECParameterSpec spec = ecKey.getParams(); getCurveFromSpec(spec); } diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java index 2b14f2b5..729c0840 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java @@ -29,6 +29,9 @@ import java.security.interfaces.RSAPublicKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; + +import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.SignatureException; @@ -370,6 +373,27 @@ protected synchronized void engineInitSign(PrivateKey privateKey) throw new InvalidKeyException("Key is not of type ECPrivateKey"); } + /* If ECDSA key, validate EC private key range. Validating here to + * match Sun behavior. */ + if ((this.keyType == KeyType.WC_ECDSA) && + (privateKey instanceof ECPrivateKey)) { + + ECPrivateKey ecPrivKey = (ECPrivateKey) privateKey; + BigInteger privateValue = ecPrivKey.getS(); + ECParameterSpec ecParams = ecPrivKey.getParams(); + + if (privateValue.signum() <= 0) { + throw new InvalidKeyException( + "EC private key value must be positive"); + } + + BigInteger order = ecParams.getOrder(); + if (privateValue.compareTo(order) >= 0) { + throw new InvalidKeyException( + "EC private key value must be less than curve order"); + } + } + /* get encoded key, returns PKCS#8 formatted private key */ encodedKey = privateKey.getEncoded(); if (encodedKey == null) diff --git a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java index d09ba325..11917cd1 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java @@ -637,6 +637,56 @@ public static byte[] bigIntToByteArrayNoLeadingZeros(BigInteger value) { return bytes; } + /** + * Convert BigInteger to byte array with fixed size, padding with leading + * zeros if necessary. This ensures consistent byte array lengths for + * ECC curve parameter matching in native wolfCrypt. + * + * @param value the BigInteger to convert + * @param fieldSizeBytes expected byte array size + * + * @return byte array representation padded to fieldSizeBytes + * + * @throws IllegalArgumentException if BigInteger value is too large + * to fit into expected fieldSizeBytes size + */ + public static byte[] bigIntToFixedSizeByteArray(BigInteger value, + int fieldSizeBytes) throws IllegalArgumentException { + + byte[] bytes = null; + + if (value == null) { + return null; + } + + bytes = value.toByteArray(); + + /* If we have the exact size, use as is */ + if (bytes.length == fieldSizeBytes) { + return bytes; + } + + /* If too long (should have leading zero for sign), remove it */ + if ((bytes.length == fieldSizeBytes + 1) && (bytes[0] == 0)) { + byte[] result = new byte[fieldSizeBytes]; + System.arraycopy(bytes, 1, result, 0, fieldSizeBytes); + return result; + } + + /* If too short, pad with leading zeros */ + if (bytes.length < fieldSizeBytes) { + byte[] result = new byte[fieldSizeBytes]; + System.arraycopy(bytes, 0, result, fieldSizeBytes - bytes.length, + bytes.length); + return result; + } + + /* Should not happen for valid ECC parameters */ + throw new IllegalArgumentException( + "Parameter byte array too large: " + bytes.length + + " > " + fieldSizeBytes); + } + /** * Get ECC curve name from ECParameterSpec * @@ -653,29 +703,66 @@ public static String getCurveName(ECParameterSpec spec) throws WolfCryptException, InvalidAlgorithmParameterException { int curve_id; + int fieldSize; + int expectedByteSize; + int wolfCryptByteSize; + ECFieldFp field = null; + EllipticCurve curve = null; + byte[] pBytes = null; + byte[] aBytes = null; + byte[] bBytes = null; + byte[] orderBytes = null; + byte[] gxBytes = null; + byte[] gyBytes = null; /* Ecc object doesn't need to be initialied before call */ if (!(spec.getCurve().getField() instanceof ECFieldFp)) { throw new InvalidAlgorithmParameterException( "Currently only ECFieldFp fields supported"); } - ECFieldFp field = (ECFieldFp)spec.getCurve().getField(); - EllipticCurve curve = spec.getCurve(); + + field = (ECFieldFp)spec.getCurve().getField(); + curve = spec.getCurve(); /* Convert BigIntegers to byte arrays for native wolfCrypt. * Remove extra leading zero bytes that BigInteger.toByteArray() * might add. */ - curve_id = wc_ecc_get_curve_id_from_params( - field.getFieldSize(), - bigIntToByteArrayNoLeadingZeros(field.getP()), - bigIntToByteArrayNoLeadingZeros(curve.getA()), - bigIntToByteArrayNoLeadingZeros(curve.getB()), - bigIntToByteArrayNoLeadingZeros(spec.getOrder()), - bigIntToByteArrayNoLeadingZeros( - spec.getGenerator().getAffineX()), - bigIntToByteArrayNoLeadingZeros( - spec.getGenerator().getAffineY()), - spec.getCofactor()); + pBytes = bigIntToByteArrayNoLeadingZeros(field.getP()); + aBytes = bigIntToByteArrayNoLeadingZeros(curve.getA()); + bBytes = bigIntToByteArrayNoLeadingZeros(curve.getB()); + orderBytes = bigIntToByteArrayNoLeadingZeros(spec.getOrder()); + gxBytes = bigIntToByteArrayNoLeadingZeros( + spec.getGenerator().getAffineX()); + gyBytes = bigIntToByteArrayNoLeadingZeros( + spec.getGenerator().getAffineY()); + + /* Calculate correct field size for wolfCrypt curve lookup. + * wolfCrypt uses curveSz = (fieldSize + 1) / 8 to determine expected + * parameter byte length, but this can cause mismatches for curves like + * secp521r1 where field size is 521 bits but parameters need 66 bytes. + * We need to adjust the field size to ensure wolfCrypt calculates the + * correct byte size for parameter matching. */ + fieldSize = field.getFieldSize(); + + /* Calculate how many bytes are actually needed for this field size, + * rounding up to the next byte boundary. */ + expectedByteSize = (field.getFieldSize() + 7) / 8; + + /* Calculate what wolfCrypt will compute with current field size. + * wolfCrypt uses "+ 1" instead of "+ 7" for rounding. */ + wolfCryptByteSize = (fieldSize + 1) / 8; + + if (wolfCryptByteSize < expectedByteSize) { + /* Adjust field size so wolfCrypt calculates the correct byte size. + * We need wolfCrypt to compute expectedByteSize, so: + * (adjustedFieldSize + 1) / 8 = expectedByteSize + * Therefore: adjustedFieldSize = expectedByteSize * 8 - 1 + * Ex: 66 * 8 - 1 = 527, then (527 + 1) / 8 = 66 bytes */ + fieldSize = expectedByteSize * 8 - 1; + } + + curve_id = wc_ecc_get_curve_id_from_params(fieldSize, pBytes, aBytes, + bBytes, orderBytes, gxBytes, gyBytes, spec.getCofactor()); if (curve_id == -1) { /* ECC_CURVE_INVALID */ diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/EccTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/EccTest.java index 2a95697e..94faa712 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/EccTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/EccTest.java @@ -330,6 +330,40 @@ public void getEccCurveNameFromSpec() assertEquals(curveName, "SECP256R1"); } + @Test + public void getEccCurveNameFromSpec_Secp521r1() + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + + /* Check if secp521r1 is supported in native wolfSSL */ + boolean secp521r1Supported = false; + String[] supportedCurves = Ecc.getAllSupportedCurves(); + for (String curve : supportedCurves) { + if ("SECP521R1".equalsIgnoreCase(curve)) { + secp521r1Supported = true; + break; + } + } + + if (!secp521r1Supported) { + /* Curve not compiled in, skip */ + return; + } + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec genSpec = new ECGenParameterSpec("secp521r1"); + kpg.initialize(genSpec); + + KeyPair pair = kpg.genKeyPair(); + ECPrivateKey priv = (ECPrivateKey)pair.getPrivate(); + + ECParameterSpec spec = priv.getParams(); + + String curveName = Ecc.getCurveName(spec); + + assertEquals("Should recognize secp521r1 from JDK ECParameterSpec", + "SECP521R1", curveName); + } + @Test public void threadedEccSharedSecretTest() throws InterruptedException { From be868538f47a198b879e28435d8909b4d09d5ac7 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 11:59:44 -0600 Subject: [PATCH 2/8] JCE: add Signature implementation for SHA256withECDSAinP1363Format and SHA384withECDSAinP1363Format, along with tests --- jni/include/com_wolfssl_wolfcrypt_Ecc.h | 24 + jni/jni_ecc.c | 258 +++++++++++ .../provider/jce/WolfCryptProvider.java | 6 + .../provider/jce/WolfCryptSignature.java | 241 +++++++++- src/main/java/com/wolfssl/wolfcrypt/Ecc.java | 78 ++++ .../jce/test/WolfCryptSignatureP1363Test.java | 414 ++++++++++++++++++ .../provider/jce/test/WolfJCETestSuite.java | 1 + 7 files changed, 1021 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java diff --git a/jni/include/com_wolfssl_wolfcrypt_Ecc.h b/jni/include/com_wolfssl_wolfcrypt_Ecc.h index 1989ed50..349d329a 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Ecc.h +++ b/jni/include/com_wolfssl_wolfcrypt_Ecc.h @@ -233,6 +233,30 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1import_1public_1r JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1get_1curve_1id (JNIEnv *, jobject); +/* + * Class: com_wolfssl_wolfcrypt_Ecc + * Method: wc_ecc_size + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1size + (JNIEnv *, jobject); + +/* + * Class: com_wolfssl_wolfcrypt_Ecc + * Method: wc_ecc_sig_to_rs_raw + * Signature: ([B)[[B + */ +JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1sig_1to_1rs_1raw + (JNIEnv *, jobject, jbyteArray); + +/* + * Class: com_wolfssl_wolfcrypt_Ecc + * Method: wc_ecc_rs_raw_to_sig + * Signature: ([B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1rs_1raw_1to_1sig + (JNIEnv *, jobject, jbyteArray, jbyteArray); + #ifdef __cplusplus } #endif diff --git a/jni/jni_ecc.c b/jni/jni_ecc.c index 58b2ff17..01a61159 100644 --- a/jni/jni_ecc.c +++ b/jni/jni_ecc.c @@ -1769,3 +1769,261 @@ JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1get_1all_ return result; } +JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1size( + JNIEnv* env, jobject this) +{ + jint ret = 0; + +#ifdef HAVE_ECC + ecc_key* ecc = NULL; + + ecc = (ecc_key*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return 0; + } + + if (ecc == NULL) { + throwWolfCryptException(env, "ECC key is NULL"); + return 0; + } + + ret = wc_ecc_size(ecc); + + LogStr("wc_ecc_size(ecc) = %d\n", ret); + +#else + (void)this; + throwNotCompiledInException(env); +#endif + + return ret; +} + +/* + * Convert DER-encoded ECDSA signature to raw {r,s} values. + * Used for IEEE P1363 signature format conversion. + * + * Returns byte[][] where [0] is r value and [1] is s value, or NULL on error. + */ +JNIEXPORT jobjectArray JNICALL +Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1sig_1to_1rs_1raw( + JNIEnv* env, jobject this, jbyteArray signature_object) +{ + jobjectArray result = NULL; + +#if defined(HAVE_ECC) && defined(HAVE_ECC_SIGN) + int ret = 0; + ecc_key* ecc = NULL; + byte* signature = NULL; + byte* r = NULL; + byte* s = NULL; + word32 signatureSz = 0; + word32 rSz = 0; + word32 sSz = 0; + word32 keySz = 0; + jbyteArray rArray = NULL; + jbyteArray sArray = NULL; + jclass byteArrayClass = NULL; + + ecc = (ecc_key*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return NULL; + } + + if (ecc == NULL) { + throwWolfCryptException(env, "ECC key is NULL"); + return NULL; + } + + signature = getByteArray(env, signature_object); + signatureSz = getByteArrayLength(env, signature_object); + + if (signature == NULL) { + throwWolfCryptException(env, "Input signature is NULL"); + return NULL; + } + + /* Get key size to determine buffer sizes for r and s */ + keySz = wc_ecc_size(ecc); + if (keySz == 0) { + releaseByteArray(env, signature_object, signature, JNI_ABORT); + throwWolfCryptException(env, "Invalid ECC key size"); + return NULL; + } + + /* Allocate buffers for r and s (should be at most key size) */ + rSz = sSz = keySz; + r = (byte*)XMALLOC(rSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + s = (byte*)XMALLOC(sSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + if (r == NULL || s == NULL) { + ret = MEMORY_E; + + } else { + XMEMSET(r, 0, rSz); + XMEMSET(s, 0, sSz); + } + + if (ret == 0) { + ret = wc_ecc_sig_to_rs(signature, signatureSz, r, &rSz, s, &sSz); + } + + if (ret == 0) { + byteArrayClass = (*env)->FindClass(env, "[B"); + if (byteArrayClass == NULL) { + ret = MEMORY_E; + } else { + result = (*env)->NewObjectArray(env, 2, byteArrayClass, NULL); + } + } + + if ((ret == 0) && (result != NULL)) { + rArray = (*env)->NewByteArray(env, rSz); + sArray = (*env)->NewByteArray(env, sSz); + + if ((rArray != NULL) && (sArray != NULL)) { + (*env)->SetByteArrayRegion(env, rArray, 0, rSz, (const jbyte*)r); + (*env)->SetByteArrayRegion(env, sArray, 0, sSz, (const jbyte*)s); + (*env)->SetObjectArrayElement(env, result, 0, rArray); + (*env)->SetObjectArrayElement(env, result, 1, sArray); + } else { + ret = MEMORY_E; + } + } + + /* Clean up */ + if (r != NULL) { + XMEMSET(r, 0, keySz); + XFREE(r, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + if (s != NULL) { + XMEMSET(s, 0, keySz); + XFREE(s, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + releaseByteArray(env, signature_object, signature, JNI_ABORT); + + if (ret != 0) { + throwWolfCryptExceptionFromError(env, ret); + return NULL; + } + + LogStr("wc_ecc_sig_to_rs(sig, sigLen, r, &rLen, s, &sLen) = %d\n", ret); + +#else + (void)this; + (void)signature_object; + throwNotCompiledInException(env); +#endif + + return result; +} + +/* + * Convert raw {r,s} values to DER-encoded ECDSA signature. + * Used for IEEE P1363 signature format conversion. + * + * Returns DER-encoded signature byte array, or NULL on error. + */ +JNIEXPORT jbyteArray JNICALL +Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1rs_1raw_1to_1sig( + JNIEnv* env, jobject this, jbyteArray r_object, jbyteArray s_object) +{ + jbyteArray result = NULL; + +#if defined(HAVE_ECC) && defined(HAVE_ECC_SIGN) + int ret = 0; + ecc_key* ecc = NULL; + byte* r = NULL; + byte* s = NULL; + byte* signature = NULL; + word32 rSz = 0; + word32 sSz = 0; + word32 signatureSz = 0; + word32 signatureBufSz = 0; + + ecc = (ecc_key*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return NULL; + } + + if (ecc == NULL) { + throwWolfCryptException(env, "ECC key is NULL"); + return NULL; + } + + r = getByteArray(env, r_object); + rSz = getByteArrayLength(env, r_object); + s = getByteArray(env, s_object); + sSz = getByteArrayLength(env, s_object); + + if (r == NULL || s == NULL) { + LogStr("Input r or s is NULL\n"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Estimate signature size based on ECC key size */ + signatureBufSz = wc_ecc_sig_size(ecc); + if (signatureBufSz == 0) { + LogStr("Invalid signature size estimate\n"); + ret = BAD_FUNC_ARG; + } + } + + if (ret == 0) { + signatureSz = signatureBufSz; + signature = (byte*)XMALLOC(signatureSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (signature == NULL) { + ret = MEMORY_E; + } else { + XMEMSET(signature, 0, signatureSz); + } + } + + if (ret == 0) { + ret = wc_ecc_rs_raw_to_sig(r, rSz, s, sSz, signature, &signatureSz); + } + + if (ret == 0) { + result = (*env)->NewByteArray(env, signatureSz); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, signatureSz, + (const jbyte*)signature); + } else { + ret = MEMORY_E; + } + } + + LogStr("wc_ecc_rs_raw_to_sig(r, rSz, s, sSz, sig, &sigLen) = %d\n", ret); + + if (signature != NULL) { + XMEMSET(signature, 0, signatureBufSz); + XFREE(signature, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + if (r != NULL) { + releaseByteArray(env, r_object, r, JNI_ABORT); + } + if (s != NULL) { + releaseByteArray(env, s_object, s, JNI_ABORT); + } + + if (ret != 0) { + throwWolfCryptExceptionFromError(env, ret); + return NULL; + } + +#else + (void)this; + (void)r_object; + (void)s_object; + throwNotCompiledInException(env); +#endif /* HAVE_ECC && HAVE_ECC_SIGN */ + + return result; +} + diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index d5098330..869e3a62 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -147,6 +147,9 @@ private void registerServices() { put("Signature.SHA256withECDSA", "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA256wECDSA"); put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA"); + /* IEEE P1363 format ECDSA */ + put("Signature.SHA256withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA256wECDSAP1363"); } if (FeatureDetect.Sha384Enabled()) { put("Signature.SHA384withRSA", @@ -154,6 +157,9 @@ private void registerServices() { put("Signature.SHA384withECDSA", "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA384wECDSA"); put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA"); + /* IEEE P1363 format ECDSA */ + put("Signature.SHA384withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA384wECDSAP1363"); } if (FeatureDetect.Sha512Enabled()) { put("Signature.SHA512withRSA", diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java index 729c0840..f93cd255 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java @@ -102,7 +102,9 @@ enum PaddingType { /* internal key objects */ private Rsa rsa = null; - private Ecc ecc = null; + + /** Internal Ecc object */ + protected Ecc ecc = null; /* internal hash objects */ private Md5 md5 = null; @@ -1707,4 +1709,241 @@ public wcSHA512wRSAPSS() throws NoSuchAlgorithmException { PaddingType.WC_RSA_PSS); } } + + /** + * Get the component size in bytes for P1363 format based on curve. + * This is calculated as ceil(curve_bits / 8). + * + * @param ecc ECC key to get curve size from + * + * @return component size in bytes for P1363 format + * + * @throws SignatureException if curve size cannot be determined + */ + private static int getP1363ComponentSize(Ecc ecc) + throws SignatureException { + + int componentSize; + + try { + /* Get curve size in bytes, gives us the component size */ + componentSize = ecc.getCurveSizeByKey(); + if (componentSize <= 0) { + throw new SignatureException( + "Invalid curve size for P1363 format"); + } + + return componentSize; + + } catch (Exception e) { + throw new SignatureException("Failed to get curve size: " + + e.getMessage()); + } + } + + /** + * Convert DER-encoded signature to P1363 format (r|s). + * + * @param derSignature DER-encoded ECDSA signature + * @param ecc ECC key for determining component size + * + * @return P1363 format signature (r|s concatenated) + * + * @throws SignatureException if conversion fails + */ + private static byte[] derToP1363(byte[] derSignature, Ecc ecc) + throws SignatureException { + + int componentSize; + int rOffset, rCopyLen; + int sOffset, sCopyLen; + byte[] r; + byte[] s; + byte[] p1363Signature; + + try { + /* Extract raw r,s from DER signature via JNI */ + byte[][] rs = ecc.sigToRsRaw(derSignature); + if (rs == null || rs.length != 2 || + rs[0] == null || rs[1] == null) { + throw new SignatureException( + "Failed to extract r,s from DER signature"); + } + + r = rs[0]; + s = rs[1]; + + /* Get component size for P1363 format */ + componentSize = getP1363ComponentSize(ecc); + + /* Create P1363 format: r | s with fixed sizes */ + p1363Signature = new byte[componentSize * 2]; + + /* Pad r to component size (big-endian, so pad on left) */ + rOffset = Math.max(0, componentSize - r.length); + rCopyLen = Math.min(r.length, componentSize); + System.arraycopy(r, Math.max(0, r.length - componentSize), + p1363Signature, rOffset, rCopyLen); + + /* Pad s to component size (big-endian, so pad on left) */ + sOffset = componentSize + Math.max(0, componentSize - s.length); + sCopyLen = Math.min(s.length, componentSize); + System.arraycopy(s, Math.max(0, s.length - componentSize), + p1363Signature, sOffset, sCopyLen); + + return p1363Signature; + + } catch (Exception e) { + throw new SignatureException("DER to P1363 conversion failed: " + + e.getMessage()); + } + } + + /** + * Convert P1363 format signature (r|s) to DER-encoded format. + * + * @param p1363Signature P1363 format signature + * @param ecc ECC key for determining component size + * + * @return DER-encoded ECDSA signature + * + * @throws SignatureException if conversion fails + */ + private static byte[] p1363ToDer(byte[] p1363Signature, Ecc ecc) + throws SignatureException { + + int componentSize; + byte[] r; + byte[] s; + + try { + /* Get component size for P1363 format */ + componentSize = getP1363ComponentSize(ecc); + + /* Validate P1363 signature size */ + if (p1363Signature.length != componentSize * 2) { + throw new SignatureException( + "Invalid P1363 signature size: expected " + + (componentSize * 2) + ", got " + p1363Signature.length); + } + + /* Extract r and s components */ + r = new byte[componentSize]; + s = new byte[componentSize]; + + System.arraycopy(p1363Signature, 0, r, 0, componentSize); + System.arraycopy(p1363Signature, componentSize, s, 0, + componentSize); + + /* Create DER signature from raw r,s */ + return ecc.rsRawToSig(r, s); + + } catch (Exception e) { + throw new SignatureException("P1363 to DER conversion failed: " + + e.getMessage()); + } + } + + /** + * wolfJCE SHA256withECDSAinP1363Format signature class + */ + public static final class wcSHA256wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA256wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA256wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA256); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } + + /** + * wolfJCE SHA384withECDSAinP1363Format signature class + */ + public static final class wcSHA384wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA384wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA384wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA384); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } } + diff --git a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java index 11917cd1..29fb2878 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Ecc.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Ecc.java @@ -147,6 +147,9 @@ private native void wc_ecc_import_private_raw(byte[] privateKey, private native void wc_ecc_import_public_raw(byte[] xCoord, byte[] yCoord, String curveName); private native int wc_ecc_get_curve_id(); + private native int wc_ecc_size(); + private native byte[][] wc_ecc_sig_to_rs_raw(byte[] signature); + private native byte[] wc_ecc_rs_raw_to_sig(byte[] r, byte[] s); /** * Internal helper method to initialize object if/when needed. @@ -900,6 +903,28 @@ public synchronized int getCurveId() } } + /** + * Get curve size in bytes for this ECC key. + * This is the size needed for each component (r or s) in P1363 format. + * + * @return curve size in bytes + * + * @throws WolfCryptException if native operation fails + * @throws IllegalStateException if key has not been set, if object + * fails to initialize, or if releaseNativeStruct() has been + * called and object has been released. + */ + public synchronized int getCurveSizeByKey() + throws WolfCryptException, IllegalStateException { + + checkStateAndInitialize(); + throwIfKeyNotLoaded(); + + synchronized (pointerLock) { + return wc_ecc_size(); + } + } + /** * Get ECC curve parameters for specified curve name. * @@ -940,5 +965,58 @@ public static String[] getCurveParameters(String curveName) public static String[] getAllSupportedCurves() throws WolfCryptException { return wc_ecc_get_all_curve_names(); } + + /** + * Convert DER-encoded ECDSA signature to raw r,s values. + * Used for IEEE P1363 signature format conversion. + * + * @param signature DER-encoded ECDSA signature + * + * @return byte array where [0] is r value and [1] is s value + * + * @throws WolfCryptException if signature conversion fails + * @throws IllegalStateException if object has been freed + */ + public synchronized byte[][] sigToRsRaw(byte[] signature) + throws WolfCryptException { + + checkStateAndInitialize(); + + if (signature == null) { + throw new IllegalArgumentException( + "Signature cannot be null"); + } + + synchronized (pointerLock) { + return wc_ecc_sig_to_rs_raw(signature); + } + } + + /** + * Convert raw r,s values to DER-encoded ECDSA signature. + * Used for IEEE P1363 signature format conversion. + * + * @param r raw r value + * @param s raw s value + * + * @return DER-encoded ECDSA signature + * + * @throws WolfCryptException if signature conversion fails + * @throws IllegalStateException if object has been freed + */ + public synchronized byte[] rsRawToSig(byte[] r, byte[] s) + throws WolfCryptException { + + checkStateAndInitialize(); + + if (r == null || s == null) { + throw new IllegalArgumentException( + "r and s values cannot be null"); + } + + synchronized (pointerLock) { + return wc_ecc_rs_raw_to_sig(r, s); + } + } } diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java new file mode 100644 index 00000000..389d645d --- /dev/null +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java @@ -0,0 +1,414 @@ +/* WolfCryptSignatureP1363Test.java + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +package com.wolfssl.provider.jce.test; + +import org.junit.Test; +import org.junit.BeforeClass; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.util.ArrayList; +import java.security.Security; +import java.security.Provider; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Signature; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; + +import com.wolfssl.wolfcrypt.Ecc; +import com.wolfssl.provider.jce.WolfCryptProvider; + +/** + * JUnit4 test cases for WolfCryptSignature P1363 format support + */ +public class WolfCryptSignatureP1363Test { + + private static String PROVIDER = "wolfJCE"; + private static Provider provider; + + /* Standard curves to test for P1363 support */ + private static String supportedCurves[] = { + "secp256r1", + "secp384r1", + "secp521r1" + }; + + /* Track which curves are actually available in native wolfSSL */ + private static ArrayList enabledCurves = + new ArrayList(); + + @Rule(order = Integer.MIN_VALUE) + public TestRule testWatcher = new TestWatcher() { + protected void starting(Description desc) { + System.out.println("\t" + desc.getMethodName()); + } + }; + + @BeforeClass + public static void testProviderInstallationAtRuntime() { + + System.out.println("JCE WolfCryptSignatureP1363Test Class"); + + /* install wolfJCE provider at runtime */ + Security.insertProviderAt(new WolfCryptProvider(), 1); + + provider = Security.getProvider(PROVIDER); + Assert.assertNotNull(provider); + + /* Test which curves are enabled in native wolfSSL */ + for (int i = 0; i < supportedCurves.length; i++) { + int size = Ecc.getCurveSizeFromName( + supportedCurves[i].toUpperCase()); + if (size > 0) { + enabledCurves.add(supportedCurves[i]); + } + } + } + + /* Helper method to get expected P1363 signature length for curve */ + private int getExpectedP1363Length(String curveName) { + switch (curveName) { + case "secp256r1": return 64; /* 32 * 2 */ + case "secp384r1": return 96; /* 48 * 2 */ + case "secp521r1": return 132; /* 66 * 2 */ + default: + /* For other curves, calculate from curve size */ + int curveSize = Ecc.getCurveSizeFromName( + curveName.toUpperCase()); + return curveSize * 2; + } + } + + @Test + public void testP1363AlgorithmAvailability() throws Exception { + + /* Test SHA256withECDSAinP1363Format availability */ + try { + Signature sig256 = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + Assert.assertNotNull(sig256); + Assert.assertEquals(PROVIDER, sig256.getProvider().getName()); + + } catch (Exception e) { + Assert.fail("SHA256withECDSAinP1363Format should be available: " + + e.getMessage()); + } + + /* Test SHA384withECDSAinP1363Format availability */ + try { + Signature sig384 = Signature.getInstance( + "SHA384withECDSAinP1363Format", provider); + Assert.assertNotNull(sig384); + Assert.assertEquals(PROVIDER, sig384.getProvider().getName()); + + } catch (Exception e) { + Assert.fail("SHA384withECDSAinP1363Format should be available: " + + e.getMessage()); + } + } + + @Test + public void testP1363SignatureLength() throws Exception { + + /* Skip test if no ECC curves are available */ + if (enabledCurves.size() == 0) { + return; + } + + /* Test enabled curves with their expected P1363 signature lengths */ + for (int i = 0; i < enabledCurves.size(); i++) { + String curve = enabledCurves.get(i); + int expectedLen = getExpectedP1363Length(curve); + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec(curve)); + KeyPair keyPair = keyGen.generateKeyPair(); + + /* Test message */ + byte[] message = ("P1363 test message for " + curve).getBytes(); + + /* Test SHA256withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + Assert.assertEquals("P1363 signature length for " + curve, + expectedLen, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 signature verification for " + curve, + sig.verify(signature)); + } + } + + @Test + public void testP1363vsDERFormat() throws Exception { + + /* Skip test if secp256r1 not available */ + if (!enabledCurves.contains("secp256r1")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp256r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "P1363 vs DER format test".getBytes(); + + /* Create DER format signature */ + Signature sigDer = Signature.getInstance("SHA256withECDSA", provider); + sigDer.initSign(keyPair.getPrivate()); + sigDer.update(message); + byte[] derSignature = sigDer.sign(); + + /* Create P1363 format signature */ + Signature sigP1363 = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sigP1363.initSign(keyPair.getPrivate()); + sigP1363.update(message); + byte[] p1363Signature = sigP1363.sign(); + + /* P1363 signature should be exactly 64 bytes for secp256r1 */ + Assert.assertEquals("P1363 signature should be 64 bytes", + 64, p1363Signature.length); + + /* DER signature length varies, but should be different from P1363 */ + Assert.assertNotEquals( + "DER and P1363 signatures should have different lengths", + derSignature.length, p1363Signature.length); + + /* Verify P1363 signature */ + sigP1363.initVerify(keyPair.getPublic()); + sigP1363.update(message); + Assert.assertTrue("P1363 signature should verify", + sigP1363.verify(p1363Signature)); + + /* Verify DER signature */ + sigDer.initVerify(keyPair.getPublic()); + sigDer.update(message); + Assert.assertTrue("DER signature should verify", + sigDer.verify(derSignature)); + } + + @Test + public void testP1363SHA384() throws Exception { + + /* Skip test if secp384r1 not available */ + if (!enabledCurves.contains("secp384r1")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp384r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "SHA384 P1363 test message".getBytes(); + + /* Test SHA384withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA384withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* P1363 signature should be exactly 96 bytes for secp384r1 */ + Assert.assertEquals("P1363 SHA384 signature should be 96 bytes", + 96, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 SHA384 signature should verify", + sig.verify(signature)); + } + + @Test + public void testP1363MultipleUpdates() throws Exception { + + /* Skip test if no ECC curves are available */ + if (enabledCurves.size() == 0) { + return; + } + + /* Generate key pair using first available curve */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec(enabledCurves.get(0))); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message1 = "First part of ".getBytes(); + byte[] message2 = "multi-update message".getBytes(); + + /* Test with multiple updates */ + Signature sig = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message1); + sig.update(message2); + byte[] signature = sig.sign(); + + int expectedLen = getExpectedP1363Length(enabledCurves.get(0)); + Assert.assertEquals("P1363 signature should be " + expectedLen + + " bytes", expectedLen, signature.length); + + /* Verify with same multiple updates */ + sig.initVerify(keyPair.getPublic()); + sig.update(message1); + sig.update(message2); + Assert.assertTrue("Multi-update P1363 signature should verify", + sig.verify(signature)); + + /* Verify with single update should fail */ + byte[] fullMessage = new byte[message1.length + message2.length]; + System.arraycopy(message1, 0, fullMessage, 0, message1.length); + System.arraycopy(message2, 0, fullMessage, message1.length, + message2.length); + + sig.initVerify(keyPair.getPublic()); + sig.update(fullMessage); + Assert.assertTrue( + "Single update should also verify (content is the same)", + sig.verify(signature)); + } + + @Test + public void testP1363InvalidSignature() throws Exception { + + /* Skip test if no ECC curves are available */ + if (enabledCurves.size() == 0) { + return; + } + + /* Generate two key pairs using first available curve */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec(enabledCurves.get(0))); + KeyPair keyPair1 = keyGen.generateKeyPair(); + KeyPair keyPair2 = keyGen.generateKeyPair(); + + byte[] message = "Invalid signature test".getBytes(); + + /* Create signature with first key */ + Signature sig = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sig.initSign(keyPair1.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* Try to verify with second key - should fail */ + sig.initVerify(keyPair2.getPublic()); + sig.update(message); + Assert.assertFalse("Signature should not verify with wrong key", + sig.verify(signature)); + + /* Try to verify with correct key but wrong message - should fail */ + sig.initVerify(keyPair1.getPublic()); + sig.update("Wrong message".getBytes()); + Assert.assertFalse("Signature should not verify with wrong message", + sig.verify(signature)); + } + + @Test + public void testP1363LargeMessage() throws Exception { + + /* Skip test if no ECC curves are available */ + if (enabledCurves.size() == 0) { + return; + } + + /* Generate key pair using first available curve */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec(enabledCurves.get(0))); + KeyPair keyPair = keyGen.generateKeyPair(); + + /* Create large message */ + byte[] largeMessage = new byte[10000]; + new SecureRandom().nextBytes(largeMessage); + + /* Test P1363 signature with large message */ + Signature sig = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(largeMessage); + byte[] signature = sig.sign(); + + int expectedLen = getExpectedP1363Length(enabledCurves.get(0)); + Assert.assertEquals("P1363 signature should be " + expectedLen + + " bytes", expectedLen, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(largeMessage); + Assert.assertTrue("Large message P1363 signature should verify", + sig.verify(signature)); + } + + @Test + public void testP1363EmptyMessage() throws Exception { + + /* Skip test if no ECC curves are available */ + if (enabledCurves.size() == 0) { + return; + } + + /* Generate key pair using first available curve */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec(enabledCurves.get(0))); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] emptyMessage = new byte[0]; + + /* Test P1363 signature with empty message */ + Signature sig = Signature.getInstance( + "SHA256withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(emptyMessage); + byte[] signature = sig.sign(); + + int expectedLen = getExpectedP1363Length(enabledCurves.get(0)); + Assert.assertEquals("P1363 signature should be " + expectedLen + + " bytes", expectedLen, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(emptyMessage); + Assert.assertTrue("Empty message P1363 signature should verify", + sig.verify(signature)); + } +} + diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java b/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java index 5a39f1a4..acfc71b7 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java @@ -37,6 +37,7 @@ WolfCryptRandomTest.class, WolfCryptSecretKeyFactoryTest.class, WolfCryptSignatureTest.class, + WolfCryptSignatureP1363Test.class, WolfCryptMacTest.class, WolfCryptCipherTest.class, WolfCryptKeyAgreementTest.class, From 93e23e5a43fe383e9a28e597283381d443f09511 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 12:00:15 -0600 Subject: [PATCH 3/8] update CLAUDE.md with items for adding new Java and test files --- CLAUDE.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f46d8679..96d31f31 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,3 +48,11 @@ - Example .jks files are located under "examples/certs" - Example .wk files are located under "examples/certs" - Example .jks files are updated using the update-jks-wks.sh script + +# Adding new JUnit test files +- All new wolfCrypt JUnit test files must be added to src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java +- All new wolfJCE JUnit test files must be added to src/test/java/com/wolfssl/provider/jce/test/WolfJCETestSuite.java +- New JUnit test classes must define TestRule like existing ones do + +# Adding new Java files +- MUST add all new JNI or JCE Java files to scripts/infer.sh for Infer static analysis From d65b98b01e78039248c2bafd8c0da21e5d73969a Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 14:57:30 -0600 Subject: [PATCH 4/8] JCE: add Signature implementation for SHA512withECDSAinP1363Format, SHA3-256withECDSAinP1363Format, SHA3-384withECDSAinP1363Format, SHA3-512withECDSAinP1363Format, along with tests --- .../provider/jce/WolfCryptProvider.java | 11 + .../provider/jce/WolfCryptSignature.java | 204 ++++++++++++++++ .../jce/test/WolfCryptSignatureP1363Test.java | 223 +++++++++++++++++- 3 files changed, 429 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index 869e3a62..6b0c4748 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -167,6 +167,9 @@ private void registerServices() { put("Signature.SHA512withECDSA", "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA512wECDSA"); put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA"); + /* IEEE P1363 format ECDSA */ + put("Signature.SHA512withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA512wECDSAP1363"); } if (FeatureDetect.Sha3Enabled()) { put("Signature.SHA3-224withRSA", @@ -186,6 +189,14 @@ private void registerServices() { "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA3_384wECDSA"); put("Signature.SHA3-512withECDSA", "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA3_512wECDSA"); + + /* IEEE P1363 format ECDSA with SHA3 */ + put("Signature.SHA3-256withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA3_256wECDSAP1363"); + put("Signature.SHA3-384withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA3_384wECDSAP1363"); + put("Signature.SHA3-512withECDSAinP1363Format", + "com.wolfssl.provider.jce.WolfCryptSignature$wcSHA3_512wECDSAP1363"); } /* RSA-PSS Signature support. diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java index f93cd255..625ad01f 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java @@ -1945,5 +1945,209 @@ protected synchronized boolean engineVerify(byte[] signature) return super.engineVerify(derSignature); } } + + /** + * wolfJCE SHA3-256withECDSAinP1363Format signature class + */ + public static final class wcSHA3_256wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA3_256wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA3_256wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA3_256); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } + + /** + * wolfJCE SHA3-384withECDSAinP1363Format signature class + */ + public static final class wcSHA3_384wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA3_384wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA3_384wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA3_384); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } + + /** + * wolfJCE SHA3-512withECDSAinP1363Format signature class + */ + public static final class wcSHA3_512wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA3_512wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA3_512wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA3_512); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } + + /** + * wolfJCE SHA512withECDSAinP1363Format signature class + */ + public static final class wcSHA512wECDSAP1363 extends WolfCryptSignature { + /** + * Create new wcSHA512wECDSAP1363 object + * + * @throws NoSuchAlgorithmException if signature type is not + * available in native wolfCrypt library + */ + public wcSHA512wECDSAP1363() throws NoSuchAlgorithmException { + super(KeyType.WC_ECDSA, DigestType.WC_SHA512); + } + + /** + * Override engineSign to return P1363 format signature + */ + @Override + protected synchronized byte[] engineSign() + throws SignatureException { + + /* Get DER signature from parent class */ + byte[] derSignature = super.engineSign(); + if (derSignature == null) { + throw new SignatureException( + "Failed to generate DER signature"); + } + + /* Convert DER to P1363 format */ + return derToP1363(derSignature, this.ecc); + } + + /** + * Override engineVerify to handle P1363 format signature + */ + @Override + protected synchronized boolean engineVerify(byte[] signature) + throws SignatureException { + + if (signature == null) { + return false; + } + + /* Convert P1363 to DER format */ + byte[] derSignature = p1363ToDer(signature, this.ecc); + + /* Verify using parent class with DER signature */ + return super.engineVerify(derSignature); + } + } } diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java index 389d645d..494a084d 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureP1363Test.java @@ -60,6 +60,10 @@ public class WolfCryptSignatureP1363Test { private static ArrayList enabledCurves = new ArrayList(); + /* Track which signature algorithms are available */ + private static ArrayList availableAlgorithms = + new ArrayList(); + @Rule(order = Integer.MIN_VALUE) public TestRule testWatcher = new TestWatcher() { protected void starting(Description desc) { @@ -86,6 +90,32 @@ public static void testProviderInstallationAtRuntime() { enabledCurves.add(supportedCurves[i]); } } + + /* Test which signature algorithms are available */ + String[] algorithmsToTest = { + "SHA256withECDSAinP1363Format", + "SHA384withECDSAinP1363Format", + "SHA512withECDSAinP1363Format", + "SHA3-256withECDSAinP1363Format", + "SHA3-384withECDSAinP1363Format", + "SHA3-512withECDSAinP1363Format" + }; + + for (String algorithm : algorithmsToTest) { + if (isAlgorithmAvailable(algorithm)) { + availableAlgorithms.add(algorithm); + } + } + } + + /* Helper method to check if signature algorithm is available */ + private static boolean isAlgorithmAvailable(String algorithm) { + try { + Signature.getInstance(algorithm, provider); + return true; + } catch (Exception e) { + return false; + } } /* Helper method to get expected P1363 signature length for curve */ @@ -106,27 +136,51 @@ private int getExpectedP1363Length(String curveName) { public void testP1363AlgorithmAvailability() throws Exception { /* Test SHA256withECDSAinP1363Format availability */ - try { + if (availableAlgorithms.contains("SHA256withECDSAinP1363Format")) { Signature sig256 = Signature.getInstance( "SHA256withECDSAinP1363Format", provider); Assert.assertNotNull(sig256); Assert.assertEquals(PROVIDER, sig256.getProvider().getName()); - - } catch (Exception e) { - Assert.fail("SHA256withECDSAinP1363Format should be available: " + - e.getMessage()); } /* Test SHA384withECDSAinP1363Format availability */ - try { + if (availableAlgorithms.contains("SHA384withECDSAinP1363Format")) { Signature sig384 = Signature.getInstance( "SHA384withECDSAinP1363Format", provider); Assert.assertNotNull(sig384); Assert.assertEquals(PROVIDER, sig384.getProvider().getName()); + } - } catch (Exception e) { - Assert.fail("SHA384withECDSAinP1363Format should be available: " + - e.getMessage()); + /* Test SHA3-256withECDSAinP1363Format availability */ + if (availableAlgorithms.contains("SHA3-256withECDSAinP1363Format")) { + Signature sigSha3_256 = Signature.getInstance( + "SHA3-256withECDSAinP1363Format", provider); + Assert.assertNotNull(sigSha3_256); + Assert.assertEquals(PROVIDER, sigSha3_256.getProvider().getName()); + } + + /* Test SHA3-384withECDSAinP1363Format availability */ + if (availableAlgorithms.contains("SHA3-384withECDSAinP1363Format")) { + Signature sigSha3_384 = Signature.getInstance( + "SHA3-384withECDSAinP1363Format", provider); + Assert.assertNotNull(sigSha3_384); + Assert.assertEquals(PROVIDER, sigSha3_384.getProvider().getName()); + } + + /* Test SHA3-512withECDSAinP1363Format availability */ + if (availableAlgorithms.contains("SHA3-512withECDSAinP1363Format")) { + Signature sigSha3_512 = Signature.getInstance( + "SHA3-512withECDSAinP1363Format", provider); + Assert.assertNotNull(sigSha3_512); + Assert.assertEquals(PROVIDER, sigSha3_512.getProvider().getName()); + } + + /* Test SHA512withECDSAinP1363Format availability */ + if (availableAlgorithms.contains("SHA512withECDSAinP1363Format")) { + Signature sig512 = Signature.getInstance( + "SHA512withECDSAinP1363Format", provider); + Assert.assertNotNull(sig512); + Assert.assertEquals(PROVIDER, sig512.getProvider().getName()); } } @@ -255,6 +309,157 @@ public void testP1363SHA384() throws Exception { sig.verify(signature)); } + @Test + public void testP1363SHA3_256() throws Exception { + + /* Skip test if secp256r1 not available */ + if (!enabledCurves.contains("secp256r1")) { + return; + } + + /* Skip test if SHA3-256 algorithm not available */ + if (!availableAlgorithms.contains("SHA3-256withECDSAinP1363Format")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp256r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "SHA3-256 P1363 test message".getBytes(); + + /* Test SHA3-256withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA3-256withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* P1363 signature should be exactly 64 bytes for secp256r1 */ + Assert.assertEquals("P1363 SHA3-256 signature should be 64 bytes", + 64, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 SHA3-256 signature should verify", + sig.verify(signature)); + } + + @Test + public void testP1363SHA3_384() throws Exception { + + /* Skip test if secp384r1 not available */ + if (!enabledCurves.contains("secp384r1")) { + return; + } + + /* Skip test if SHA3-384 algorithm not available */ + if (!availableAlgorithms.contains("SHA3-384withECDSAinP1363Format")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp384r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "SHA3-384 P1363 test message".getBytes(); + + /* Test SHA3-384withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA3-384withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* P1363 signature should be exactly 96 bytes for secp384r1 */ + Assert.assertEquals("P1363 SHA3-384 signature should be 96 bytes", + 96, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 SHA3-384 signature should verify", + sig.verify(signature)); + } + + @Test + public void testP1363SHA3_512() throws Exception { + + /* Skip test if secp521r1 not available */ + if (!enabledCurves.contains("secp521r1")) { + return; + } + + /* Skip test if SHA3-512 algorithm not available */ + if (!availableAlgorithms.contains("SHA3-512withECDSAinP1363Format")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp521r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "SHA3-512 P1363 test message".getBytes(); + + /* Test SHA3-512withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA3-512withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* P1363 signature should be exactly 132 bytes for secp521r1 */ + Assert.assertEquals("P1363 SHA3-512 signature should be 132 bytes", + 132, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 SHA3-512 signature should verify", + sig.verify(signature)); + } + + @Test + public void testP1363SHA512() throws Exception { + + /* Skip test if secp521r1 not available */ + if (!enabledCurves.contains("secp521r1")) { + return; + } + + /* Generate key pair */ + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance("EC", provider); + keyGen.initialize(new ECGenParameterSpec("secp521r1")); + KeyPair keyPair = keyGen.generateKeyPair(); + + byte[] message = "SHA512 P1363 test message".getBytes(); + + /* Test SHA512withECDSAinP1363Format */ + Signature sig = Signature.getInstance( + "SHA512withECDSAinP1363Format", provider); + sig.initSign(keyPair.getPrivate()); + sig.update(message); + byte[] signature = sig.sign(); + + /* P1363 signature should be exactly 132 bytes for secp521r1 */ + Assert.assertEquals("P1363 SHA512 signature should be 132 bytes", + 132, signature.length); + + /* Verify signature */ + sig.initVerify(keyPair.getPublic()); + sig.update(message); + Assert.assertTrue("P1363 SHA512 signature should verify", + sig.verify(signature)); + } + @Test public void testP1363MultipleUpdates() throws Exception { From 93fc406b2edd3a247fa9d6ac5a8eba8dc56fd4e7 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 16:04:01 -0600 Subject: [PATCH 5/8] JCE: fix Signature class handling of ECDSA parameters, add test --- .../provider/jce/WolfCryptSignature.java | 9 +- .../jce/test/WolfCryptSignatureTest.java | 112 ++++++++++++++++-- 2 files changed, 110 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java index 625ad01f..b24fe935 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java @@ -609,7 +609,14 @@ protected synchronized void engineSetParameter( return; } - /* For non-PSS signatures, reject any non-null parameters */ + if (this.keyType == KeyType.WC_ECDSA && + params instanceof ECParameterSpec) { + /* To match Sun behavior, ECDSA signatures should not store/return + * parameters, but should accept them without error */ + return; + } + + /* For other non-PSS signatures, reject any non-null parameters */ throw new InvalidAlgorithmParameterException( "Parameters not supported for " + keyTypeToString(this.keyType) + " with PKCS#1 v1.5 padding"); diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java index 6cdf9792..f2374439 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptSignatureTest.java @@ -44,9 +44,12 @@ import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.PrivateKey; +import java.security.AlgorithmParameters; import java.security.spec.ECGenParameterSpec; import java.security.spec.PSSParameterSpec; import java.security.interfaces.RSAKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; import java.security.NoSuchProviderException; import java.security.NoSuchAlgorithmException; @@ -2031,17 +2034,32 @@ public void testNonPssSignatureRejectsNonNullParameters() /* Expected */ } - /* Test setting some other non-null parameter object should fail */ - java.security.spec.ECGenParameterSpec ecSpec = - new java.security.spec.ECGenParameterSpec("secp256r1"); + /* Test setting inappropriate parameter types */ + if (algo.equals("SHA256withRSA")) { + /* RSA should reject ECGenParameterSpec */ + ECGenParameterSpec ecSpec = + new java.security.spec.ECGenParameterSpec("secp256r1"); + try { + signer.setParameter(ecSpec); + fail("Should throw InvalidAlgorithmParameterException " + + "when setting ECGenParameterSpec on RSA algorithm: " + + algo); + } catch (InvalidAlgorithmParameterException e) { + /* Expected */ + } - try { - signer.setParameter(ecSpec); - fail("Should have thrown InvalidAlgorithmParameterException " + - "when setting non-null parameters on non-PSS algorithm: " + - algo); - } catch (InvalidAlgorithmParameterException e) { - /* Expected */ + } else if (algo.equals("SHA256withECDSA")) { + /* ECDSA should accept ECParameterSpec (JDK bug 8286908) + * but should reject other parameter types like PSS */ + ECPrivateKey ecPriv = (ECPrivateKey)pair.getPrivate(); + + try { + signer.setParameter(ecPriv.getParams()); + /* Success - should not throw */ + } catch (Exception e) { + fail("ECDSA should accept ECParameterSpec, " + + "but got exception: " + e.getMessage()); + } } } } @@ -2239,5 +2257,79 @@ public void testECDSASignatureOIDMappings() " should be verified with OID " + oid, verified); } } + + /** + * ECDSA signature should not return parameters. + * This test verifies that ECDSA signatures: + * 1. Accept ECParameterSpec parameters via setParameter() without throwing + * 2. Return null from getParameters() (do not store/return parameters) + */ + @Test + public void testECDSASignatureParametersRegression() + throws NoSuchProviderException, NoSuchAlgorithmException, + SignatureException, InvalidKeyException, + InvalidAlgorithmParameterException { + + /* Test ECDSA algorithms that should support parameter handling */ + String[] ecdsaAlgos = { + "SHA256withECDSA", + "SHA384withECDSA", + "SHA512withECDSA" + }; + + for (String algo : ecdsaAlgos) { + if (!enabledAlgos.contains(algo)) { + continue; /* Skip if algorithm not enabled */ + } + + /* Generate ECDSA key pair */ + KeyPair pair = generateKeyPair(algo, secureRandom); + assertNotNull("Key pair should not be null for " + algo, pair); + + ECPrivateKey ecPriv = (ECPrivateKey)pair.getPrivate(); + ECPublicKey ecPub = (ECPublicKey)pair.getPublic(); + + /* Create signature instance */ + Signature sig = Signature.getInstance(algo, "wolfJCE"); + assertNotNull("Signature should not be null for " + algo, sig); + + sig.initSign(ecPriv); + + /* Test 1: setParameter(ECParameterSpec) should not throw */ + try { + sig.setParameter(ecPriv.getParams()); + /* Success - should not throw */ + } catch (Exception e) { + fail("ECDSA signature should accept ECParameterSpec without " + + "throwing exception for " + algo + ", but got: " + + e.getMessage()); + } + + /* Test 2: getParameters() should return null (not store/return + * parameters) */ + AlgorithmParameters params = sig.getParameters(); + assertNull("ECDSA signature should return null from " + + "getParameters() for " + algo, params); + + /* Test 3: Verify signature still works after setParameter() */ + String testMessage = "Test message for " + algo; + byte[] testData = testMessage.getBytes(); + + sig.update(testData); + byte[] signature = sig.sign(); + assertNotNull("Signature should not be null for " + algo, + signature); + assertTrue("Signature should not be empty for " + algo, + signature.length > 0); + + /* Verify the signature */ + Signature verifier = Signature.getInstance(algo, "wolfJCE"); + verifier.initVerify(ecPub); + verifier.update(testData); + boolean verified = verifier.verify(signature); + assertTrue("Signature verification should succeed for " + algo + + " after setParameter(ECParameterSpec)", verified); + } + } } From 5c58ecb45233451a035640017fc7af0d1e8be5b1 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 16:14:13 -0600 Subject: [PATCH 6/8] JCE: add default 256-bit key size for ECC KeyPairGenerator, and test --- .../jce/WolfCryptKeyPairGenerator.java | 14 ++++++++ .../test/WolfCryptKeyPairGeneratorTest.java | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java index f5d4dbb5..82e8d39e 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptKeyPairGenerator.java @@ -100,6 +100,20 @@ private WolfCryptKeyPairGenerator(KeyType type) { } } + /* Set default parameters for ECC key generation */ + if (type == KeyType.WC_ECC) { + /* Default to 256-bit ECC */ + this.keysize = 32; + + /* Initialize RNG for default key generation */ + synchronized (rngLock) { + if (this.rng == null) { + this.rng = new Rng(); + this.rng.init(); + } + } + } + if (WolfCryptDebug.DEBUG) { algString = typeToString(type); } diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java index c537d234..a1afe93e 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptKeyPairGeneratorTest.java @@ -56,6 +56,7 @@ import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.interfaces.ECPublicKey; +import java.security.interfaces.ECPrivateKey; import java.security.spec.ECParameterSpec; import com.wolfssl.wolfcrypt.Rsa; @@ -642,6 +643,40 @@ public void testKeyPairGeneratorRsaDefaultKeySize() pubKey.getPublicExponent()); } + @Test + public void testKeyPairGeneratorEccDefaultKeySize() + throws NoSuchProviderException, NoSuchAlgorithmException { + + /* Skip test if 256-bit ECC not supported */ + if (!enabledEccKeySizes.contains(Integer.valueOf(32))) { + return; + } + + /* Test that ECC KeyPairGenerator works with default parameters + * without explicit initialization */ + KeyPairGenerator kpg = + KeyPairGenerator.getInstance("EC", "wolfJCE"); + + /* Generate key pair without calling initialize() first */ + KeyPair kp = kpg.generateKeyPair(); + assertNotNull(kp); + assertNotNull(kp.getPublic()); + assertNotNull(kp.getPrivate()); + + /* Verify the generated key is ECC */ + assertTrue(kp.getPublic() instanceof ECPublicKey); + assertTrue(kp.getPrivate() instanceof ECPrivateKey); + + ECPublicKey pubKey = (ECPublicKey) kp.getPublic(); + ECParameterSpec ecParams = pubKey.getParams(); + assertNotNull(ecParams); + + /* Default key size should be 256 bits (32 bytes), verify field size */ + int fieldSize = ecParams.getCurve().getField().getFieldSize(); + assertEquals("Default ECC key field size should be 256 bits", + 256, fieldSize); + } + @Test public void testKeyPairGeneratorRsassaPssKeyGeneration() throws NoSuchProviderException, NoSuchAlgorithmException, From 5aefe9060487cb6193db97e5f8e18a7435f5595a Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 16:35:43 -0600 Subject: [PATCH 7/8] JCE: add P1363 Signature algorithms to README_JCE.md --- README_JCE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README_JCE.md b/README_JCE.md index a27ae686..f9b63529 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -153,6 +153,12 @@ The JCE provider currently supports the following algorithms: SHA3-256withECDSA SHA3-384withECDSA SHA3-512withECDSA + SHA256withECDSAinP1363Format + SHA384withECDSAinP1363Format + SHA512withECDSAinP1363Format + SHA3-256withECDSAinP1363Format + SHA3-384withECDSAinP1363Format + SHA3-512withECDSAinP1363Format KeyAgreement Class DiffieHellman From 34bd3b9aec223fce419cd2b8e292589709fbd4bd Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 24 Sep 2025 16:54:27 -0600 Subject: [PATCH 8/8] JNI: fix Doxygen warning in com.wolfssl.wolfcrypt.Rng --- src/main/java/com/wolfssl/wolfcrypt/Rng.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/wolfssl/wolfcrypt/Rng.java b/src/main/java/com/wolfssl/wolfcrypt/Rng.java index 829b3f6f..7d98cc83 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Rng.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Rng.java @@ -28,7 +28,7 @@ */ public class Rng extends NativeStruct { - /* Maximum generate block length for wolfCrypt */ + /** Maximum generate block length for wolfCrypt */ public static int RNG_MAX_BLOCK_LEN = Rng.getRNG_MAX_BLOCK_LEN();