diff --git a/tests/api.c b/tests/api.c index 238ea67397..f79e6b079e 100644 --- a/tests/api.c +++ b/tests/api.c @@ -177,6 +177,9 @@ #ifdef WOLFSSL_HAVE_MLDSA #include #endif +#ifdef WOLFSSL_HAVE_SLHDSA + #include +#endif #if defined(WOLFSSL_HAVE_MLKEM) #include #endif @@ -26218,6 +26221,396 @@ static int test_wc_MakeCRL_max_crlnum(void) return EXPECT_RESULT(); } +/* Guard on the union of the caller test-body guards so this helper is only + * compiled when at least one caller references it (avoids -Wunused-function + * under -Werror). */ +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN) && \ + ((defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_PEM_TO_DER)) || \ + defined(WOLFSSL_HAVE_SLHDSA) || \ + (defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN)) || \ + (defined(HAVE_ED448) && defined(HAVE_ED448_SIGN))) +/* Build a CRL, sign it with the given CA key through wc_MakeCRL_ex + + * wc_SignCRL_ex2, then load it via the certificate manager so the signature is + * verified against the issuing CA. keyType selects how key is interpreted. + * Also confirms a tampered signature and a family-mismatched signature type are + * rejected. Returns the EXPECT result. */ +static int crl_sign_verify_ex2(const byte* caCertDer, word32 caCertDerSz, + int keyType, void* key, int sigType) +{ + EXPECT_DECLS; + WOLFSSL_CERT_MANAGER* cm = NULL; + DecodedCert caCert; + int caCertInit = 0; + WC_RNG rng; + int rngInit = 0; + byte* issuerDer = NULL; + word32 issuerDerSz = 0; + byte* tbsBuf = NULL; + byte* crlBuf = NULL; + int tbsSz = 0; + int crlSz = 0; + int bufSz = 0; + + /* thisUpdate in the past, nextUpdate far in the future so the CRL is + * current whenever the test runs. */ + const byte thisUpdate[] = "260101000000Z"; /* Jan 1, 2026 */ + const byte nextUpdate[] = "350101000000Z"; /* Jan 1, 2035 */ + /* Minimal CRL number for the v2 extension. */ + const byte crlNum[] = { 0x01 }; + + /* Extract the issuer Name (= CA subject) for the CRL's issuer field so the + * verifier can locate this CA by name. subjectRaw lacks the outer SEQUENCE + * tag, so re-wrap it. */ + wc_InitDecodedCert(&caCert, caCertDer, caCertDerSz, NULL); + caCertInit = 1; + ExpectIntEQ(wc_ParseCert(&caCert, CERT_TYPE, 0, NULL), 0); + if (EXPECT_SUCCESS()) { + ExpectNotNull(issuerDer = (byte*)XMALLOC( + (size_t)caCert.subjectRawLen + MAX_SEQ_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + } + if (EXPECT_SUCCESS()) { + word32 seqHdrSz = SetSequence((word32)caCert.subjectRawLen, issuerDer); + XMEMCPY(issuerDer + seqHdrSz, caCert.subjectRaw, + (size_t)caCert.subjectRawLen); + issuerDerSz = seqHdrSz + (word32)caCert.subjectRawLen; + } + + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) + rngInit = 1; + + /* Size, then encode, the TBSCertList. */ + if (EXPECT_SUCCESS()) { + tbsSz = wc_MakeCRL_ex(issuerDer, issuerDerSz, + thisUpdate, ASN_UTC_TIME, nextUpdate, ASN_UTC_TIME, + NULL, crlNum, (word32)sizeof(crlNum), sigType, 2, NULL, 0); + ExpectIntGT(tbsSz, 0); + } + /* wc_SignCRL_ex2 sizes its internal signature buffer from the key; the + * caller's output buffer only needs headroom for the largest signature. + * The biggest supported scheme is SLH-DSA (up to ~50KB), so size generously + * rather than per-algorithm. */ + if (EXPECT_SUCCESS()) { + bufSz = tbsSz + 64 * 1024; + ExpectNotNull(tbsBuf = (byte*)XMALLOC(bufSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(crlBuf = (byte*)XMALLOC(bufSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + } + if (EXPECT_SUCCESS()) { + tbsSz = wc_MakeCRL_ex(issuerDer, issuerDerSz, + thisUpdate, ASN_UTC_TIME, nextUpdate, ASN_UTC_TIME, + NULL, crlNum, (word32)sizeof(crlNum), sigType, 2, + tbsBuf, (word32)bufSz); + ExpectIntGT(tbsSz, 0); + } + + /* Sign the CRL with the CA key. */ + if (EXPECT_SUCCESS()) { + crlSz = wc_SignCRL_ex2(tbsBuf, tbsSz, sigType, crlBuf, (word32)bufSz, + keyType, key, &rng); + ExpectIntGT(crlSz, 0); + } + + /* Negative: an RSA signatureAlgorithm OID must be rejected for a non-RSA + * key before any signature is produced. CheckSigTypeForKey runs before the + * TBS is copied into the output, so crlBuf still holds the valid CRL. */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wc_SignCRL_ex2(tbsBuf, tbsSz, CTC_SHA256wRSA, crlBuf, + (word32)bufSz, keyType, key, &rng), + WC_NO_ERR_TRACE(ALGO_ID_E)); + } + + /* Load the issuing CA and verify the freshly signed CRL. */ + ExpectNotNull(cm = wolfSSL_CertManagerNew()); + ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caCertDer, caCertDerSz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CertManagerLoadCRLBuffer(cm, crlBuf, crlSz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + + /* Negative: flip a byte of the signature *value*. The DER lengths are + * unchanged so the CRL still parses; only the signature check can reject + * it, which must surface as ASN_CRL_CONFIRM_E. */ + if (EXPECT_SUCCESS()) { + WOLFSSL_CERT_MANAGER* cm2 = NULL; + crlBuf[crlSz - 1] ^= 0xFF; + ExpectNotNull(cm2 = wolfSSL_CertManagerNew()); + ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm2, caCertDer, + caCertDerSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm2, WOLFSSL_CRL_CHECKALL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CertManagerLoadCRLBuffer(cm2, crlBuf, crlSz, + WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_CRL_CONFIRM_E)); + wolfSSL_CertManagerFree(cm2); + } + + wolfSSL_CertManagerFree(cm); + XFREE(issuerDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(crlBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(tbsBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (rngInit) + wc_FreeRng(&rng); + if (caCertInit) + wc_FreeDecodedCert(&caCert); + return EXPECT_RESULT(); +} +#endif /* CRL gen + (ED25519 | ED448 | MLDSA | SLHDSA) */ + +/* Sign and verify CRLs with ML-DSA CA keys for all three parameter sets. */ +static int test_wc_SignCRL_mldsa(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN) && defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WOLFSSL_PEM_TO_DER) + static const struct { + const char* certDer; + const char* keyPem; + int keyType; + int sigType; + } cases[] = { + { "./certs/mldsa/mldsa44-cert.der", "./certs/mldsa/mldsa44-key.pem", + ML_DSA_44_TYPE, CTC_ML_DSA_44 }, + { "./certs/mldsa/mldsa65-cert.der", "./certs/mldsa/mldsa65-key.pem", + ML_DSA_65_TYPE, CTC_ML_DSA_65 }, + { "./certs/mldsa/mldsa87-cert.der", "./certs/mldsa/mldsa87-key.pem", + ML_DSA_87_TYPE, CTC_ML_DSA_87 }, + }; + int i; + int n = (int)(sizeof(cases) / sizeof(cases[0])); + + for (i = 0; i < n; i++) { + byte* certDer = NULL; + size_t certDerSz = 0; + byte* keyPem = NULL; + size_t keyPemSz = 0; + byte* keyDer = NULL; + int keyDerSz = 0; + wc_MlDsaKey key; + int keyInit = 0; + word32 idx = 0; + + ExpectIntEQ(load_file(cases[i].certDer, &certDer, &certDerSz), 0); + ExpectIntEQ(load_file(cases[i].keyPem, &keyPem, &keyPemSz), 0); + + /* Convert the PKCS#8 PEM private key to DER. */ + if (EXPECT_SUCCESS()) { + keyDer = (byte*)XMALLOC(keyPemSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(keyDer); + } + if (EXPECT_SUCCESS()) { + keyDerSz = wc_KeyPemToDer(keyPem, (int)keyPemSz, keyDer, + (int)keyPemSz, NULL); + ExpectIntGT(keyDerSz, 0); + } + + ExpectIntEQ(wc_MlDsaKey_Init(&key, NULL, INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) + keyInit = 1; + ExpectIntEQ(wc_MlDsaKey_PrivateKeyDecode(&key, keyDer, (word32)keyDerSz, + &idx), 0); + + if (EXPECT_SUCCESS()) { + ExpectIntEQ(crl_sign_verify_ex2(certDer, (word32)certDerSz, + cases[i].keyType, &key, cases[i].sigType), TEST_SUCCESS); + } + + if (keyInit) + wc_MlDsaKey_Free(&key); + XFREE(keyDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(certDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + return EXPECT_RESULT(); +} + +/* Sign and verify CRLs with SLH-DSA CA keys (SHA2 and SHAKE 128s roots). */ +static int test_wc_SignCRL_slhdsa(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN) && defined(WOLFSSL_HAVE_SLHDSA) + static const struct { + const char* certDer; + const char* keyDer; + int keyType; + int sigType; + int param; + } cases[] = { + /* SHAKE variants are always built with --enable-slhdsa. */ + { "./certs/slhdsa/root-slhdsa-shake-128s.der", + "./certs/slhdsa/root-slhdsa-shake-128s-priv.der", + SLH_DSA_SHAKE_128S_TYPE, CTC_SLH_DSA_SHAKE_128S, SLHDSA_SHAKE128S }, +#ifdef WOLFSSL_SLHDSA_SHA2 + { "./certs/slhdsa/root-slhdsa-sha2-128s.der", + "./certs/slhdsa/root-slhdsa-sha2-128s-priv.der", + SLH_DSA_SHA2_128S_TYPE, CTC_SLH_DSA_SHA2_128S, SLHDSA_SHA2_128S }, +#endif + }; + int i; + int n = (int)(sizeof(cases) / sizeof(cases[0])); + + for (i = 0; i < n; i++) { + byte* certDer = NULL; + size_t certDerSz = 0; + byte* keyDer = NULL; + size_t keyDerSz = 0; + SlhDsaKey key; + int keyInit = 0; + word32 idx = 0; + + ExpectIntEQ(load_file(cases[i].certDer, &certDer, &certDerSz), 0); + ExpectIntEQ(load_file(cases[i].keyDer, &keyDer, &keyDerSz), 0); + + ExpectIntEQ(wc_SlhDsaKey_Init(&key, (enum SlhDsaParam)cases[i].param, + NULL, INVALID_DEVID), 0); + if (EXPECT_SUCCESS()) + keyInit = 1; + ExpectIntEQ(wc_SlhDsaKey_PrivateKeyDecode(keyDer, &idx, &key, + (word32)keyDerSz), 0); + + if (EXPECT_SUCCESS()) { + ExpectIntEQ(crl_sign_verify_ex2(certDer, (word32)certDerSz, + cases[i].keyType, &key, cases[i].sigType), TEST_SUCCESS); + } + + if (keyInit) + wc_SlhDsaKey_Free(&key); + XFREE(keyDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(certDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + return EXPECT_RESULT(); +} + +/* Sign and verify a CRL with an Ed25519 CA key (wc_SignCRL_ex2). */ +static int test_wc_SignCRL_ed25519(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN) && defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) + byte* certDer = NULL; + size_t certDerSz = 0; + byte* keyDer = NULL; + size_t keyDerSz = 0; + ed25519_key key; + int keyInit = 0; + word32 idx = 0; + + ExpectIntEQ(load_file("./certs/ed25519/root-ed25519.der", &certDer, + &certDerSz), 0); + ExpectIntEQ(load_file("./certs/ed25519/root-ed25519-priv.der", &keyDer, + &keyDerSz), 0); + + ExpectIntEQ(wc_ed25519_init(&key), 0); + if (EXPECT_SUCCESS()) + keyInit = 1; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(keyDer, &idx, &key, + (word32)keyDerSz), 0); + /* The key file carries the private key only, so derive the public key that + * Ed25519 signing needs. */ + ExpectIntEQ(wc_ed25519_make_public(&key, key.p, ED25519_PUB_KEY_SIZE), 0); + if (EXPECT_SUCCESS()) + key.pubKeySet = 1; + + if (EXPECT_SUCCESS()) { + ExpectIntEQ(crl_sign_verify_ex2(certDer, (word32)certDerSz, + ED25519_TYPE, &key, CTC_ED25519), TEST_SUCCESS); + } + + if (keyInit) + wc_ed25519_free(&key); + XFREE(keyDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(certDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + +/* Sign and verify a CRL with an Ed448 CA key (wc_SignCRL_ex2). */ +static int test_wc_SignCRL_ed448(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN) && defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) + byte* certDer = NULL; + size_t certDerSz = 0; + byte* keyDer = NULL; + size_t keyDerSz = 0; + ed448_key key; + int keyInit = 0; + word32 idx = 0; + + ExpectIntEQ(load_file("./certs/ed448/root-ed448.der", &certDer, + &certDerSz), 0); + ExpectIntEQ(load_file("./certs/ed448/root-ed448-priv.der", &keyDer, + &keyDerSz), 0); + + ExpectIntEQ(wc_ed448_init(&key), 0); + if (EXPECT_SUCCESS()) + keyInit = 1; + ExpectIntEQ(wc_Ed448PrivateKeyDecode(keyDer, &idx, &key, + (word32)keyDerSz), 0); + /* The key file carries the private key only, so derive the public key that + * Ed448 signing needs. */ + ExpectIntEQ(wc_ed448_make_public(&key, key.p, ED448_PUB_KEY_SIZE), 0); + if (EXPECT_SUCCESS()) + key.pubKeySet = 1; + + if (EXPECT_SUCCESS()) { + ExpectIntEQ(crl_sign_verify_ex2(certDer, (word32)certDerSz, + ED448_TYPE, &key, CTC_ED448), TEST_SUCCESS); + } + + if (keyInit) + wc_ed448_free(&key); + XFREE(keyDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(certDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + +/* Stateful hash-based schemes (LMS/XMSS) are intentionally rejected for CRL + * signing; wc_SignCRL_ex2 must return ALGO_ID_E for those key types. */ +static int test_wc_SignCRL_stateful_rejected(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_ASN) + WC_RNG rng; + int rngInit = 0; + byte tbs[8]; + byte out[64]; + int dummyKey = 0; + + XMEMSET(tbs, 0, sizeof(tbs)); + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) + rngInit = 1; + + /* The key type is rejected in the dispatch before key is dereferenced, so a + * dummy non-NULL key pointer is sufficient. */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wc_SignCRL_ex2((const byte*)tbs, (int)sizeof(tbs), + CTC_SHA256wRSA, out, (word32)sizeof(out), LMS_TYPE, &dummyKey, + &rng), WC_NO_ERR_TRACE(ALGO_ID_E)); + ExpectIntEQ(wc_SignCRL_ex2((const byte*)tbs, (int)sizeof(tbs), + CTC_SHA256wRSA, out, (word32)sizeof(out), XMSS_TYPE, &dummyKey, + &rng), WC_NO_ERR_TRACE(ALGO_ID_E)); + ExpectIntEQ(wc_SignCRL_ex2((const byte*)tbs, (int)sizeof(tbs), + CTC_SHA256wRSA, out, (word32)sizeof(out), XMSSMT_TYPE, &dummyKey, + &rng), WC_NO_ERR_TRACE(ALGO_ID_E)); + } + + if (rngInit) + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + static int test_X509_REQ(void) { EXPECT_DECLS; @@ -37208,6 +37601,11 @@ TEST_CASE testCases[] = { TEST_DECL(test_sk_X509_CRL_encode), TEST_DECL(test_wolfSSL_X509_CRL_sign_large), TEST_DECL(test_wc_MakeCRL_max_crlnum), + TEST_DECL(test_wc_SignCRL_mldsa), + TEST_DECL(test_wc_SignCRL_slhdsa), + TEST_DECL(test_wc_SignCRL_ed25519), + TEST_DECL(test_wc_SignCRL_ed448), + TEST_DECL(test_wc_SignCRL_stateful_rejected), /* OpenSSL X509 REQ API test */ TEST_DECL(test_wolfSSL_d2i_X509_REQ), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 8b870e3ce0..686abc999d 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -37575,39 +37575,142 @@ int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, return (int)idx; } -/* Sign a CRL TBS and produce complete CRL DER. +/* Sign a CRL TBS with an RSA or ECC key and produce the complete CRL DER. * tbsBuf: contains the TBS at the beginning * tbsSz: size of TBS in tbsBuf - * sType: signature type (e.g., CTC_SHA256wRSA) + * sType: signature type (e.g., CTC_SHA256wRSA, CTC_SHA256wECDSA) * buf: output buffer for complete CRL. May be the same as tbsBuf. * bufSz: size of output buffer - * rsaKey/eccKey: signing key (one must be non-NULL) + * rsaKey/eccKey: signing key (exactly one must be non-NULL) * rng: random number generator * + * For other key types (Ed25519/Ed448, ML-DSA, SLH-DSA, ...) use + * wc_SignCRL_ex2. + * * Returns: size of complete CRL on success, negative error on failure */ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, byte* buf, word32 bufSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng) +{ + int keyType; + void* key; + + /* Accept exactly one of the RSA/ECC keys, then defer to the any-key + * wc_SignCRL_ex2 so the two entry points share one implementation. */ + if (rsaKey != NULL && eccKey != NULL) + return BAD_FUNC_ARG; + + if (rsaKey != NULL) { + keyType = RSA_TYPE; + key = rsaKey; + } + else if (eccKey != NULL) { + keyType = ECC_TYPE; + key = eccKey; + } + else { + return BAD_FUNC_ARG; + } + + return wc_SignCRL_ex2(tbsBuf, tbsSz, sType, buf, bufSz, keyType, key, rng); +} + +/* Sign a CRL TBS with any supported key type and produce the complete CRL DER. + * key is interpreted according to keyType (RSA_TYPE, ECC_TYPE, ED25519_TYPE, + * ED448_TYPE, FALCON_*, ML_DSA_*, SLH_DSA_*), the same selector wc_SignCert_ex + * uses. The signature buffer is sized from the key, so post-quantum signatures + * get enough room. Stateful hash-based schemes (LMS/XMSS) are rejected. + * tbsBuf: contains the TBS at the beginning + * tbsSz: size of TBS in tbsBuf + * sType: signature type matching the key (e.g., CTC_ML_DSA_44) + * buf: output buffer for complete CRL. May be the same as tbsBuf. + * bufSz: size of output buffer + * keyType: selects how key is interpreted + * key: signing key + * rng: random number generator + * + * Returns: size of complete CRL on success, negative error on failure + */ +int wc_SignCRL_ex2(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, int keyType, void* key, + WC_RNG* rng) { int ret; int sigSz; - word32 sigCap = MAX_ENCODED_CLASSIC_SIG_SZ; + int maxSigSz; CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; void* heap = NULL; - - if (tbsBuf == NULL || tbsSz <= 0 || buf == NULL || rng == NULL) + RsaKey* rsaKey = NULL; + ecc_key* eccKey = NULL; + ed25519_key* ed25519Key = NULL; + ed448_key* ed448Key = NULL; + falcon_key* falconKey = NULL; + wc_MlDsaKey* mldsaKey = NULL; + SlhDsaKey* slhDsaKey = NULL; + + if (tbsBuf == NULL || tbsSz <= 0 || buf == NULL || key == NULL || + rng == NULL) return BAD_FUNC_ARG; - if (rsaKey == NULL && eccKey == NULL) - return BAD_FUNC_ARG; - if (rsaKey != NULL && eccKey != NULL) + + /* Resolve the untyped key from keyType, matching wc_SignCert_ex. */ + if (keyType == RSA_TYPE) + rsaKey = (RsaKey*)key; + else if (keyType == ECC_TYPE) + eccKey = (ecc_key*)key; + else if (keyType == ED25519_TYPE) + ed25519Key = (ed25519_key*)key; + else if (keyType == ED448_TYPE) + ed448Key = (ed448_key*)key; + else if (keyType == FALCON_LEVEL1_TYPE) + falconKey = (falcon_key*)key; + else if (keyType == FALCON_LEVEL5_TYPE) + falconKey = (falcon_key*)key; +#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT + else if (keyType == DILITHIUM_LEVEL2_TYPE) + mldsaKey = (wc_MlDsaKey*)key; + else if (keyType == DILITHIUM_LEVEL3_TYPE) + mldsaKey = (wc_MlDsaKey*)key; + else if (keyType == DILITHIUM_LEVEL5_TYPE) + mldsaKey = (wc_MlDsaKey*)key; +#endif + else if (keyType == ML_DSA_44_TYPE) + mldsaKey = (wc_MlDsaKey*)key; + else if (keyType == ML_DSA_65_TYPE) + mldsaKey = (wc_MlDsaKey*)key; + else if (keyType == ML_DSA_87_TYPE) + mldsaKey = (wc_MlDsaKey*)key; + else if (keyType == SLH_DSA_SHA2_128S_TYPE || + keyType == SLH_DSA_SHA2_128F_TYPE || + keyType == SLH_DSA_SHA2_192S_TYPE || + keyType == SLH_DSA_SHA2_192F_TYPE || + keyType == SLH_DSA_SHA2_256S_TYPE || + keyType == SLH_DSA_SHA2_256F_TYPE || + keyType == SLH_DSA_SHAKE_128S_TYPE || + keyType == SLH_DSA_SHAKE_128F_TYPE || + keyType == SLH_DSA_SHAKE_192S_TYPE || + keyType == SLH_DSA_SHAKE_192F_TYPE || + keyType == SLH_DSA_SHAKE_256S_TYPE || + keyType == SLH_DSA_SHAKE_256F_TYPE) + slhDsaKey = (SlhDsaKey*)key; + else if (keyType == LMS_TYPE || keyType == XMSS_TYPE || + keyType == XMSSMT_TYPE) { + /* Stateful hash-based schemes are intentionally rejected for CRL + * signing: a CRL is reissued periodically and would exhaust the key's + * one-time signature state, with catastrophic reuse risk if that state + * is mismanaged. */ + WOLFSSL_MSG("Stateful signatures (LMS/XMSS) not supported for CRLs"); + return ALGO_ID_E; + } + else { return BAD_FUNC_ARG; + } /* The CRL's signatureAlgorithm OID is written from sType while the * signature is produced from the key, so reject a mismatch. */ - ret = CheckSigTypeForKey(sType, rsaKey, eccKey, NULL, NULL, NULL, NULL, - NULL, NULL, NULL); + ret = CheckSigTypeForKey(sType, rsaKey, eccKey, ed25519Key, ed448Key, + falconKey, mldsaKey, slhDsaKey, NULL, NULL); if (ret != 0) { WOLFSSL_MSG("Signature type does not match signing key"); return ret; @@ -37615,17 +37718,24 @@ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); - heap = GetSigningKeyHeap(rsaKey, eccKey, NULL, NULL, NULL, NULL, NULL, NULL); + heap = GetSigningKeyHeap(rsaKey, eccKey, ed25519Key, ed448Key, mldsaKey, + slhDsaKey, NULL, NULL); /* Copy TBS to output buffer first */ if ((word32)tbsSz > bufSz) return BUFFER_E; XMEMCPY(buf, tbsBuf, (size_t)tbsSz); - /* Only RSA/ECC keys are accepted above, so the signature is a classic - * (non-PQC) one and fits MAX_ENCODED_CLASSIC_SIG_SZ. */ + /* Size the signature buffer from the key in use so post-quantum + * (ML-DSA/SLH-DSA) signatures, which far exceed a classic signature, get + * enough room. */ + maxSigSz = GetSignatureBufferSz(rsaKey, eccKey, ed25519Key, ed448Key, + falconKey, mldsaKey, slhDsaKey, NULL, NULL); + if (maxSigSz <= 0) + return (maxSigSz < 0) ? maxSigSz : ALGO_ID_E; + #ifndef WOLFSSL_NO_MALLOC - certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, heap, + certSignCtx->sig = (byte*)XMALLOC((word32)maxSigSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (certSignCtx->sig == NULL) return MEMORY_E; @@ -37633,15 +37743,21 @@ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, * uninitialized memory if MakeSignature fails before writing sig. */ certSignCtx->sig[0] = 0; #else - /* Don't claim more capacity than the fixed sig buffer really has. */ - if (sigCap > (word32)sizeof(certSignCtx->sig)) - sigCap = (word32)sizeof(certSignCtx->sig); + /* Without dynamic memory the signature is written into the fixed + * CertSignCtx.sig array; reject rather than overflow it. A no-malloc build + * that signs CRLs with large (PQC) keys must size WOLFSSL_MAX_SIG_SZ to fit + * that signature. */ + if ((word32)maxSigSz > (word32)sizeof(certSignCtx->sig)) { + WOLFSSL_MSG("Signature larger than fixed CertSignCtx buffer"); + return BUFFER_E; + } #endif /* Create signature */ sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig, - sigCap, rsaKey, eccKey, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, rng, (word32)sType, heap); + (word32)maxSigSz, rsaKey, eccKey, ed25519Key, + ed448Key, falconKey, mldsaKey, slhDsaKey, NULL, + NULL, rng, (word32)sType, heap); if (sigSz < 0) { #ifndef WOLFSSL_NO_MALLOC XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index d557ac94cd..fa6ad32e36 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -717,6 +717,9 @@ WOLFSSL_API int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, WOLFSSL_API int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, byte* buf, word32 bufSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng); +WOLFSSL_API int wc_SignCRL_ex2(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, int keyType, void* key, + WC_RNG* rng); #endif /* WOLFSSL_CERT_GEN && HAVE_CRL */ WOLFSSL_API int wc_GetDateInfo(const byte* certDate, int certDateSz,