Skip to content

Commit 352c362

Browse files
committed
Support raw sign callbacks without privateKey for HSM/secure element use case
1 parent 37f4c13 commit 352c362

File tree

3 files changed

+279
-24
lines changed

3 files changed

+279
-24
lines changed

CLAUDE.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build and Development Commands
6+
7+
### Primary Build System (Autotools)
8+
```bash
9+
# From git checkout
10+
./autogen.sh # Generate configure script (requires autoconf, automake, libtool)
11+
./configure # Configure build with default options
12+
make # Build the library
13+
make check # Run tests (highly recommended)
14+
sudo make install # Install the library
15+
16+
# Quick test configurations
17+
make test # Run unit tests
18+
make egs # Build client/server examples
19+
make wc_egs # Build wolfCrypt test/benchmark
20+
```
21+
22+
### Alternative Build System (CMake)
23+
```bash
24+
# Basic CMake build
25+
mkdir build && cd build
26+
cmake ..
27+
cmake --build .
28+
29+
# With options (example)
30+
cmake .. -DWOLFSSL_TLS13=no -DWOLFSSL_DSA=yes -DCMAKE_BUILD_TYPE=Debug
31+
cmake --build .
32+
```
33+
34+
### Testing Commands
35+
```bash
36+
# Commit tests (must pass before commits)
37+
./commit-tests.sh # Runs current, basic, and full config tests
38+
39+
# Individual test suites
40+
./wolfcrypt/test/testwolfcrypt # wolfCrypt unit tests
41+
./wolfcrypt/benchmark/benchmark # Performance benchmarks
42+
./testsuite/testsuite # Main test suite
43+
./tests/unit.test # Unit test runner
44+
```
45+
46+
### Package Building
47+
```bash
48+
make deb # Generate Debian package
49+
make deb-docker # Generate Debian package in Docker
50+
make rpm # Generate RPM package
51+
make rpm-docker # Generate RPM package in Docker
52+
```
53+
54+
## Code Architecture
55+
56+
### Core Directory Structure
57+
- **`src/`** - Main wolfSSL/TLS implementation
58+
- `ssl.c` - Core SSL/TLS functionality
59+
- `internal.c` - Internal helper functions
60+
- `ssl_crypto.c` - Cryptographic operations for TLS
61+
- `x509*.c` - X.509 certificate handling
62+
- `ocsp.c`, `crl.c` - Certificate revocation
63+
- `sniffer.c` - Packet capture/analysis
64+
- `quic.c` - QUIC protocol support
65+
66+
- **`wolfssl/`** - Public header files
67+
- Main API headers and OpenSSL compatibility layer
68+
- `wolfssl/options.h` - Build configuration (auto-generated)
69+
70+
- **`wolfcrypt/`** - Cryptographic library (can be used standalone)
71+
- `src/` - Implementation of crypto primitives
72+
- `test/` - Crypto unit tests
73+
- `benchmark/` - Performance testing
74+
75+
- **`examples/`** - Sample applications
76+
- `client/`, `server/` - Basic TLS client/server
77+
- `echoclient/`, `echoserver/` - Echo examples
78+
- `benchmark/` - TLS performance testing
79+
80+
- **`tests/`** - Test configuration files and API tests
81+
- **`testsuite/`** - Main test orchestration
82+
- **`certs/`** - Test certificates and keys
83+
84+
### Key Build Files
85+
- `configure.ac` - Autotools configuration
86+
- `Makefile.am` - Automake configuration
87+
- `CMakeLists.txt` - CMake configuration
88+
- `wolfssl/options.h` - Generated build options (include first in applications)
89+
90+
### Platform Support
91+
wolfSSL supports numerous platforms through `IDE/` directory:
92+
- Embedded: STM32, ESP32, Renesas, TI, NXP
93+
- Mobile: iOS, Android
94+
- RTOS: FreeRTOS, ThreadX, Zephyr
95+
- Kernel: Linux kernel module (`linuxkm/`), BSD kernel module (`bsdkm/`)
96+
97+
## Development Workflows
98+
99+
### Configuration Options
100+
wolfSSL uses extensive `./configure` options for feature selection:
101+
```bash
102+
# Common feature flags
103+
--enable-opensslextra # OpenSSL API compatibility
104+
--enable-tls13 # TLS 1.3 support
105+
--enable-dtls # DTLS protocol
106+
--enable-psk # Pre-shared key support
107+
--enable-ecc # Elliptic curve cryptography
108+
--enable-fips # FIPS 140-2 mode
109+
--enable-kyber --enable-dilithium # Post-quantum algorithms
110+
111+
# Debug and testing
112+
--enable-debug # Debug symbols and logging
113+
--enable-debuglog # Verbose debugging
114+
--enable-test # Include test features
115+
```
116+
117+
### Code Style and Standards
118+
- **ANSI C** - Written in ANSI C for maximum portability
119+
- **Options Header** - Applications must include `wolfssl/options.h` first, or define `WOLFSSL_USE_OPTIONS_H`
120+
- **Security Focus** - Constant-time implementations, secure defaults
121+
- **FIPS Compliance** - FIPS 140-2 validated versions available
122+
123+
### Git Workflow
124+
- Pre-commit hooks run `commit-tests.sh` automatically
125+
- Tests must pass on current config, basic config, and full-featured config
126+
- Use `--no-verify` to bypass commit tests during development
127+
128+
### Testing Strategy
129+
- Unit tests for individual components
130+
- Integration tests via examples
131+
- Configuration matrix testing via CI
132+
- External library compatibility testing (OpenSSL apps, etc.)
133+
134+
## Key Concepts
135+
136+
### FIPS Mode
137+
- wolfCrypt has FIPS 140-2 validated versions
138+
- Use `fips-check.sh` and `fips-hash.sh` for FIPS builds
139+
- Special build requirements and restrictions apply
140+
141+
### Certificate Management
142+
- `gencertbuf.pl` generates C arrays from certificates for embedded use
143+
- Test certificates renewable via `certs/renewcerts.sh`
144+
- OCSP responder test scripts in `certs/ocsp/`
145+
146+
### Compatibility Layers
147+
- OpenSSL compatibility API for porting existing applications
148+
- Extensive platform abstraction for embedded/RTOS use
149+
- Hardware acceleration support for various crypto engines
150+
151+
### Security Notes
152+
- Perfect forward secrecy enabled by default (ephemeral key exchange)
153+
- Static key cipher suites disabled by default for security
154+
- Memory management designed for resource-constrained environments
155+
- Constant-time implementations to prevent timing attacks

tests/api/test_pkcs7.c

Lines changed: 104 additions & 24 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) {
@@ -453,7 +487,10 @@ static int rsaSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
453487

454488
#if defined(HAVE_PKCS7) && defined(HAVE_PKCS7_ECC_RAW_SIGN_CALLBACK) && \
455489
defined(HAVE_ECC) && !defined(NO_SHA256)
456-
/* ECC sign raw digest callback */
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+
*/
457494
static int eccSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
458495
byte* out, word32 outSz, byte* privateKey,
459496
word32 privateKeySz, int devid, int hashOID)
@@ -463,19 +500,46 @@ static int eccSignRawDigestCb(PKCS7* pkcs7, byte* digest, word32 digestSz,
463500
word32 sigSz = outSz;
464501
ecc_key ecc;
465502

503+
/* privateKey may be NULL in HSM/secure element use case - we load it
504+
* independently in this callback to simulate that scenario */
505+
(void)privateKey;
506+
(void)privateKeySz;
507+
(void)hashOID;
508+
466509
if (pkcs7 == NULL || digest == NULL || out == NULL) {
467510
return -1;
468511
}
469512

470-
(void)hashOID;
471-
472513
/* set up ECC key */
473514
ret = wc_ecc_init_ex(&ecc, pkcs7->heap, devid);
474515
if (ret != 0) {
475516
return ret;
476517
}
477518

478-
ret = wc_EccPrivateKeyDecode(privateKey, &idx, &ecc, privateKeySz);
519+
/* Load key from test buffer - simulates HSM/secure element access */
520+
#if defined(USE_CERT_BUFFERS_256)
521+
ret = wc_EccPrivateKeyDecode(ecc_clikey_der_256, &idx, &ecc,
522+
sizeof_ecc_clikey_der_256);
523+
#else
524+
{
525+
XFILE fp;
526+
byte keyBuf[ONEK_BUF];
527+
int keySz;
528+
529+
fp = XFOPEN("./certs/client-ecc-key.der", "rb");
530+
if (fp == XBADFILE) {
531+
wc_ecc_free(&ecc);
532+
return -1;
533+
}
534+
keySz = (int)XFREAD(keyBuf, 1, sizeof(keyBuf), fp);
535+
XFCLOSE(fp);
536+
if (keySz <= 0) {
537+
wc_ecc_free(&ecc);
538+
return -1;
539+
}
540+
ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &ecc, (word32)keySz);
541+
}
542+
#endif
479543

480544
/* sign digest */
481545
if (ret == 0) {
@@ -797,8 +861,7 @@ int test_wc_PKCS7_EncodeSignedData(void)
797861
if (pkcs7 != NULL) {
798862
pkcs7->content = data;
799863
pkcs7->contentSz = (word32)sizeof(data);
800-
pkcs7->privateKey = key;
801-
pkcs7->privateKeySz = (word32)sizeof(key);
864+
/* privateKey not set - callback simulates HSM/secure element access */
802865
pkcs7->encryptOID = RSAk;
803866
pkcs7->hashOID = SHA256h;
804867
pkcs7->rng = &rng;
@@ -813,24 +876,41 @@ int test_wc_PKCS7_EncodeSignedData(void)
813876
defined(HAVE_ECC) && !defined(NO_SHA256)
814877
/* test ECC sign raw digest callback, if using ECC and compiled in.
815878
* Example callback assumes SHA-256, so only run test if compiled in. */
816-
wc_PKCS7_Free(pkcs7);
817-
pkcs7 = NULL;
818-
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
819-
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, cert, certSz), 0);
879+
{
880+
#if defined(USE_CERT_BUFFERS_256)
881+
byte eccCert[sizeof(cliecc_cert_der_256)];
882+
word32 eccCertSz = (word32)sizeof(eccCert);
883+
XMEMCPY(eccCert, cliecc_cert_der_256, eccCertSz);
884+
#else
885+
byte eccCert[ONEK_BUF];
886+
int eccCertSz;
887+
XFILE eccFp = XBADFILE;
820888

821-
if (pkcs7 != NULL) {
822-
pkcs7->content = data;
823-
pkcs7->contentSz = (word32)sizeof(data);
824-
pkcs7->privateKey = key;
825-
pkcs7->privateKeySz = (word32)keySz;
826-
pkcs7->encryptOID = ECDSAk;
827-
pkcs7->hashOID = SHA256h;
828-
pkcs7->rng = &rng;
829-
}
889+
ExpectTrue((eccFp = XFOPEN("./certs/client-ecc-cert.der", "rb")) !=
890+
XBADFILE);
891+
ExpectIntGT(eccCertSz = (int)XFREAD(eccCert, 1, ONEK_BUF, eccFp), 0);
892+
if (eccFp != XBADFILE)
893+
XFCLOSE(eccFp);
894+
#endif
895+
896+
wc_PKCS7_Free(pkcs7);
897+
pkcs7 = NULL;
898+
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
899+
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, eccCert, (word32)eccCertSz), 0);
830900

831-
ExpectIntEQ(wc_PKCS7_SetEccSignRawDigestCb(pkcs7, eccSignRawDigestCb), 0);
901+
if (pkcs7 != NULL) {
902+
pkcs7->content = data;
903+
pkcs7->contentSz = (word32)sizeof(data);
904+
/* privateKey not set - callback simulates HSM/secure element access */
905+
pkcs7->encryptOID = ECDSAk;
906+
pkcs7->hashOID = SHA256h;
907+
pkcs7->rng = &rng;
908+
}
832909

833-
ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0);
910+
ExpectIntEQ(wc_PKCS7_SetEccSignRawDigestCb(pkcs7, eccSignRawDigestCb), 0);
911+
912+
ExpectIntGT(wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz), 0);
913+
}
834914
#endif
835915

836916
wc_PKCS7_Free(pkcs7);

wolfcrypt/src/pkcs7.c

Lines changed: 20 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
}

0 commit comments

Comments
 (0)