From 09b288000cf48ad61bc48a24454035cfecda528f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Mon, 8 Jun 2026 20:14:46 +0200 Subject: [PATCH] Size cert signature buffers from the key and check sig type vs key MAX_ENCODED_SIG_SZ grows to ~50KB once SLH-DSA is enabled, yet it was used to size PKCS#1/signature scratch and output buffers across the library, wasting stack and heap even for classic RSA/ECC operations. - Add MAX_ENCODED_CLASSIC_SIG_SZ for RSA/DSA/ECC DigestInfo buffers that can never hold a PQC signature. - Size the certificate/CSR signing output buffer from the signing key at runtime instead of the worst-case macro. - Add overridable WOLFSSL_MAX_SIG_SZ for the WOLFSSL_NO_MALLOC buffer. - Reject a signature type that does not match the signing key. --- src/internal.c | 34 ++- src/pk_rsa.c | 8 +- tests/api.c | 19 ++ wolfcrypt/src/asn.c | 441 +++++++++++++++++++++++++++++++------- wolfcrypt/src/signature.c | 8 +- wolfssl/wolfcrypt/asn.h | 4 +- wolfssl/wolfcrypt/types.h | 54 +++-- 7 files changed, 459 insertions(+), 109 deletions(-) diff --git a/src/internal.c b/src/internal.c index 9a80fd7b06..e7b5a2edd7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -33825,12 +33825,14 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, } #endif if (IsAtLeastTLSv1_2(ssl)) { + /* DigestInfo encoding for RSA, never a PQC + * signature -- size to the classic tier. */ WC_DECLARE_VAR(encodedSig, byte, - MAX_ENCODED_SIG_SZ, 0); + MAX_ENCODED_CLASSIC_SIG_SZ, 0); word32 encSigSz; WC_ALLOC_VAR_EX(encodedSig, byte, - MAX_ENCODED_SIG_SZ, ssl->heap, + MAX_ENCODED_CLASSIC_SIG_SZ, ssl->heap, DYNAMIC_TYPE_SIGNATURE, ERROR_OUT(MEMORY_E,exit_dske)); @@ -33840,7 +33842,9 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, TypeHash(ssl->options.peerHashAlgo)); if (encSigSz != args->sigSz || !args->output || XMEMCMP(args->output, encodedSig, - min(encSigSz, MAX_ENCODED_SIG_SZ)) != 0) { + min(encSigSz, + MAX_ENCODED_CLASSIC_SIG_SZ)) + != 0) { ret = VERIFY_SIGN_ERROR; } WC_FREE_VAR_EX(encodedSig, ssl->heap, @@ -35141,9 +35145,14 @@ int SendCertificateVerify(WOLFSSL* ssl) args->verify = &args->output[RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ]; args->extraSz = 0; /* tls 1.2 hash/sig */ - /* build encoded signature buffer */ - ssl->buffers.sig.length = MAX_ENCODED_SIG_SZ; - ssl->buffers.sig.buffer = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, + /* Build encoded signature buffer. This is TLS 1.2 and earlier + * (TLS 1.3 uses SendTls13CertificateVerify), so the signature is + * always classic (RSA/ECC/EdDSA), never PQC -- size to the classic + * tier rather than the (potentially huge) PQC worst case. This + * comfortably holds an ECC/EdDSA signature written directly, or the + * PKCS#1 DigestInfo that is the input to RsaSign. */ + ssl->buffers.sig.length = MAX_ENCODED_CLASSIC_SIG_SZ; + ssl->buffers.sig.buffer = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (ssl->buffers.sig.buffer == NULL) { ERROR_OUT(MEMORY_E, exit_scv); @@ -36354,8 +36363,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) static int ReEncodeSig(WOLFSSL* ssl) { /* For TLS 1.2 re-encode signature */ if (IsAtLeastTLSv1_2(ssl)) { - byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, ssl->heap, - DYNAMIC_TYPE_DIGEST); + /* DigestInfo encoding for RSA, never a PQC signature. */ + byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, + ssl->heap, DYNAMIC_TYPE_DIGEST); if (encodedSig == NULL) return MEMORY_E; ssl->buffers.digest.length = @@ -39489,10 +39499,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) else #endif { + /* DigestInfo encoding for RSA */ #ifndef WOLFSSL_SMALL_STACK - byte encodedSig[MAX_ENCODED_SIG_SZ]; + byte encodedSig[MAX_ENCODED_CLASSIC_SIG_SZ]; #else - byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, + byte* encodedSig = + (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, ssl->heap, DYNAMIC_TYPE_SIGNATURE); if (encodedSig == NULL) { ERROR_OUT(MEMORY_E, exit_dcv); @@ -39507,7 +39519,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (args->sendSz != args->sigSz || !args->output || XMEMCMP(args->output, encodedSig, - min(args->sigSz, MAX_ENCODED_SIG_SZ)) != 0) { + min(args->sigSz, MAX_ENCODED_CLASSIC_SIG_SZ)) != 0) { ret = VERIFY_CERT_ERROR; } diff --git a/src/pk_rsa.c b/src/pk_rsa.c index 685fdb9fd1..63ab70abd2 100644 --- a/src/pk_rsa.c +++ b/src/pk_rsa.c @@ -3117,7 +3117,7 @@ int wolfSSL_RSA_sign_mgf(int hashAlg, const unsigned char* hash, #else WC_RNG _tmpRng[1]; WC_RNG* tmpRng = _tmpRng; - byte encodedSig[MAX_ENCODED_SIG_SZ]; + byte encodedSig[MAX_ENCODED_CLASSIC_SIG_SZ]; #endif unsigned int encSz = 0; @@ -3158,7 +3158,7 @@ int wolfSSL_RSA_sign_mgf(int hashAlg, const unsigned char* hash, #ifdef WOLFSSL_SMALL_STACK if (ret == 1) { /* Allocate encoded signature buffer if doing PKCS#1 padding. */ - encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, NULL, + encodedSig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, NULL, DYNAMIC_TYPE_SIGNATURE); if (encodedSig == NULL) { ret = 0; @@ -3314,10 +3314,10 @@ int wolfSSL_RSA_verify_mgf(int hashAlg, const unsigned char* hash, #ifdef WOLFSSL_SMALL_STACK unsigned char* encodedSig = NULL; #else - unsigned char encodedSig[MAX_ENCODED_SIG_SZ]; + unsigned char encodedSig[MAX_ENCODED_CLASSIC_SIG_SZ]; #endif unsigned char* sigDec = NULL; - unsigned int len = MAX_ENCODED_SIG_SZ; + unsigned int len = MAX_ENCODED_CLASSIC_SIG_SZ; int verLen = 0; #if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5, 1)) && !defined(HAVE_SELFTEST) enum wc_HashType hType = WC_HASH_TYPE_NONE; diff --git a/tests/api.c b/tests/api.c index 9cac424dca..31c9a02a06 100644 --- a/tests/api.c +++ b/tests/api.c @@ -23304,6 +23304,11 @@ static int test_wc_SignCert_cb(void) /* Invalid keyType for ECC signature */ ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, FOURK_BUF, ED25519_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* sigType/key family mismatch: an RSA signature OID against an ECC + * key must be rejected with ALGO_ID_E before any signing happens. */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, CTC_SHA256wRSA, der, + FOURK_BUF, ECC_TYPE, mockSignCb, &signCtx, &rng), + WC_NO_ERR_TRACE(ALGO_ID_E)); #endif ret = wc_ecc_free(&key); @@ -23390,6 +23395,11 @@ static int test_wc_SignCert_cb(void) /* Invalid keyType */ ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, FOURK_BUF, ED448_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* sigType/key family mismatch: an ECDSA signature OID against an RSA + * key must be rejected with ALGO_ID_E before any signing happens. */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, CTC_SHA256wECDSA, der, + FOURK_BUF, RSA_TYPE, mockSignCb, &signCtx, &rng), + WC_NO_ERR_TRACE(ALGO_ID_E)); #endif ret = wc_FreeRsaKey(&key); @@ -24329,6 +24339,15 @@ static int test_wc_MakeCRL_max_crlnum(void) ExpectIntGT(crlSz, 0); } + /* --- Negative: a sigType whose family does not match the signing key + * must be rejected before any signature is produced. The RSA key here + * paired with an ECDSA OID must return ALGO_ID_E. --- */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wc_SignCRL_ex(tbsBuf, tbsSz, CTC_SHA256wECDSA, + crlBuf, (word32)bufSz, &rsaKey, NULL, &rng), + WC_NO_ERR_TRACE(ALGO_ID_E)); + } + /* --- Decode the CRL and verify CRL number --- */ if (EXPECT_SUCCESS()) { ExpectNotNull(decodedCrl = d2i_X509_CRL(NULL, crlBuf, crlSz)); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index a80ec115b5..96ee114b33 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -17030,7 +17030,9 @@ int ConfirmSignature(SignatureCtx* sigCtx, ERROR_OUT(MEMORY_E, exit_cs); } #endif - if (sigSz > MAX_ENCODED_SIG_SZ) { + /* RSA signature copied into sigCpy, which under + * WOLFSSL_NO_MALLOC is sized to MAX_ENCODED_CLASSIC_SIG_SZ. */ + if (sigSz > MAX_ENCODED_CLASSIC_SIG_SZ) { WOLFSSL_MSG("Verify Signature is too big"); ERROR_OUT(BUFFER_E, exit_cs); } @@ -17058,7 +17060,7 @@ int ConfirmSignature(SignatureCtx* sigCtx, WOLFSSL_MSG("Verify Signature is too small"); ERROR_OUT(BUFFER_E, exit_cs); } - else if (sigSz > MAX_ENCODED_SIG_SZ) { + else if (sigSz > MAX_ENCODED_CLASSIC_SIG_SZ) { WOLFSSL_MSG("Verify Signature is too big"); ERROR_OUT(BUFFER_E, exit_cs); } @@ -17747,13 +17749,15 @@ int ConfirmSignature(SignatureCtx* sigCtx, if (sigCtx->CertAtt.verifyByTSIP_SCE == 1) break; #endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) - byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, + /* Holds a PKCS#1 DigestInfo encoding for RSA verification, + * never a PQC signature -- size to the classic tier. */ + byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, sigCtx->heap, DYNAMIC_TYPE_TMP_BUFFER); if (encodedSig == NULL) { ERROR_OUT(MEMORY_E, exit_cs); } #else - byte encodedSig[MAX_ENCODED_SIG_SZ]; + byte encodedSig[MAX_ENCODED_CLASSIC_SIG_SZ]; #endif verifySz = ret; @@ -28380,6 +28384,270 @@ static int InternalSignCb(const byte* in, word32 inLen, #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */ +/* Return the buffer size needed to hold the raw signature that + * MakeSignature() will produce for the provided key, or a negative error code + * if it cannot be determined. The per-algorithm branches mirror the dispatch + * in MakeSignature() so the two stay in agreement; exactly one key pointer is + * expected to be non-NULL. + * + * Sizing the signature buffer from the key (rather than the worst-case + * MAX_ENCODED_SIG_SZ) avoids allocating tens of KB for classic keys in builds + * that merely enable a PQC algorithm, and is required for LMS/XMSS whose + * parameter-dependent signatures can exceed MAX_ENCODED_SIG_SZ. */ +static int GetSignatureBufferSz(RsaKey* rsaKey, ecc_key* eccKey, + ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, + wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey, + XmssKey* xmssKey) +{ + int sigSz = ALGO_ID_E; + + (void)rsaKey; (void)eccKey; (void)ed25519Key; (void)ed448Key; + (void)falconKey; (void)mldsaKey; (void)slhDsaKey; (void)lmsKey; + (void)xmssKey; + +#ifndef NO_RSA + if (rsaKey != NULL) + sigSz = wc_RsaEncryptSize(rsaKey); +#endif +#ifdef HAVE_ECC + if (eccKey != NULL) + sigSz = wc_ecc_sig_size(eccKey); +#endif +#if defined(HAVE_ED25519) + if (ed25519Key != NULL) + sigSz = wc_ed25519_sig_size(ed25519Key); +#endif +#if defined(HAVE_ED448) + if (ed448Key != NULL) + sigSz = wc_ed448_sig_size(ed448Key); +#endif +#if defined(HAVE_FALCON) + if (falconKey != NULL) + sigSz = wc_falcon_sig_size(falconKey); +#endif +#if defined(WOLFSSL_HAVE_MLDSA) + if (mldsaKey != NULL) { + int len = 0; + int rc = wc_MlDsaKey_GetSigLen(mldsaKey, &len); + sigSz = (rc == 0) ? len : rc; + } +#endif +#if defined(WOLFSSL_HAVE_SLHDSA) + if (slhDsaKey != NULL) + sigSz = wc_SlhDsaKey_SigSize(slhDsaKey); +#endif +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + if (lmsKey != NULL) { + word32 len = 0; + int rc = wc_LmsKey_GetSigLen(lmsKey, &len); + sigSz = (rc == 0) ? (int)len : rc; + } +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if (xmssKey != NULL) { + word32 len = 0; + int rc = wc_XmssKey_GetSigLen(xmssKey, &len); + sigSz = (rc == 0) ? (int)len : rc; + } +#endif + + /* sigSz still ALGO_ID_E means no (compiled-in) key matched; a getter + * returning 0 means the matched key was not usable. */ + if (sigSz == 0) + sigSz = BAD_FUNC_ARG; + return sigSz; +} + +#ifndef NO_RSA +/* True if sType is an RSA signature-algorithm OID (any hash, incl. PSS). */ +static int IsRsaSigType(int sType) +{ + switch (sType) { + case CTC_MD2wRSA: + case CTC_MD5wRSA: + case CTC_SHAwRSA: + case CTC_SHA224wRSA: + case CTC_SHA256wRSA: + case CTC_SHA384wRSA: + case CTC_SHA512wRSA: + case CTC_SHA3_224wRSA: + case CTC_SHA3_256wRSA: + case CTC_SHA3_384wRSA: + case CTC_SHA3_512wRSA: + case CTC_RSASSAPSS: + return 1; + default: + return 0; + } +} +#endif /* !NO_RSA */ +#ifdef HAVE_ECC +/* True if sType is an ECDSA (or SM2) signature-algorithm OID (any hash). */ +static int IsEccSigType(int sType) +{ + switch (sType) { + case CTC_SHAwECDSA: + case CTC_SHA224wECDSA: + case CTC_SHA256wECDSA: + case CTC_SHA384wECDSA: + case CTC_SHA512wECDSA: + case CTC_SHA3_224wECDSA: + case CTC_SHA3_256wECDSA: + case CTC_SHA3_384wECDSA: + case CTC_SHA3_512wECDSA: + #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) + case CTC_SM3wSM2: + #endif + return 1; + default: + return 0; + } +} +#endif /* HAVE_ECC */ + +/* Return the heap hint of whichever signing key is set, or NULL if none does + * (falcon_key has no heap member). Centralizes the lookup shared by the + * certificate/CSR signing entry points. */ +static void* GetSigningKeyHeap(RsaKey* rsaKey, ecc_key* eccKey, + ed25519_key* ed25519Key, ed448_key* ed448Key, wc_MlDsaKey* mldsaKey, + SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey) +{ + (void)rsaKey; (void)eccKey; (void)ed25519Key; (void)ed448Key; + (void)mldsaKey; (void)slhDsaKey; (void)lmsKey; (void)xmssKey; +#ifndef NO_RSA + if (rsaKey != NULL) + return rsaKey->heap; +#endif +#ifdef HAVE_ECC + if (eccKey != NULL) + return eccKey->heap; +#endif +#ifdef HAVE_ED25519 + if (ed25519Key != NULL) + return ed25519Key->heap; +#endif +#ifdef HAVE_ED448 + if (ed448Key != NULL) + return ed448Key->heap; +#endif +#ifdef WOLFSSL_HAVE_MLDSA + if (mldsaKey != NULL) + return mldsaKey->heap; +#endif +#ifdef WOLFSSL_HAVE_SLHDSA + if (slhDsaKey != NULL) + return slhDsaKey->heap; +#endif +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + if (lmsKey != NULL) + return lmsKey->heap; +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if (xmssKey != NULL) + return xmssKey->heap; +#endif + return NULL; +} + +/* Verify that the requested signature type -- which becomes the certificate's + * signatureAlgorithm OID -- is consistent with the signing key. The signature + * is produced by dispatching on the key while the OID is written from sType, + * so a mismatch would emit a certificate whose advertised algorithm + * contradicts the key that signed it (e.g. leaving the wc_InitCert default of + * CTC_SHA256wRSA on a non-RSA key). For RSA/ECC the hash is free to vary, so + * the whole signature-algorithm family is accepted; the remaining algorithms + * have a single (level/parameter-determined) OID. Returns 0 if consistent or + * if there is no key to check, ALGO_ID_E on a mismatch. */ +static int CheckSigTypeForKey(int sType, RsaKey* rsaKey, ecc_key* eccKey, + ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, + wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey, + XmssKey* xmssKey) +{ + (void)sType; + (void)rsaKey; (void)eccKey; (void)ed25519Key; (void)ed448Key; + (void)falconKey; (void)mldsaKey; (void)slhDsaKey; (void)lmsKey; + (void)xmssKey; + +#ifdef WOLFSSL_DUAL_ALG_CERTS + /* sigType 0 indicates preTBS encoding: no signatureAlgorithm to validate. */ + if (sType == 0) + return 0; +#endif + +#ifndef NO_RSA + if (rsaKey != NULL) + return IsRsaSigType(sType) ? 0 : ALGO_ID_E; +#endif +#ifdef HAVE_ECC + if (eccKey != NULL) + return IsEccSigType(sType) ? 0 : ALGO_ID_E; +#endif +#ifdef HAVE_ED25519 + if (ed25519Key != NULL) + return (sType == CTC_ED25519) ? 0 : ALGO_ID_E; +#endif +#ifdef HAVE_ED448 + if (ed448Key != NULL) + return (sType == CTC_ED448) ? 0 : ALGO_ID_E; +#endif +#ifdef HAVE_FALCON + if (falconKey != NULL) { + if (falconKey->level == 1) + return (sType == CTC_FALCON_LEVEL1) ? 0 : ALGO_ID_E; + if (falconKey->level == 5) + return (sType == CTC_FALCON_LEVEL5) ? 0 : ALGO_ID_E; + return ALGO_ID_E; + } +#endif +#ifdef WOLFSSL_HAVE_MLDSA + if (mldsaKey != NULL) { + if (mldsaKey->params == NULL) + return ALGO_ID_E; + #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT + if (mldsaKey->params->level == WC_ML_DSA_44_DRAFT) + return (sType == CTC_DILITHIUM_LEVEL2) ? 0 : ALGO_ID_E; + if (mldsaKey->params->level == WC_ML_DSA_65_DRAFT) + return (sType == CTC_DILITHIUM_LEVEL3) ? 0 : ALGO_ID_E; + if (mldsaKey->params->level == WC_ML_DSA_87_DRAFT) + return (sType == CTC_DILITHIUM_LEVEL5) ? 0 : ALGO_ID_E; + #endif + if (mldsaKey->level == WC_ML_DSA_44) + return (sType == CTC_ML_DSA_44) ? 0 : ALGO_ID_E; + if (mldsaKey->level == WC_ML_DSA_65) + return (sType == CTC_ML_DSA_65) ? 0 : ALGO_ID_E; + if (mldsaKey->level == WC_ML_DSA_87) + return (sType == CTC_ML_DSA_87) ? 0 : ALGO_ID_E; + return ALGO_ID_E; + } +#endif +#ifdef WOLFSSL_HAVE_SLHDSA + if (slhDsaKey != NULL) { + /* SLH-DSA uses one OID per parameter set for both the key and the + * signature, so the key OID sum equals the CTC signature value. */ + int oid; + if (slhDsaKey->params == NULL) + return ALGO_ID_E; + oid = wc_SlhDsaParamToOid(slhDsaKey->params->param); + if (oid < 0) + return ALGO_ID_E; + return (sType == oid) ? 0 : ALGO_ID_E; + } +#endif +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + if (lmsKey != NULL) + return (sType == CTC_HSS_LMS) ? 0 : ALGO_ID_E; +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if (xmssKey != NULL) { + if (xmssKey->is_xmssmt) + return (sType == CTC_XMSSMT) ? 0 : ALGO_ID_E; + return (sType == CTC_XMSS) ? 0 : ALGO_ID_E; + } +#endif + + return 0; +} + /* Make signature from buffer (sz), write to sig (sigSz) * This function now uses MakeSignatureCb internally for RSA and ECC, * eliminating code duplication. Ed25519, Ed448, and post-quantum algorithms @@ -29658,29 +29926,25 @@ static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz, LmsKey* lmsKey, XmssKey* xmssKey, WC_RNG* rng) { int sigSz = 0; + int ret; void* heap = NULL; - /* The signature buffer must hold the largest signature any supported key - * type can produce. LMS/XMSS signatures are parameter-dependent and can - * exceed MAX_ENCODED_SIG_SZ, so size them from the key at runtime. */ - word32 maxSigSz = MAX_ENCODED_SIG_SZ; + /* The signature buffer is sized from the key at runtime. */ + int maxSigSz; CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; - (void)lmsKey; - (void)xmssKey; - XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); if (requestSz < 0) return requestSz; - /* locate ctx */ + /* Async crypto reuses the signing key's embedded CertSignCtx; only RSA and + * ECC keys carry one. */ if (rsaKey) { #ifndef NO_RSA #ifdef WOLFSSL_ASYNC_CRYPT certSignCtx = &rsaKey->certSignCtx; #endif - heap = rsaKey->heap; #else return NOT_COMPILED_IN; #endif /* NO_RSA */ @@ -29690,67 +29954,49 @@ static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz, #ifdef WOLFSSL_ASYNC_CRYPT certSignCtx = &eccKey->certSignCtx; #endif - heap = eccKey->heap; #else return NOT_COMPILED_IN; #endif /* HAVE_ECC */ } -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) - else if (lmsKey) { - word32 lmsSigSz = 0; - /* The signature algorithm OID is written from sType. Reject a - * mismatch so we never emit a cert whose signatureAlgorithm - * contradicts its HSS/LMS public key. */ - if (sType != CTC_HSS_LMS) { - WOLFSSL_MSG("LMS key requires CTC_HSS_LMS signature type"); - return ALGO_ID_E; - } - heap = lmsKey->heap; - if (wc_LmsKey_GetSigLen(lmsKey, &lmsSigSz) != 0) - return BAD_FUNC_ARG; - if (lmsSigSz > maxSigSz) - maxSigSz = lmsSigSz; + heap = GetSigningKeyHeap(rsaKey, eccKey, ed25519Key, ed448Key, mldsaKey, + slhDsaKey, lmsKey, xmssKey); + + /* The signatureAlgorithm OID is written from sType while the signature is + * produced from the key, so reject a mismatch rather than emit a cert + * whose advertised algorithm contradicts the signing key. */ + ret = CheckSigTypeForKey(sType, rsaKey, eccKey, ed25519Key, ed448Key, + falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey); + if (ret != 0) { + WOLFSSL_MSG("Signature type does not match signing key"); + return ret; } -#endif -#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) - else if (xmssKey) { - word32 xmssSigSz = 0; - /* sType must match the tree variant (XMSS vs XMSS^MT) so the - * signatureAlgorithm OID agrees with the XMSS public key OID that - * MakeAnyCert derived from key->is_xmssmt. */ - if (xmssKey->is_xmssmt ? (sType != CTC_XMSSMT) - : (sType != CTC_XMSS)) { - WOLFSSL_MSG("XMSS signature type does not match key variant"); - return ALGO_ID_E; - } - heap = xmssKey->heap; - if (wc_XmssKey_GetSigLen(xmssKey, &xmssSigSz) != 0) - return BAD_FUNC_ARG; - if (xmssSigSz > maxSigSz) - maxSigSz = xmssSigSz; - } -#endif + + /* Size the signature buffer from the key in use. */ + maxSigSz = GetSignatureBufferSz(rsaKey, eccKey, ed25519Key, ed448Key, + falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey); + if (maxSigSz <= 0) + return (maxSigSz < 0) ? maxSigSz : ALGO_ID_E; #ifndef WOLFSSL_NO_MALLOC if (certSignCtx->sig == NULL) { - certSignCtx->sig = (byte*)XMALLOC(maxSigSz, heap, + certSignCtx->sig = (byte*)XMALLOC((word32)maxSigSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (certSignCtx->sig == NULL) return MEMORY_E; } #else /* Without dynamic memory the signature buffer is a fixed - * MAX_ENCODED_SIG_SZ array in CertSignCtx. LMS/XMSS signatures are + * WOLFSSL_MAX_SIG_SZ array in CertSignCtx. LMS/XMSS signatures are * parameter-dependent and can be larger, so reject rather than overflow * the fixed buffer. */ - if (maxSigSz > MAX_ENCODED_SIG_SZ) { - WOLFSSL_MSG("LMS/XMSS signature larger than fixed CertSignCtx buffer"); + if ((word32)maxSigSz > WOLFSSL_MAX_SIG_SZ) { + WOLFSSL_MSG("Signature larger than fixed CertSignCtx buffer"); return BUFFER_E; } #endif sigSz = MakeSignature(certSignCtx, buf, (word32)requestSz, certSignCtx->sig, - maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key, + (word32)maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key, falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey, rng, (word32)sType, heap); #ifdef WOLFSSL_ASYNC_CRYPT @@ -29805,6 +30051,7 @@ int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf, SlhDsaKey* slhDsaKey = NULL; int ret = 0; int headerSz; + int maxSigSz; void* heap = NULL; CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; @@ -29865,13 +30112,13 @@ int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf, return BAD_FUNC_ARG; } - /* locate ctx */ + /* Async crypto reuses the signing key's embedded CertSignCtx; only RSA and + * ECC keys carry one. */ if (rsaKey) { #ifndef NO_RSA #ifdef WOLFSSL_ASYNC_CRYPT certSignCtx = &rsaKey->certSignCtx; #endif - heap = rsaKey->heap; #else return NOT_COMPILED_IN; #endif /* NO_RSA */ @@ -29881,23 +30128,45 @@ int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf, #ifdef WOLFSSL_ASYNC_CRYPT certSignCtx = &eccKey->certSignCtx; #endif - heap = eccKey->heap; #else return NOT_COMPILED_IN; #endif /* HAVE_ECC */ } + heap = GetSigningKeyHeap(rsaKey, eccKey, ed25519Key, ed448Key, mldsaKey, + slhDsaKey, NULL, NULL); + + /* The signatureAlgorithm OID is written from sType while the signature is + * produced from the key, so reject a mismatch rather than emit a cert + * whose advertised algorithm contradicts the signing key. */ + 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; + } + + /* Size the signature buffer from the key in use. */ + 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 if (certSignCtx->sig == NULL) { - certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap, + certSignCtx->sig = (byte*)XMALLOC((word32)maxSigSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (certSignCtx->sig == NULL) return MEMORY_E; } +#else + if ((word32)maxSigSz > WOLFSSL_MAX_SIG_SZ) { + WOLFSSL_MSG("Signature larger than fixed CertSignCtx buffer"); + return BUFFER_E; + } #endif ret = MakeSignature(certSignCtx, buf, (word32)bufSz, certSignCtx->sig, - MAX_ENCODED_SIG_SZ, rsaKey, eccKey, ed25519Key, ed448Key, + (word32)maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key, falconKey, mldsaKey, slhDsaKey, NULL, NULL, rng, (word32)sType, heap); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { @@ -30050,6 +30319,7 @@ int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz, WC_RNG* rng) { int sigSz = 0; + word32 sigCap = MAX_ENCODED_CLASSIC_SIG_SZ; CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; @@ -30076,22 +30346,40 @@ int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz, return NOT_COMPILED_IN; #endif + /* The callback produces the signature for keyType while the cert's + * signatureAlgorithm OID is written from sType, so reject a family + * mismatch (e.g. an ECDSA OID with an RSA key). */ +#ifndef NO_RSA + if (keyType == RSA_TYPE && !IsRsaSigType(sType)) + return ALGO_ID_E; +#endif +#ifdef HAVE_ECC + if (keyType == ECC_TYPE && !IsEccSigType(sType)) + return ALGO_ID_E; +#endif + XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); if (requestSz < 0) { return requestSz; } + /* keyType is restricted to RSA_TYPE/ECC_TYPE above, so the signature is + * a classic (non-PQC) one and fits MAX_ENCODED_CLASSIC_SIG_SZ. */ #ifndef WOLFSSL_NO_MALLOC - certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, NULL, + certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (certSignCtx->sig == NULL) { return MEMORY_E; } +#else + /* Don't claim more capacity than the fixed sig buffer really has. */ + if (sigCap > (word32)sizeof(certSignCtx->sig)) + sigCap = (word32)sizeof(certSignCtx->sig); #endif sigSz = MakeSignatureCb(certSignCtx, buf, (word32)requestSz, - certSignCtx->sig, MAX_ENCODED_SIG_SZ, sType, keyType, + certSignCtx->sig, sigCap, sType, keyType, signCb, signCtx, rng, NULL); #ifdef WOLFSSL_ASYNC_CRYPT @@ -36611,6 +36899,7 @@ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, { int ret; int sigSz; + word32 sigCap = MAX_ENCODED_CLASSIC_SIG_SZ; CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; void* heap = NULL; @@ -36622,38 +36911,44 @@ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, if (rsaKey != NULL && eccKey != NULL) 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); + if (ret != 0) { + WOLFSSL_MSG("Signature type does not match signing key"); + return ret; + } + XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); -#ifndef NO_RSA - if (rsaKey != NULL) { - heap = rsaKey->heap; - } -#endif -#ifdef HAVE_ECC - if (eccKey != NULL) { - heap = eccKey->heap; - } -#endif + heap = GetSigningKeyHeap(rsaKey, eccKey, NULL, NULL, NULL, NULL, 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. */ #ifndef WOLFSSL_NO_MALLOC - certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap, + certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, heap, DYNAMIC_TYPE_TMP_BUFFER); if (certSignCtx->sig == NULL) return MEMORY_E; /* Initialize first byte to avoid static analysis warnings about using * 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); #endif /* Create signature */ sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig, - MAX_ENCODED_SIG_SZ, rsaKey, eccKey, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, rng, (word32)sType, heap); + sigCap, rsaKey, eccKey, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, rng, (word32)sType, heap); if (sigSz < 0) { #ifndef WOLFSSL_NO_MALLOC XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/wolfcrypt/src/signature.c b/wolfcrypt/src/signature.c index 74753f07d4..a45bdd0d92 100644 --- a/wolfcrypt/src/signature.c +++ b/wolfcrypt/src/signature.c @@ -46,8 +46,10 @@ #ifndef MAX_DER_DIGEST_ASN_SZ #define MAX_DER_DIGEST_ASN_SZ 36 #endif - #ifndef MAX_ENCODED_SIG_SZ - #define MAX_ENCODED_SIG_SZ 1024 /* Supports 8192 bit keys */ + /* Fallback when asn.h (which defines MAX_ENCODED_CLASSIC_SIG_SZ) is not + * available. Sized to hold an RSA-modulus signature. */ + #ifndef MAX_ENCODED_CLASSIC_SIG_SZ + #define MAX_ENCODED_CLASSIC_SIG_SZ 1024 /* Supports 8192 bit keys */ #endif #endif @@ -289,7 +291,7 @@ int wc_SignatureVerifyHash( #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) byte *plain_data; #else - ALIGN64 byte plain_data[MAX_ENCODED_SIG_SZ]; + ALIGN64 byte plain_data[MAX_ENCODED_CLASSIC_SIG_SZ]; #endif /* Make sure the plain text output is at least key size */ diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index bf8e15fa53..8c82294d12 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1541,7 +1541,9 @@ struct SignatureCtx { #endif #if !defined(NO_RSA) || !defined(NO_DSA) #ifdef WOLFSSL_NO_MALLOC - byte sigCpy[MAX_ENCODED_SIG_SZ]; + /* Holds a copy of the RSA/DSA signature being verified, which is at most + * an RSA-modulus-sized value -- never a (much larger) PQC signature. */ + byte sigCpy[MAX_ENCODED_CLASSIC_SIG_SZ]; #else byte* sigCpy; #endif diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index eb11f8436e..cf8900f6ee 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2375,29 +2375,39 @@ enum Max_ASN { DSA_INTS = 5, /* DSA ints in private key */ MAX_SALT_SIZE = 64, /* MAX PKCS Salt length */ MAX_IV_SIZE = 64, /* MAX PKCS Iv length */ + /* Max classic sig (RSA/DSA/ECC); separate from MAX_ENCODED_SIG_SZ so + * PKCS#1/verify buffers stay small when PQC is enabled for verify-only. */ +#if !defined(NO_RSA) +#if defined(USE_FAST_MATH) && defined(FP_MAX_BITS) + MAX_ENCODED_CLASSIC_SIG_SZ = FP_MAX_BITS / 16, +#elif (defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH)) && \ + defined(SP_INT_BITS) + MAX_ENCODED_CLASSIC_SIG_SZ = WC_BITS_TO_BYTES(SP_INT_BITS), +#elif defined(WOLFSSL_HAPROXY) + MAX_ENCODED_CLASSIC_SIG_SZ = 1024, /* Supports 8192 bit keys */ +#else + MAX_ENCODED_CLASSIC_SIG_SZ = 512, /* Supports 4096 bit keys */ +#endif +#elif defined(HAVE_ECC) + MAX_ENCODED_CLASSIC_SIG_SZ = 140, +#elif defined(HAVE_ED448) + MAX_ENCODED_CLASSIC_SIG_SZ = 114, /* Ed448 signature is 114 bytes */ +#else + MAX_ENCODED_CLASSIC_SIG_SZ = 64, /* Ed25519 signature is 64 bytes */ +#endif + + /* Largest signature any enabled algorithm can produce. Used to size the + * actual signature-output buffers. PQC signatures are large, so prefer + * runtime sizing (see GetSignatureBufferSz in asn.c) where the key is + * available. */ #ifdef WOLFSSL_HAVE_SLHDSA /* Largest raw SLH-DSA signature (SHAKE-256f) is 49856 bytes; round up * to leave headroom for ASN.1 wrapping (BIT STRING tag + length). */ MAX_ENCODED_SIG_SZ = 51200, #elif defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) MAX_ENCODED_SIG_SZ = 5120, -#elif !defined(NO_RSA) -#if defined(USE_FAST_MATH) && defined(FP_MAX_BITS) - MAX_ENCODED_SIG_SZ = FP_MAX_BITS / 16, -#elif (defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_MATH)) && \ - defined(SP_INT_BITS) - MAX_ENCODED_SIG_SZ = WC_BITS_TO_BYTES(SP_INT_BITS), -#elif defined(WOLFSSL_HAPROXY) - MAX_ENCODED_SIG_SZ = 1024, /* Supports 8192 bit keys */ #else - MAX_ENCODED_SIG_SZ = 512, /* Supports 4096 bit keys */ -#endif -#elif defined(HAVE_ECC) - MAX_ENCODED_SIG_SZ = 140, -#elif defined(HAVE_CURVE448) - MAX_ENCODED_SIG_SZ = 114, -#else - MAX_ENCODED_SIG_SZ = 64, + MAX_ENCODED_SIG_SZ = MAX_ENCODED_CLASSIC_SIG_SZ, #endif MAX_ALGO_SZ = 20, MAX_LENGTH_SZ = WOLFSSL_ASN_MAX_LENGTH_SZ, /* Max length size for DER encoding */ @@ -2457,6 +2467,16 @@ enum Max_ASN { #define MAX_SIG_SZ MAX_ENCODED_SIG_SZ +/* Size of the fixed signature buffer embedded in CertSignCtx under + * WOLFSSL_NO_MALLOC, and the reject threshold used by the cert/CSR signing + * paths when dynamic memory is unavailable. Defaults to the largest signature + * any enabled algorithm can produce. Override (e.g. via user_settings.h) to + * fit a specific LMS/XMSS parameter set, or to shrink builds that only sign + * with classic/compact algorithms. */ +#ifndef WOLFSSL_MAX_SIG_SZ +#define WOLFSSL_MAX_SIG_SZ MAX_ENCODED_SIG_SZ +#endif + #if defined(WOLFSSL_CERT_GEN) || defined(HAVE_OCSP_RESPONDER) /* Used in asn.c MakeSignature for ECC and RSA non-blocking/async */ enum CertSignState { @@ -2468,7 +2488,7 @@ enum Max_ASN { typedef struct CertSignCtx { #ifdef WOLFSSL_NO_MALLOC - byte sig[MAX_ENCODED_SIG_SZ]; + byte sig[WOLFSSL_MAX_SIG_SZ]; byte digest[WC_MAX_DIGEST_SIZE]; #ifndef NO_RSA byte encSig[MAX_DER_DIGEST_SZ];