diff --git a/doc/dox_comments/header_files/asn.h b/doc/dox_comments/header_files/asn.h index 4ff2c9d6ad..b4995dd83a 100644 --- a/doc/dox_comments/header_files/asn.h +++ b/doc/dox_comments/header_files/asn.h @@ -253,18 +253,29 @@ int wc_DhPublicKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, 3. Calls the user-provided callback to perform the actual signing 4. Encodes the signature into the certificate/CSR DER structure + NOTE: Only RSA and ECC key types are supported. Ed25519, Ed448, and + post-quantum algorithms (Falcon, Dilithium, SPHINCS+) sign messages + directly rather than hashes, so they cannot use this callback-based API. + Use wc_SignCert_ex for those algorithms. + + NOTE: This function does NOT support async crypto (WOLFSSL_ASYNC_CRYPT). + The internal context is local to this function and cannot persist across + async re-entry. + \param requestSz Size of the certificate body to sign (from Cert.bodySz). \param sType Signature algorithm type (e.g., CTC_SHA256wRSA, CTC_SHA256wECDSA). \param buf Buffer containing the certificate/CSR DER data to sign. \param buffSz Total size of the buffer (must be large enough for signature). - \param keyType Type of key used for signing (RSA_TYPE, ECC_TYPE, etc.). + \param keyType Type of key used for signing. Only RSA_TYPE and ECC_TYPE + are supported. \param signCb User-provided signing callback function. \param signCtx Context pointer passed to the signing callback. \param rng Random number generator (may be NULL if not needed). \return Size of the signed certificate/CSR on success. - \return BAD_FUNC_ARG if signCb is NULL or other parameters are invalid. + \return BAD_FUNC_ARG if signCb or buf is NULL, buffSz is 0, or keyType + is not RSA_TYPE or ECC_TYPE. \return BUFFER_E if the buffer is too small for the signed certificate. \return MEMORY_E if memory allocation fails. \return Negative error code on other failures. diff --git a/tests/api.c b/tests/api.c index d24a35a0a8..02a1b4c7ed 100644 --- a/tests/api.c +++ b/tests/api.c @@ -20041,6 +20041,20 @@ static int mockSignCb(const byte* in, word32 inLen, byte* out, word32* outLen, return ret; } + +/* Mock callback that always returns an error for testing */ +static int mockSignCbError(const byte* in, word32 inLen, byte* out, + word32* outLen, int sigAlgo, int keyType, void* ctx) +{ + (void)in; + (void)inLen; + (void)out; + (void)outLen; + (void)sigAlgo; + (void)keyType; + (void)ctx; + return BAD_STATE_E; /* Return an error */ +} #endif #ifdef WOLFSSL_CERT_SIGN_CB @@ -20058,12 +20072,14 @@ static int test_wc_SignCert_cb(void) WC_RNG rng; ecc_key key; MockSignCtx signCtx; + DecodedCert decodedCert; int ret; XMEMSET(&rng, 0, sizeof(WC_RNG)); XMEMSET(&key, 0, sizeof(ecc_key)); XMEMSET(&cert, 0, sizeof(Cert)); XMEMSET(&signCtx, 0, sizeof(MockSignCtx)); + XMEMSET(&decodedCert, 0, sizeof(DecodedCert)); ExpectIntEQ(wc_InitRng(&rng), 0); ExpectIntEQ(wc_ecc_init(&key), 0); @@ -20097,9 +20113,37 @@ static int test_wc_SignCert_cb(void) /* Verify the certificate was created properly */ ExpectIntGT(derSize, 0); + /* Parse the certificate and verify it's well-formed */ + if (EXPECT_SUCCESS()) { + wc_InitDecodedCert(&decodedCert, der, (word32)derSize, NULL); + ExpectIntEQ(wc_ParseCert(&decodedCert, CERT_TYPE, NO_VERIFY, NULL), + 0); + /* Verify signature algorithm matches what we set */ + ExpectIntEQ(decodedCert.signatureOID, CTC_SHA256wECDSA); + wc_FreeDecodedCert(&decodedCert); + } + /* Test error cases */ + /* NULL callback */ ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, FOURK_BUF, ECC_TYPE, NULL, &signCtx, &rng), BAD_FUNC_ARG); + /* NULL buffer */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, NULL, + FOURK_BUF, ECC_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* Zero buffer size */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + 0, ECC_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* Negative requestSz - should return the negative value */ + ExpectIntLT(wc_SignCert_cb(-1, cert.sigType, der, + FOURK_BUF, ECC_TYPE, mockSignCb, &signCtx, &rng), 0); + /* Callback returning error */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + FOURK_BUF, ECC_TYPE, mockSignCbError, &signCtx, &rng), BAD_STATE_E); + #ifndef NO_RSA + /* Invalid keyType for ECC signature */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + FOURK_BUF, ED25519_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + #endif ret = wc_ecc_free(&key); ExpectIntEQ(ret, 0); @@ -20117,12 +20161,14 @@ static int test_wc_SignCert_cb(void) WC_RNG rng; RsaKey key; MockSignCtx signCtx; + DecodedCert decodedCert; int ret; XMEMSET(&rng, 0, sizeof(WC_RNG)); XMEMSET(&key, 0, sizeof(RsaKey)); XMEMSET(&cert, 0, sizeof(Cert)); XMEMSET(&signCtx, 0, sizeof(MockSignCtx)); + XMEMSET(&decodedCert, 0, sizeof(DecodedCert)); ExpectIntEQ(wc_InitRng(&rng), 0); ExpectIntEQ(wc_InitRsaKey(&key, NULL), 0); @@ -20156,9 +20202,34 @@ static int test_wc_SignCert_cb(void) /* Verify the certificate was created properly */ ExpectIntGT(derSize, 0); - /* Test error case - NULL callback */ + /* Parse the certificate and verify it's well-formed */ + if (EXPECT_SUCCESS()) { + wc_InitDecodedCert(&decodedCert, der, (word32)derSize, NULL); + ExpectIntEQ(wc_ParseCert(&decodedCert, CERT_TYPE, NO_VERIFY, NULL), + 0); + /* Verify signature algorithm matches what we set */ + ExpectIntEQ(decodedCert.signatureOID, CTC_SHA256wRSA); + wc_FreeDecodedCert(&decodedCert); + } + + /* Test error cases */ + /* NULL callback */ ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, FOURK_BUF, RSA_TYPE, NULL, &signCtx, &rng), BAD_FUNC_ARG); + /* NULL buffer */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, NULL, + FOURK_BUF, RSA_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* Zero buffer size */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + 0, RSA_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + /* Callback returning error */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + FOURK_BUF, RSA_TYPE, mockSignCbError, &signCtx, &rng), BAD_STATE_E); + #ifdef HAVE_ECC + /* Invalid keyType */ + ExpectIntEQ(wc_SignCert_cb(cert.bodySz, cert.sigType, der, + FOURK_BUF, ED448_TYPE, mockSignCb, &signCtx, &rng), BAD_FUNC_ARG); + #endif ret = wc_FreeRsaKey(&key); ExpectIntEQ(ret, 0); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 29a958f485..67825b775a 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -32112,46 +32112,46 @@ static int InternalSignCb(const byte* in, word32 inLen, ret = 0; } } + else #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */ - #if defined(HAVE_ECC) && defined(HAVE_ECC_SIGN) if (keyType == ECC_TYPE && signCtx->key) { /* For ECC, input is the raw hash */ ret = wc_ecc_sign_hash(in, inLen, out, outLen, signCtx->rng, (ecc_key*)signCtx->key); } + else #endif /* HAVE_ECC && HAVE_ECC_SIGN */ - #if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) if (keyType == ED25519_TYPE && signCtx->key) { /* Ed25519 signs messages, not hashes - cannot use callback path */ ret = SIG_TYPE_E; } + else #endif /* HAVE_ED25519 && HAVE_ED25519_SIGN */ - #if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) if (keyType == ED448_TYPE && signCtx->key) { /* Ed448 signs messages, not hashes - cannot use callback path */ ret = SIG_TYPE_E; } + else #endif /* HAVE_ED448 && HAVE_ED448_SIGN */ - #if defined(HAVE_FALCON) if ((keyType == FALCON_LEVEL1_TYPE || keyType == FALCON_LEVEL5_TYPE) && signCtx->key) { /* Falcon signs messages, not hashes - cannot use callback path */ ret = SIG_TYPE_E; } + else #endif /* HAVE_FALCON */ - #if defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_SIGN) if ((keyType == DILITHIUM_LEVEL2_TYPE || keyType == DILITHIUM_LEVEL3_TYPE || keyType == DILITHIUM_LEVEL5_TYPE) && signCtx->key) { /* Dilithium signs messages, not hashes - cannot use callback path */ ret = SIG_TYPE_E; } + else #endif /* HAVE_DILITHIUM && !WOLFSSL_DILITHIUM_NO_SIGN */ - #if defined(HAVE_SPHINCS) if ((keyType == SPHINCS_FAST_LEVEL1_TYPE || keyType == SPHINCS_FAST_LEVEL3_TYPE || keyType == SPHINCS_FAST_LEVEL5_TYPE || keyType == SPHINCS_SMALL_LEVEL1_TYPE || @@ -32160,7 +32160,17 @@ static int InternalSignCb(const byte* in, word32 inLen, /* Sphincs signs messages, not hashes - cannot use callback path */ ret = SIG_TYPE_E; } + else #endif /* HAVE_SPHINCS */ + { + /* Unhandled key type */ + (void)in; + (void)inLen; + (void)out; + (void)outLen; + (void)keyType; + (void)signCtx; + } return ret; } @@ -34004,7 +34014,7 @@ static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf, word32 sz, byte* sig, word32 sigSz, int sigAlgoType, int keyType, wc_SignCertCb signCb, void* signCtx, WC_RNG* rng, void* heap) { - int digestSz = 0, typeH = 0, ret = 0; + int ret = 0; word32 outLen; (void)rng; @@ -34012,6 +34022,26 @@ static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf, (void)heap; #endif + /* Validate keyType - only RSA and ECC are supported for callback signing. + * Ed25519, Ed448, and post-quantum algorithms sign messages directly, + * not hashes, so they cannot use the callback path. */ +#if !defined(NO_RSA) && defined(HAVE_ECC) + if (keyType != RSA_TYPE && keyType != ECC_TYPE) { + return BAD_FUNC_ARG; + } +#elif !defined(NO_RSA) + if (keyType != RSA_TYPE) { + return BAD_FUNC_ARG; + } +#elif defined(HAVE_ECC) + if (keyType != ECC_TYPE) { + return BAD_FUNC_ARG; + } +#else + (void)keyType; + return NOT_COMPILED_IN; +#endif + switch (certSignCtx->state) { case CERTSIGN_STATE_BEGIN: case CERTSIGN_STATE_DIGEST: @@ -34025,7 +34055,8 @@ static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf, } #endif ret = HashForSignature(buf, sz, (word32)sigAlgoType, certSignCtx->digest, - &typeH, &digestSz, 0, NULL, INVALID_DEVID); + &certSignCtx->typeH, &certSignCtx->digestSz, 0, + NULL, INVALID_DEVID); certSignCtx->state = CERTSIGN_STATE_ENCODE; if (ret != 0) { goto exit_ms; @@ -34044,8 +34075,10 @@ static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf, goto exit_ms; } #endif + /* typeH was stored in certSignCtx by HashForSignature */ certSignCtx->encSigSz = (int)wc_EncodeSignature(certSignCtx->encSig, - certSignCtx->digest, (word32)digestSz, typeH); + certSignCtx->digest, (word32)certSignCtx->digestSz, + certSignCtx->typeH); } #endif /* !NO_RSA */ FALL_THROUGH; @@ -34065,10 +34098,17 @@ static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf, #endif /* !NO_RSA */ { /* ECC: pass raw hash */ - ret = signCb(certSignCtx->digest, (word32)digestSz, + ret = signCb(certSignCtx->digest, (word32)certSignCtx->digestSz, sig, &outLen, sigAlgoType, keyType, signCtx); } + #ifdef WOLFSSL_ASYNC_CRYPT + /* If callback returns WC_PENDING_E, preserve state for re-entry */ + if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) { + return ret; + } + #endif + if (ret == 0) { ret = (int)outLen; } @@ -34089,6 +34129,8 @@ exit_ms: /* reset state */ certSignCtx->state = CERTSIGN_STATE_BEGIN; + certSignCtx->digestSz = 0; + certSignCtx->typeH = 0; if (ret < 0) { WOLFSSL_ERROR_VERBOSE(ret); @@ -34402,16 +34444,22 @@ int wc_SignCert(int requestSz, int sType, byte* buf, word32 buffSz, * This allows external signing implementations (e.g., TPM, HSM) * without requiring the crypto callback infrastructure. * + * NOTE: This function does NOT support async crypto (WOLFSSL_ASYNC_CRYPT). + * The certSignCtx is local to this function and cannot persist across + * async re-entry. Use wc_SignCert or wc_SignCert_ex for async operations. + * * @param [in] requestSz Size of certificate body to sign. * @param [in] sType The signature type. * @param [in,out] buf Der buffer to sign. * @param [in] buffSz Der buffer size. - * @param [in] keyType The type of key. + * @param [in] keyType The type of key (RSA_TYPE or ECC_TYPE only). * @param [in] signCb User signing callback. * @param [in] signCtx Context passed to callback. * @param [in] rng Random number generator (may be NULL). * * @return Size of signature on success. + * @return BAD_FUNC_ARG if signCb or buf is NULL, buffSz is 0, or invalid + * keyType. * @return < 0 on error */ #ifdef WOLFSSL_CERT_SIGN_CB @@ -34423,10 +34471,29 @@ int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz, CertSignCtx certSignCtx_lcl; CertSignCtx* certSignCtx = &certSignCtx_lcl; - if (signCb == NULL || buf == NULL) { + /* Validate parameters */ + if (signCb == NULL || buf == NULL || buffSz == 0) { return BAD_FUNC_ARG; } + /* Validate keyType - only RSA and ECC supported */ +#if !defined(NO_RSA) && defined(HAVE_ECC) + if (keyType != RSA_TYPE && keyType != ECC_TYPE) { + return BAD_FUNC_ARG; + } +#elif !defined(NO_RSA) + if (keyType != RSA_TYPE) { + return BAD_FUNC_ARG; + } +#elif defined(HAVE_ECC) + if (keyType != ECC_TYPE) { + return BAD_FUNC_ARG; + } +#else + (void)keyType; + return NOT_COMPILED_IN; +#endif + XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); if (requestSz < 0) { @@ -34447,13 +34514,23 @@ int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz, #ifdef WOLFSSL_ASYNC_CRYPT if (sigSz == WC_NO_ERR_TRACE(WC_PENDING_E)) { - /* Not free'ing certSignCtx->sig here because it could still be in use - * with async operations. */ - return sigSz; + /* Async crypto not supported with wc_SignCert_cb because certSignCtx + * is local and cannot persist across re-entry. Clean up and return + * error. */ + #ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); + certSignCtx->sig = NULL; + #endif + return NOT_COMPILED_IN; } #endif if (sigSz >= 0) { + /* Check buffer has room for signature structure. This is an estimate + * using MAX_SEQ_SZ * 2 to account for sequence headers and algorithm + * identifier overhead. For precise sizing, call AddSignature with + * NULL buffer first, but this estimate matches the existing pattern + * used in SignCert. */ if (requestSz + MAX_SEQ_SZ * 2 + sigSz > (int)buffSz) { sigSz = BUFFER_E; } diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 7425ba8a9b..dbcc91a789 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -250,7 +250,7 @@ typedef int (wc_pem_password_cb)(char* passwd, int sz, int rw, void* userdata); \param outLen Input: size of output buffer. Output: actual signature size. \param sigAlgo Signature algorithm identifier (e.g., CTC_SHA256wRSA, CTC_SHA256wECDSA). - \param keyType Key type (RSA_TYPE, ECC_TYPE, etc.). + \param keyType Key type (RSA_TYPE or ECC_TYPE only). \param ctx User-provided context pointer for callback state. \return 0 on success. diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 6ba7f8466c..22c37073f2 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2397,6 +2397,8 @@ enum Max_ASN { #ifndef NO_RSA int encSigSz; #endif + int digestSz; + int typeH; /* Hash algorithm type for encoding */ int state; /* enum CertSignState */ } CertSignCtx;