Skip to content

Commit 2a32e10

Browse files
authored
Merge pull request #9656 from jackctj117/PKCS7-signing
Add PKCS7 ECC raw sign callback support
2 parents 6d7cb87 + cfcd384 commit 2a32e10

File tree

4 files changed

+259
-5
lines changed

4 files changed

+259
-5
lines changed

.wolfssl_known_macro_extras

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ HAVE_INTEL_QAT_SYNC
280280
HAVE_INTEL_SPEEDUP
281281
HAVE_MDK_RTX
282282
HAVE_NETX_BSD
283+
HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK
283284
HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK
284285
HAVE_POCO_LIB
285286
HAVE_RTP_SYS

tests/api/test_pkcs7.c

Lines changed: 175 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,15 @@ int test_wc_PKCS7_EncodeData(void)
388388

389389
#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && \
390390
!defined(NO_RSA) && !defined(NO_SHA256)
391-
/* RSA sign raw digest callback */
391+
/* RSA sign raw digest callback
392+
* This callback demonstrates HSM/secure element use case where the private
393+
* key is not passed through PKCS7 structure but obtained independently.
394+
*/
392395
static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
393396
byte* out, word32 outSz, byte* privateKey,
394397
word32 privateKeySz, int devid, int hashOID)
395398
{
396-
/* specific DigestInfo ASN.1 encoding prefix for a SHA2565 digest */
399+
/* specific DigestInfo ASN.1 encoding prefix for a SHA256 digest */
397400
byte digInfoEncoding[] = {
398401
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
399402
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
@@ -407,6 +410,11 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
407410
word32 idx = 0;
408411
RsaKey rsa;
409412

413+
/* privateKey may be NULL in HSM/secure element use case - we load it
414+
* independently in this callback to simulate that scenario */
415+
(void)privateKey;
416+
(void)privateKeySz;
417+
410418
/* SHA-256 required only for this example callback due to above
411419
* digInfoEncoding[] */
412420
if (pkcs7 == NULL || digest == NULL || out == NULL ||
@@ -427,7 +435,33 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
427435
return ret;
428436
}
429437

430-
ret = wc_RsaPrivateKeyDecode(privateKey, &idx, &rsa, privateKeySz);
438+
/* Load key from test buffer - simulates HSM/secure element access */
439+
#if defined(USE_CERT_BUFFERS_2048)
440+
ret = wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &rsa,
441+
sizeof_client_key_der_2048);
442+
#elif defined(USE_CERT_BUFFERS_1024)
443+
ret = wc_RsaPrivateKeyDecode(client_key_der_1024, &idx, &rsa,
444+
sizeof_client_key_der_1024);
445+
#else
446+
{
447+
XFILE fp;
448+
byte keyBuf[ONEK_BUF];
449+
int keySz;
450+
451+
fp = XFOPEN("./certs/client-key.der", "rb");
452+
if (fp == XBADFILE) {
453+
wc_FreeRsaKey(&rsa);
454+
return -1;
455+
}
456+
keySz = (int)XFREAD(keyBuf, 1, sizeof(keyBuf), fp);
457+
XFCLOSE(fp);
458+
if (keySz <= 0) {
459+
wc_FreeRsaKey(&rsa);
460+
return -1;
461+
}
462+
ret = wc_RsaPrivateKeyDecode(keyBuf, &idx, &rsa, (word32)keySz);
463+
}
464+
#endif
431465

432466
/* sign DigestInfo */
433467
if (ret == 0) {
@@ -451,6 +485,102 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
451485
}
452486
#endif
453487

488+
#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && \
489+
defined(HAVE_ECC) && !defined(NO_SHA256)
490+
/* ECC sign raw digest callback
491+
* This callback demonstrates HSM/secure element use case where the private
492+
* key is not passed through PKCS7 structure but obtained independently.
493+
* Note: This example callback is hash-agnostic and will work with any
494+
* hash algorithm. The hashOID parameter can be used to validate or select
495+
* different signing behavior if needed.
496+
*/
497+
static int eccSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
498+
byte* out, word32 outSz, byte* privateKey,
499+
word32 privateKeySz, int devid, int hashOID)
500+
{
501+
int ret;
502+
word32 idx = 0;
503+
word32 sigSz = outSz;
504+
#ifdef WOLFSSL_SMALL_STACK
505+
ecc_key* ecc = NULL;
506+
#else
507+
ecc_key ecc[1];
508+
#endif
509+
510+
/* privateKey may be NULL in HSM/secure element use case - we load it
511+
* independently in this callback to simulate that scenario */
512+
(void)privateKey;
513+
(void)privateKeySz;
514+
(void)hashOID;
515+
516+
if (pkcs7 == NULL || digest == NULL || out == NULL) {
517+
return -1;
518+
}
519+
520+
#ifdef WOLFSSL_SMALL_STACK
521+
ecc = (ecc_key*)XMALLOC(sizeof(ecc_key), pkcs7->heap, DYNAMIC_TYPE_ECC);
522+
if (ecc == NULL) {
523+
return MEMORY_E;
524+
}
525+
#endif
526+
527+
/* set up ECC key */
528+
ret = wc_ecc_init_ex(ecc, pkcs7->heap, devid);
529+
if (ret != 0) {
530+
#ifdef WOLFSSL_SMALL_STACK
531+
XFREE(ecc, pkcs7->heap, DYNAMIC_TYPE_ECC);
532+
#endif
533+
return ret;
534+
}
535+
536+
/* Load key from test buffer - simulates HSM/secure element access */
537+
#if defined(USE_CERT_BUFFERS_256)
538+
ret = wc_EccPrivateKeyDecode(ecc_clikey_der_256, &idx, ecc,
539+
sizeof_ecc_clikey_der_256);
540+
#else
541+
{
542+
XFILE fp;
543+
byte keyBuf[ONEK_BUF];
544+
int keySz;
545+
546+
fp = XFOPEN("./certs/client-ecc-key.der", "rb");
547+
if (fp == XBADFILE) {
548+
wc_ecc_free(ecc);
549+
#ifdef WOLFSSL_SMALL_STACK
550+
XFREE(ecc, pkcs7->heap, DYNAMIC_TYPE_ECC);
551+
#endif
552+
return -1;
553+
}
554+
keySz = (int)XFREAD(keyBuf, 1, sizeof(keyBuf), fp);
555+
XFCLOSE(fp);
556+
if (keySz <= 0) {
557+
wc_ecc_free(ecc);
558+
#ifdef WOLFSSL_SMALL_STACK
559+
XFREE(ecc, pkcs7->heap, DYNAMIC_TYPE_ECC);
560+
#endif
561+
return -1;
562+
}
563+
ret = wc_EccPrivateKeyDecode(keyBuf, &idx, ecc, (word32)keySz);
564+
}
565+
#endif
566+
567+
/* sign digest */
568+
if (ret == 0) {
569+
ret = wc_ecc_sign_hash(digest, digestSz, out, &sigSz, pkcs7->rng, ecc);
570+
if (ret == 0) {
571+
ret = (int)sigSz;
572+
}
573+
}
574+
575+
wc_ecc_free(ecc);
576+
#ifdef WOLFSSL_SMALL_STACK
577+
XFREE(ecc, pkcs7->heap, DYNAMIC_TYPE_ECC);
578+
#endif
579+
580+
return ret;
581+
}
582+
#endif
583+
454584
#if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER)
455585
typedef struct encodeSignedDataStream {
456586
byte out[FOURK_BUF*3];
@@ -757,8 +887,7 @@ int test_wc_PKCS7_EncodeSignedData(void)
757887
if (pkcs7 != NULL) {
758888
pkcs7->content = data;
759889
pkcs7->contentSz = (word32)sizeof(data);
760-
pkcs7->privateKey = key;
761-
pkcs7->privateKeySz = (word32)sizeof(key);
890+
/* privateKey not set - callback simulates HSM/secure element access */
762891
pkcs7->encryptOID = RSAk;
763892
pkcs7->hashOID = SHA256h;
764893
pkcs7->rng = &rng;
@@ -769,6 +898,47 @@ int test_wc_PKCS7_EncodeSignedData(void)
769898
ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0);
770899
#endif
771900

901+
#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && \
902+
defined(HAVE_ECC) && !defined(NO_SHA256)
903+
/* test ECC sign raw digest callback, if using ECC and compiled in.
904+
* Example callback assumes SHA-256, so only run test if compiled in. */
905+
{
906+
#if defined(USE_CERT_BUFFERS_256)
907+
byte eccCert[sizeof(cliecc_cert_der_256)];
908+
word32 eccCertSz = (word32)sizeof(eccCert);
909+
XMEMCPY(eccCert, cliecc_cert_der_256, eccCertSz);
910+
#else
911+
byte eccCert[ONEK_BUF];
912+
int eccCertSz;
913+
XFILE eccFp = XBADFILE;
914+
915+
ExpectTrue((eccFp = XFOPEN("./certs/client-ecc-cert.der", "rb")) !=
916+
XBADFILE);
917+
ExpectIntGT(eccCertSz = (int)XFREAD(eccCert, 1, ONEK_BUF, eccFp), 0);
918+
if (eccFp != XBADFILE)
919+
XFCLOSE(eccFp);
920+
#endif
921+
922+
wc_PKCS7_Free(pkcs7);
923+
pkcs7 = NULL;
924+
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
925+
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, eccCert, (word32)eccCertSz), 0);
926+
927+
if (pkcs7 != NULL) {
928+
pkcs7->content = data;
929+
pkcs7->contentSz = (word32)sizeof(data);
930+
/* privateKey not set - callback simulates HSM/secure element access */
931+
pkcs7->encryptOID = ECDSAk;
932+
pkcs7->hashOID = SHA256h;
933+
pkcs7->rng = &rng;
934+
}
935+
936+
ExpectIntEQ(wc_PKCS7_SetEccSignRawDigestCb(pkcs7, eccSignRawDigestCb), 0);
937+
938+
ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0);
939+
}
940+
#endif
941+
772942
wc_PKCS7_Free(pkcs7);
773943
DoExpectIntEQ(wc_FreeRng(&rng), 0);
774944

wolfcrypt/src/pkcs7.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,16 @@ static int wc_PKCS7_ImportRSA(wc_PKCS7* pkcs7, RsaKey* privKey)
17941794
}
17951795
#endif
17961796
}
1797+
#ifdef HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK
1798+
else if (pkcs7->rsaSignRawDigestCb != NULL && pkcs7->publicKeySz > 0) {
1799+
/* When using raw sign callback (e.g., HSM/secure element), private
1800+
* key may not be available. Use public key from signer certificate
1801+
* for signature size calculation. */
1802+
idx = 0;
1803+
ret = wc_RsaPublicKeyDecode(pkcs7->publicKey, &idx, privKey,
1804+
pkcs7->publicKeySz);
1805+
}
1806+
#endif
17971807
else if (pkcs7->devId == INVALID_DEVID) {
17981808
ret = BAD_FUNC_ARG;
17991809
}
@@ -1874,6 +1884,16 @@ static int wc_PKCS7_ImportECC(wc_PKCS7* pkcs7, ecc_key* privKey)
18741884
}
18751885
#endif
18761886
}
1887+
#ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK
1888+
else if (pkcs7->eccSignRawDigestCb != NULL && pkcs7->publicKeySz > 0) {
1889+
/* When using raw sign callback (e.g., HSM/secure element), private
1890+
* key may not be available. Use public key from signer certificate
1891+
* for signature size calculation. */
1892+
idx = 0;
1893+
ret = wc_EccPublicKeyDecode(pkcs7->publicKey, &idx, privKey,
1894+
pkcs7->publicKeySz);
1895+
}
1896+
#endif
18771897
else if (pkcs7->devId == INVALID_DEVID) {
18781898
ret = BAD_FUNC_ARG;
18791899
}
@@ -2398,6 +2418,28 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7,
23982418

23992419
#ifdef HAVE_ECC
24002420
case ECDSAk:
2421+
#ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK
2422+
if (pkcs7->eccSignRawDigestCb != NULL) {
2423+
/* get hash OID */
2424+
int eccHashOID = wc_HashGetOID(esd->hashType);
2425+
if (eccHashOID < 0) {
2426+
ret = eccHashOID;
2427+
break;
2428+
}
2429+
2430+
/* user signing plain digest */
2431+
ret = pkcs7->eccSignRawDigestCb(pkcs7,
2432+
esd->contentAttribsDigest, hashSz,
2433+
esd->encContentDigest, sizeof(esd->encContentDigest),
2434+
pkcs7->privateKey, pkcs7->privateKeySz, pkcs7->devId,
2435+
eccHashOID);
2436+
/* validate return value doesn't exceed buffer size */
2437+
if (ret > 0 && (word32)ret > sizeof(esd->encContentDigest)) {
2438+
ret = BUFFER_E;
2439+
}
2440+
break;
2441+
}
2442+
#endif
24012443
/* CMS with ECDSA does not sign DigestInfo structure
24022444
* like PKCS#7 with RSA does */
24032445
ret = wc_PKCS7_EcdsaSign(pkcs7, esd->contentAttribsDigest,
@@ -3986,6 +4028,30 @@ int wc_PKCS7_SetRsaSignRawDigestCb(wc_PKCS7* pkcs7, CallbackRsaSignRawDigest cb)
39864028
}
39874029
#endif
39884030

4031+
#endif /* NO_RSA */
4032+
4033+
4034+
#ifdef HAVE_ECC
4035+
4036+
#ifdef HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK
4037+
/* register raw ECC sign digest callback */
4038+
int wc_PKCS7_SetEccSignRawDigestCb(wc_PKCS7* pkcs7, CallbackEccSignRawDigest cb)
4039+
{
4040+
if (pkcs7 == NULL || cb == NULL) {
4041+
return BAD_FUNC_ARG;
4042+
}
4043+
4044+
pkcs7->eccSignRawDigestCb = cb;
4045+
4046+
return 0;
4047+
}
4048+
#endif
4049+
4050+
#endif /* HAVE_ECC */
4051+
4052+
4053+
#ifndef NO_RSA
4054+
39894055
/* returns size of signature put into out, negative on error */
39904056
static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
39914057
byte* hash, word32 hashSz)

wolfssl/wolfcrypt/pkcs7.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ typedef int (*CallbackRsaSignRawDigest)(wc_PKCS7* pkcs7, byte* digest,
229229
int devId, int hashOID);
230230
#endif
231231

232+
#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC)
233+
/* ECC sign raw digest callback, user signs hash directly */
234+
typedef int (*CallbackEccSignRawDigest)(wc_PKCS7* pkcs7, byte* digest,
235+
word32 digestSz, byte* out, word32 outSz,
236+
byte* privateKey, word32 privateKeySz,
237+
int devId, int hashOID);
238+
#endif
239+
232240

233241
/* Public Structure Warning:
234242
* Existing members must not be changed to maintain backwards compatibility!
@@ -376,6 +384,10 @@ struct wc_PKCS7 {
376384

377385
CallbackAESKeyWrapUnwrap aesKeyWrapUnwrapCb;
378386

387+
#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC)
388+
CallbackEccSignRawDigest eccSignRawDigestCb;
389+
#endif
390+
379391
/* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */
380392
};
381393

@@ -511,6 +523,11 @@ WOLFSSL_API int wc_PKCS7_SetRsaSignRawDigestCb(wc_PKCS7* pkcs7,
511523
CallbackRsaSignRawDigest cb);
512524
#endif
513525

526+
#if defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && defined(HAVE_ECC)
527+
WOLFSSL_API int wc_PKCS7_SetEccSignRawDigestCb(wc_PKCS7* pkcs7,
528+
CallbackEccSignRawDigest cb);
529+
#endif
530+
514531
/* CMS/PKCS#7 EnvelopedData */
515532
WOLFSSL_API int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7,
516533
byte* output, word32 outputSz);

0 commit comments

Comments
 (0)