From 6d6d0ab0880962017386b0079f547d5500f92754 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 13 Jan 2026 14:22:30 -0700 Subject: [PATCH] Add PKCS7 ECC raw sign callback support --- .wolfssl_known_macro_extras | 1 + tests/api/test_pkcs7.c | 154 ++++++++++++++++++++++++++++++++++-- wolfcrypt/src/pkcs7.c | 58 ++++++++++++++ wolfssl/wolfcrypt/pkcs7.h | 17 ++++ 4 files changed, 225 insertions(+), 5 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 56eb6a0dfda..ad3350d28ec 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -280,6 +280,7 @@ HAVE_INTEL_QAT_SYNC HAVE_INTEL_SPEEDUP HAVE_MDK_RTX HAVE_NETX_BSD +HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK HAVE_POCO_LIB HAVE_RTP_SYS diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index d60e3abb67f..6dee044e36b 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -388,12 +388,15 @@ int test_wc_PKCS7_EncodeData(void) #if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && \ !defined(NO_RSA) && !defined(NO_SHA256) -/* RSA sign raw digest callback */ +/* RSA sign raw digest callback + * This callback demonstrates HSM/secure element use case where the private + * key is not passed through PKCS7 structure but obtained independently. + */ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz, byte* out, word32 outSz, byte* privateKey, word32 privateKeySz, int devid, int hashOID) { - /* specific DigestInfo ASN.1 encoding prefix for a SHA2565 digest */ + /* specific DigestInfo ASN.1 encoding prefix for a SHA256 digest */ byte digInfoEncoding[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, @@ -407,6 +410,11 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz, word32 idx = 0; RsaKey rsa; + /* privateKey may be NULL in HSM/secure element use case - we load it + * independently in this callback to simulate that scenario */ + (void)privateKey; + (void)privateKeySz; + /* SHA-256 required only for this example callback due to above * digInfoEncoding[] */ if (pkcs7 == NULL || digest == NULL || out == NULL || @@ -427,7 +435,33 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz, return ret; } - ret = wc_RsaPrivateKeyDecode(privateKey, &idx, &rsa, privateKeySz); + /* Load key from test buffer - simulates HSM/secure element access */ +#if defined(USE_CERT_BUFFERS_2048) + ret = wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &rsa, + sizeof_client_key_der_2048); +#elif defined(USE_CERT_BUFFERS_1024) + ret = wc_RsaPrivateKeyDecode(client_key_der_1024, &idx, &rsa, + sizeof_client_key_der_1024); +#else + { + XFILE fp; + byte keyBuf[ONEK_BUF]; + int keySz; + + fp = XFOPEN("./certs/client-key.der", "rb"); + if (fp == XBADFILE) { + wc_FreeRsaKey(&rsa); + return -1; + } + keySz = (int)XFREAD(keyBuf, 1, sizeof(keyBuf), fp); + XFCLOSE(fp); + if (keySz <= 0) { + wc_FreeRsaKey(&rsa); + return -1; + } + ret = wc_RsaPrivateKeyDecode(keyBuf, &idx, &rsa, (word32)keySz); + } +#endif /* sign DigestInfo */ if (ret == 0) { @@ -451,6 +485,76 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz, } #endif +#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && \ + defined(HAVE_ECC) && !defined(NO_SHA256) +/* ECC sign raw digest callback + * This callback demonstrates HSM/secure element use case where the private + * key is not passed through PKCS7 structure but obtained independently. + */ +static int eccSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz, + byte* out, word32 outSz, byte* privateKey, + word32 privateKeySz, int devid, int hashOID) +{ + int ret; + word32 idx = 0; + word32 sigSz = outSz; + ecc_key ecc; + + /* privateKey may be NULL in HSM/secure element use case - we load it + * independently in this callback to simulate that scenario */ + (void)privateKey; + (void)privateKeySz; + (void)hashOID; + + if (pkcs7 == NULL || digest == NULL || out == NULL) { + return -1; + } + + /* set up ECC key */ + ret = wc_ecc_init_ex(&ecc, pkcs7->heap, devid); + if (ret != 0) { + return ret; + } + + /* Load key from test buffer - simulates HSM/secure element access */ +#if defined(USE_CERT_BUFFERS_256) + ret = wc_EccPrivateKeyDecode(ecc_clikey_der_256, &idx, &ecc, + sizeof_ecc_clikey_der_256); +#else + { + XFILE fp; + byte keyBuf[ONEK_BUF]; + int keySz; + + fp = XFOPEN("./certs/client-ecc-key.der", "rb"); + if (fp == XBADFILE) { + wc_ecc_free(&ecc); + return -1; + } + keySz = (int)XFREAD(keyBuf, 1, sizeof(keyBuf), fp); + XFCLOSE(fp); + if (keySz <= 0) { + wc_ecc_free(&ecc); + return -1; + } + ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &ecc, (word32)keySz); + } +#endif + + /* sign digest */ + if (ret == 0) { + ret = wc_ecc_sign_hash(digest, digestSz, out, &sigSz, pkcs7->rng, &ecc); + if (ret == 0) { + ret = (int)sigSz; + } + } + + wc_ecc_free(&ecc); + + return ret; +} +#endif + #if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER) typedef struct encodeSignedDataStream { byte out[FOURK_BUF*3]; @@ -757,8 +861,7 @@ int test_wc_PKCS7_EncodeSignedData(void) if (pkcs7 != NULL) { pkcs7->content = data; pkcs7->contentSz = (word32)sizeof(data); - pkcs7->privateKey = key; - pkcs7->privateKeySz = (word32)sizeof(key); + /* privateKey not set - callback simulates HSM/secure element access */ pkcs7->encryptOID = RSAk; pkcs7->hashOID = SHA256h; pkcs7->rng = &rng; @@ -769,6 +872,47 @@ int test_wc_PKCS7_EncodeSignedData(void) ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0); #endif +#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && \ + defined(HAVE_ECC) && !defined(NO_SHA256) + /* test ECC sign raw digest callback, if using ECC and compiled in. + * Example callback assumes SHA-256, so only run test if compiled in. */ + { + #if defined(USE_CERT_BUFFERS_256) + byte eccCert[sizeof(cliecc_cert_der_256)]; + word32 eccCertSz = (word32)sizeof(eccCert); + XMEMCPY(eccCert, cliecc_cert_der_256, eccCertSz); + #else + byte eccCert[ONEK_BUF]; + int eccCertSz; + XFILE eccFp = XBADFILE; + + ExpectTrue((eccFp = XFOPEN("./certs/client-ecc-cert.der", "rb")) != + XBADFILE); + ExpectIntGT(eccCertSz = (int)XFREAD(eccCert, 1, ONEK_BUF, eccFp), 0); + if (eccFp != XBADFILE) + XFCLOSE(eccFp); + #endif + + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, eccCert, (word32)eccCertSz), 0); + + if (pkcs7 != NULL) { + pkcs7->content = data; + pkcs7->contentSz = (word32)sizeof(data); + /* privateKey not set - callback simulates HSM/secure element access */ + pkcs7->encryptOID = ECDSAk; + pkcs7->hashOID = SHA256h; + pkcs7->rng = &rng; + } + + ExpectIntEQ(wc_PKCS7_SetEccSignRawDigestCb(pkcs7, eccSignRawDigestCb), 0); + + ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0); + } +#endif + wc_PKCS7_Free(pkcs7); DoExpectIntEQ(wc_FreeRng(&rng), 0); diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 677441e426e..82efb9b10da 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -1794,6 +1794,16 @@ static int wc_PKCS7_ImportRSA(wc_PKCS7* pkcs7, RsaKey* privKey) } #endif } + #ifdef HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK + else if (pkcs7->rsaSignRawDigestCb != NULL && pkcs7->publicKeySz > 0) { + /* When using raw sign callback (e.g., HSM/secure element), private + * key may not be available. Use public key from signer certificate + * for signature size calculation. */ + idx = 0; + ret = wc_RsaPublicKeyDecode(pkcs7->publicKey, &idx, privKey, + pkcs7->publicKeySz); + } + #endif else if (pkcs7->devId == INVALID_DEVID) { ret = BAD_FUNC_ARG; } @@ -1874,6 +1884,16 @@ static int wc_PKCS7_ImportECC(wc_PKCS7* pkcs7, ecc_key* privKey) } #endif } + #ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK + else if (pkcs7->eccSignRawDigestCb != NULL && pkcs7->publicKeySz > 0) { + /* When using raw sign callback (e.g., HSM/secure element), private + * key may not be available. Use public key from signer certificate + * for signature size calculation. */ + idx = 0; + ret = wc_EccPublicKeyDecode(pkcs7->publicKey, &idx, privKey, + pkcs7->publicKeySz); + } + #endif else if (pkcs7->devId == INVALID_DEVID) { ret = BAD_FUNC_ARG; } @@ -2398,6 +2418,20 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7, #ifdef HAVE_ECC case ECDSAk: + #ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK + if (pkcs7->eccSignRawDigestCb != NULL) { + /* get hash OID */ + int eccHashOID = wc_HashGetOID(esd->hashType); + + /* user signing plain digest */ + ret = pkcs7->eccSignRawDigestCb(pkcs7, + esd->contentAttribsDigest, hashSz, + esd->encContentDigest, sizeof(esd->encContentDigest), + pkcs7->privateKey, pkcs7->privateKeySz, pkcs7->devId, + eccHashOID); + break; + } + #endif /* CMS with ECDSA does not sign DigestInfo structure * like PKCS#7 with RSA does */ ret = wc_PKCS7_EcdsaSign(pkcs7, esd->contentAttribsDigest, @@ -3986,6 +4020,30 @@ int wc_PKCS7_SetRsaSignRawDigestCb(wc_PKCS7* pkcs7, CallbackRsaSignRawDigest cb) } #endif +#endif /* NO_RSA */ + + +#ifdef HAVE_ECC + +#ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK +/* register raw ECC sign digest callback */ +int wc_PKCS7_SetEccSignRawDigestCb(wc_PKCS7* pkcs7, CallbackEccSignRawDigest cb) +{ + if (pkcs7 == NULL || cb == NULL) { + return BAD_FUNC_ARG; + } + + pkcs7->eccSignRawDigestCb = cb; + + return 0; +} +#endif + +#endif /* HAVE_ECC */ + + +#ifndef NO_RSA + /* returns size of signature put into out, negative on error */ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz, byte* hash, word32 hashSz) diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 834d17fa5cc..57bd83594d9 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -229,6 +229,14 @@ typedef int (*CallbackRsaSignRawDigest)(wc_PKCS7* pkcs7, byte* digest, int devId, int hashOID); #endif +#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC) +/* ECC sign raw digest callback, user signs hash directly */ +typedef int (*CallbackEccSignRawDigest)(wc_PKCS7* pkcs7, byte* digest, + word32 digestSz, byte* out, word32 outSz, + byte* privateKey, word32 privateKeySz, + int devId, int hashOID); +#endif + /* Public Structure Warning: * Existing members must not be changed to maintain backwards compatibility! @@ -376,6 +384,10 @@ struct wc_PKCS7 { CallbackAESKeyWrapUnwrap aesKeyWrapUnwrapCb; +#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC) + CallbackEccSignRawDigest eccSignRawDigestCb; +#endif + /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ }; @@ -511,6 +523,11 @@ WOLFSSL_API int wc_PKCS7_SetRsaSignRawDigestCb(wc_PKCS7* pkcs7, CallbackRsaSignRawDigest cb); #endif +#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC) +WOLFSSL_API int wc_PKCS7_SetEccSignRawDigestCb(wc_PKCS7* pkcs7, + CallbackEccSignRawDigest cb); +#endif + /* CMS/PKCS#7 EnvelopedData */ WOLFSSL_API int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz);