Skip to content

Commit 958c5f4

Browse files
committed
LMS/XMSS CryptoCb: sign/verify now take the pre-computed digest
PKCS#11 v3.2 section 6.65 (CKM_HSS) and section 6.66.8 ("XMSS and XMSSMT without hashing") specify that CKM_HSS, CKM_XMSS and CKM_XMSSMT take the pre-computed message digest, not the raw message. Align the crypto-callback layer with that semantic so a future wolfHSM or PKCS#11 provider hooking into the CryptoCb surface receives exactly the bytes PKCS#11 expects. - wc_CryptoInfo.pk.pqc_stateful_sig_sign / _verify rename in/inSz/msg/msgSz fields to hash/hashSz. - wc_CryptoCb_PqcStatefulSigSign / _Verify take hash/hashSz. - wc_lms.c and wc_xmss.c pre-hash the message with the algorithm dictated by the parameter set (SHA-256 or SHAKE256 for LMS; SHA-256/SHA-512/SHAKE128/SHAKE256 for XMSS) before dispatching through the callback. New static helpers wc_lms_hash_msg / wc_xmss_hash_msg encapsulate the param -> hash-type mapping. - myCryptoDevCb feeds the pre-computed digest back into the software API as the "message" when it bounces the op back to software; both Sign and Verify pre-hash the same way, so the round-trip self-verifies inside the harness. - wc_LmsKey_ExportPub / wc_XmssKey_ExportPub now preserve the source key's devId so a verify-only copy still dispatches through the same device. Previously ForceZero left the destination devId at 0 (a valid non-INVALID_DEVID value) which caused a spurious software fallback after ExportPub when sign was done via CryptoCb. All three config combinations (lms+xmss+cryptocb, lms+xmss, cryptocb alone) build and pass testwolfcrypt. https://claude.ai/code/session_01MixzJP9kPWkS8bhfDDDBnX
1 parent 9315596 commit 958c5f4

5 files changed

Lines changed: 180 additions & 29 deletions

File tree

wolfcrypt/src/cryptocb.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ int wc_CryptoCb_PqcStatefulSigKeyGen(int type, void* key, WC_RNG* rng)
10681068
return wc_CryptoCb_TranslateErrorCode(ret);
10691069
}
10701070

1071-
int wc_CryptoCb_PqcStatefulSigSign(const byte* in, word32 inSz, byte* out,
1071+
int wc_CryptoCb_PqcStatefulSigSign(const byte* hash, word32 hashSz, byte* out,
10721072
word32* outSz, int type, void* key)
10731073
{
10741074
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
@@ -1088,8 +1088,8 @@ int wc_CryptoCb_PqcStatefulSigSign(const byte* in, word32 inSz, byte* out,
10881088
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
10891089
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
10901090
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN;
1091-
cryptoInfo.pk.pqc_stateful_sig_sign.in = in;
1092-
cryptoInfo.pk.pqc_stateful_sig_sign.inSz = inSz;
1091+
cryptoInfo.pk.pqc_stateful_sig_sign.hash = hash;
1092+
cryptoInfo.pk.pqc_stateful_sig_sign.hashSz = hashSz;
10931093
cryptoInfo.pk.pqc_stateful_sig_sign.out = out;
10941094
cryptoInfo.pk.pqc_stateful_sig_sign.outSz = outSz;
10951095
cryptoInfo.pk.pqc_stateful_sig_sign.key = key;
@@ -1102,7 +1102,7 @@ int wc_CryptoCb_PqcStatefulSigSign(const byte* in, word32 inSz, byte* out,
11021102
}
11031103

11041104
int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig, word32 sigSz,
1105-
const byte* msg, word32 msgSz, int* res, int type, void* key)
1105+
const byte* hash, word32 hashSz, int* res, int type, void* key)
11061106
{
11071107
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
11081108
int devId = INVALID_DEVID;
@@ -1123,8 +1123,8 @@ int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig, word32 sigSz,
11231123
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY;
11241124
cryptoInfo.pk.pqc_stateful_sig_verify.sig = sig;
11251125
cryptoInfo.pk.pqc_stateful_sig_verify.sigSz = sigSz;
1126-
cryptoInfo.pk.pqc_stateful_sig_verify.msg = msg;
1127-
cryptoInfo.pk.pqc_stateful_sig_verify.msgSz = msgSz;
1126+
cryptoInfo.pk.pqc_stateful_sig_verify.hash = hash;
1127+
cryptoInfo.pk.pqc_stateful_sig_verify.hashSz = hashSz;
11281128
cryptoInfo.pk.pqc_stateful_sig_verify.res = res;
11291129
cryptoInfo.pk.pqc_stateful_sig_verify.key = key;
11301130
cryptoInfo.pk.pqc_stateful_sig_verify.type = type;

wolfcrypt/src/wc_lms.c

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,52 @@
3838

3939
#ifdef WOLF_CRYPTO_CB
4040
#include <wolfssl/wolfcrypt/cryptocb.h>
41+
42+
/* Pre-compute the message digest with the hash function dictated by the LMS
43+
* parameter set. PKCS#11 v3.2 section 6.65 specifies that CKM_HSS takes the
44+
* pre-computed digest, not the raw message, so every Sign/Verify dispatch
45+
* through the crypto-callback layer funnels through this helper. */
46+
static int wc_lms_hash_msg(const LmsParams* params, const byte* msg,
47+
word32 msgSz, byte* hash, word32 hashSz)
48+
{
49+
int ret = 0;
50+
51+
if ((params == NULL) || (msg == NULL) || (hash == NULL))
52+
return BAD_FUNC_ARG;
53+
if (hashSz != params->hash_len)
54+
return BAD_FUNC_ARG;
55+
56+
switch (params->lmsType & 0xF000) {
57+
case LMS_SHA256: /* 32-byte SHA-256 */
58+
case LMS_SHA256_192: /* SHA-256 truncated to 24 bytes */ {
59+
byte full[WC_SHA256_DIGEST_SIZE];
60+
ret = wc_Sha256Hash(msg, msgSz, full);
61+
if (ret == 0)
62+
XMEMCPY(hash, full, hashSz);
63+
break;
64+
}
65+
#ifdef WOLFSSL_LMS_SHAKE256
66+
case LMS_SHAKE256: /* SHAKE256 with 32-byte output */
67+
case LMS_SHAKE256_192: /* SHAKE256 with 24-byte output */ {
68+
wc_Shake shake;
69+
ret = wc_InitShake256(&shake, NULL, INVALID_DEVID);
70+
if (ret == 0) {
71+
ret = wc_Shake256_Update(&shake, msg, msgSz);
72+
if (ret == 0)
73+
ret = wc_Shake256_Final(&shake, hash, hashSz);
74+
wc_Shake256_Free(&shake);
75+
}
76+
break;
77+
}
78+
#endif
79+
default:
80+
WOLFSSL_MSG("LMS: unsupported hash family for CryptoCb pre-hash");
81+
ret = NOT_COMPILED_IN;
82+
break;
83+
}
84+
85+
return ret;
86+
}
4187
#endif
4288

4389

@@ -1272,8 +1318,13 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg,
12721318
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
12731319
* software checks below still run. */
12741320
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
1275-
ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgSz, sig, sigSz,
1276-
WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
1321+
byte hash[LMS_MAX_NODE_LEN];
1322+
word32 hashSz = key->params->hash_len;
1323+
ret = wc_lms_hash_msg(key->params, msg, (word32)msgSz, hash, hashSz);
1324+
if (ret == 0) {
1325+
ret = wc_CryptoCb_PqcStatefulSigSign(hash, hashSz, sig, sigSz,
1326+
WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
1327+
}
12771328
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
12781329
return ret;
12791330
ret = 0; /* fall through to software path */
@@ -1436,6 +1487,12 @@ int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
14361487
keyDst->params = keySrc->params;
14371488
XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
14381489

1490+
#ifdef WOLF_CRYPTO_CB
1491+
/* Preserve the source key's device binding so the verify-only
1492+
* copy dispatches through the same crypto callback. */
1493+
keyDst->devId = keySrc->devId;
1494+
#endif
1495+
14391496
/* Mark this key as verify only, to prevent misuse. */
14401497
keyDst->state = WC_LMS_STATE_VERIFYONLY;
14411498
}
@@ -1588,9 +1645,14 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
15881645

15891646
#ifdef WOLF_CRYPTO_CB
15901647
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
1648+
byte hash[LMS_MAX_NODE_LEN];
1649+
word32 hashSz = key->params->hash_len;
15911650
int res = 0;
1592-
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigSz, msg, (word32)msgSz,
1593-
&res, WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
1651+
ret = wc_lms_hash_msg(key->params, msg, (word32)msgSz, hash, hashSz);
1652+
if (ret == 0) {
1653+
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigSz, hash, hashSz,
1654+
&res, WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
1655+
}
15941656
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
15951657
if (ret == 0 && res != 1)
15961658
ret = SIG_VERIFY_E;

wolfcrypt/src/wc_xmss.c

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,67 @@
3838

3939
#ifdef WOLF_CRYPTO_CB
4040
#include <wolfssl/wolfcrypt/cryptocb.h>
41+
42+
/* Pre-compute the message digest with the hash function dictated by the XMSS
43+
* parameter set. PKCS#11 v3.2 section 6.66 specifies that CKM_XMSS and
44+
* CKM_XMSSMT take the pre-computed digest, not the raw message (the section
45+
* is titled "XMSS and XMSSMT without hashing"), so every Sign/Verify
46+
* dispatch through the crypto-callback layer funnels through this helper. */
47+
static int wc_xmss_hash_msg(const XmssParams* params, const byte* msg,
48+
word32 msgSz, byte* hash, word32 hashSz)
49+
{
50+
int ret = 0;
51+
52+
if ((params == NULL) || (msg == NULL) || (hash == NULL))
53+
return BAD_FUNC_ARG;
54+
if (hashSz != params->n)
55+
return BAD_FUNC_ARG;
56+
57+
switch (params->hash) {
58+
#ifdef WC_XMSS_SHA256
59+
case WC_HASH_TYPE_SHA256:
60+
ret = wc_Hash(WC_HASH_TYPE_SHA256, msg, msgSz, hash, hashSz);
61+
break;
62+
#endif
63+
#ifdef WC_XMSS_SHA512
64+
case WC_HASH_TYPE_SHA512:
65+
ret = wc_Hash(WC_HASH_TYPE_SHA512, msg, msgSz, hash, hashSz);
66+
break;
67+
#endif
68+
#ifdef WC_XMSS_SHAKE128
69+
case WC_HASH_TYPE_SHAKE128: {
70+
wc_Shake shake;
71+
ret = wc_InitShake128(&shake, NULL, INVALID_DEVID);
72+
if (ret == 0) {
73+
ret = wc_Shake128_Update(&shake, msg, msgSz);
74+
if (ret == 0)
75+
ret = wc_Shake128_Final(&shake, hash, hashSz);
76+
wc_Shake128_Free(&shake);
77+
}
78+
break;
79+
}
80+
#endif
81+
#ifdef WC_XMSS_SHAKE256
82+
case WC_HASH_TYPE_SHAKE256: {
83+
wc_Shake shake;
84+
ret = wc_InitShake256(&shake, NULL, INVALID_DEVID);
85+
if (ret == 0) {
86+
ret = wc_Shake256_Update(&shake, msg, msgSz);
87+
if (ret == 0)
88+
ret = wc_Shake256_Final(&shake, hash, hashSz);
89+
wc_Shake256_Free(&shake);
90+
}
91+
break;
92+
}
93+
#endif
94+
default:
95+
WOLFSSL_MSG("XMSS: unsupported hash for CryptoCb pre-hash");
96+
ret = NOT_COMPILED_IN;
97+
break;
98+
}
99+
100+
return ret;
101+
}
41102
#endif
42103

43104

@@ -1351,8 +1412,13 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg,
13511412
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
13521413
* software checks below still run. */
13531414
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
1354-
ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgLen, sig, sigLen,
1355-
WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
1415+
byte hash[WC_XMSS_MAX_N];
1416+
word32 hashSz = key->params->n;
1417+
ret = wc_xmss_hash_msg(key->params, msg, (word32)msgLen, hash, hashSz);
1418+
if (ret == 0) {
1419+
ret = wc_CryptoCb_PqcStatefulSigSign(hash, hashSz, sig, sigLen,
1420+
WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
1421+
}
13561422
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
13571423
return ret;
13581424
ret = 0; /* fall through to software path */
@@ -1498,6 +1564,12 @@ int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc)
14981564
keyDst->oid = keySrc->oid;
14991565
keyDst->is_xmssmt = keySrc->is_xmssmt;
15001566
keyDst->params = keySrc->params;
1567+
1568+
#ifdef WOLF_CRYPTO_CB
1569+
/* Preserve the source key's device binding so the verify-only
1570+
* copy dispatches through the same crypto callback. */
1571+
keyDst->devId = keySrc->devId;
1572+
#endif
15011573
}
15021574
if (ret == 0) {
15031575
/* Mark keyDst as verify only, to prevent misuse. */
@@ -1693,9 +1765,14 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
16931765

16941766
#ifdef WOLF_CRYPTO_CB
16951767
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
1768+
byte hash[WC_XMSS_MAX_N];
1769+
word32 hashSz = key->params->n;
16961770
int res = 0;
1697-
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigLen, m, (word32)mLen,
1698-
&res, WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
1771+
ret = wc_xmss_hash_msg(key->params, m, (word32)mLen, hash, hashSz);
1772+
if (ret == 0) {
1773+
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigLen, hash, hashSz,
1774+
&res, WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
1775+
}
16991776
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
17001777
if (ret == 0 && res != 1)
17011778
ret = SIG_VERIFY_E;

wolfcrypt/test/test.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68286,14 +68286,19 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
6828668286
else if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN) {
6828768287
int pqcType = info->pk.pqc_stateful_sig_sign.type;
6828868288
word32 sigSz = *info->pk.pqc_stateful_sig_sign.outSz;
68289+
/* The dispatcher pre-hashed the message per PKCS#11 v3.2
68290+
* CKM_HSS / CKM_XMSS semantics. Feed that digest back into
68291+
* the software API as the "message"; both Sign and Verify
68292+
* paths do the same pre-hashing so the resulting signature
68293+
* self-verifies within this harness. */
6828968294
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
6829068295
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
6829168296
LmsKey* lk = (LmsKey*)info->pk.pqc_stateful_sig_sign.key;
6829268297
lk->devId = INVALID_DEVID;
6829368298
ret = wc_LmsKey_Sign(lk,
6829468299
info->pk.pqc_stateful_sig_sign.out, &sigSz,
68295-
info->pk.pqc_stateful_sig_sign.in,
68296-
(int)info->pk.pqc_stateful_sig_sign.inSz);
68300+
info->pk.pqc_stateful_sig_sign.hash,
68301+
(int)info->pk.pqc_stateful_sig_sign.hashSz);
6829768302
lk->devId = devIdArg;
6829868303
}
6829968304
#endif
@@ -68303,8 +68308,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
6830368308
xk->devId = INVALID_DEVID;
6830468309
ret = wc_XmssKey_Sign(xk,
6830568310
info->pk.pqc_stateful_sig_sign.out, &sigSz,
68306-
info->pk.pqc_stateful_sig_sign.in,
68307-
(int)info->pk.pqc_stateful_sig_sign.inSz);
68311+
info->pk.pqc_stateful_sig_sign.hash,
68312+
(int)info->pk.pqc_stateful_sig_sign.hashSz);
6830868313
xk->devId = devIdArg;
6830968314
}
6831068315
#endif
@@ -68320,8 +68325,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
6832068325
verifyRet = wc_LmsKey_Verify(lk,
6832168326
info->pk.pqc_stateful_sig_verify.sig,
6832268327
info->pk.pqc_stateful_sig_verify.sigSz,
68323-
info->pk.pqc_stateful_sig_verify.msg,
68324-
(int)info->pk.pqc_stateful_sig_verify.msgSz);
68328+
info->pk.pqc_stateful_sig_verify.hash,
68329+
(int)info->pk.pqc_stateful_sig_verify.hashSz);
6832568330
lk->devId = devIdArg;
6832668331
}
6832768332
#endif
@@ -68332,8 +68337,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
6833268337
verifyRet = wc_XmssKey_Verify(xk,
6833368338
info->pk.pqc_stateful_sig_verify.sig,
6833468339
info->pk.pqc_stateful_sig_verify.sigSz,
68335-
info->pk.pqc_stateful_sig_verify.msg,
68336-
(int)info->pk.pqc_stateful_sig_verify.msgSz);
68340+
info->pk.pqc_stateful_sig_verify.hash,
68341+
(int)info->pk.pqc_stateful_sig_verify.hashSz);
6833768342
xk->devId = devIdArg;
6833868343
}
6833968344
#endif

wolfssl/wolfcrypt/cryptocb.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,12 @@ typedef struct wc_CryptoInfo {
362362
int type; /* enum wc_PqcStatefulSignatureType */
363363
} pqc_stateful_sig_kg;
364364
struct {
365-
const byte* in;
366-
word32 inSz;
365+
/* Pre-computed digest of the message. PKCS#11 v3.2
366+
* section 6.65/6.66 ("HSS" / "XMSS and XMSSMT without
367+
* hashing") specifies that CKM_HSS, CKM_XMSS and
368+
* CKM_XMSSMT take a hash, not the raw message. */
369+
const byte* hash;
370+
word32 hashSz;
367371
byte* out;
368372
word32* outSz;
369373
void* key;
@@ -372,8 +376,9 @@ typedef struct wc_CryptoInfo {
372376
struct {
373377
const byte* sig;
374378
word32 sigSz;
375-
const byte* msg;
376-
word32 msgSz;
379+
/* Pre-computed digest of the message. See sign note. */
380+
const byte* hash;
381+
word32 hashSz;
377382
int* res;
378383
void* key;
379384
int type; /* enum wc_PqcStatefulSignatureType */
@@ -756,10 +761,12 @@ WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigGetDevId(int type, void* key);
756761

757762
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigKeyGen(int type, void* key,
758763
WC_RNG* rng);
759-
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigSign(const byte* in, word32 inSz,
760-
byte* out, word32* outSz, int type, void* key);
764+
/* hash is the pre-computed digest of the message; CKM_HSS, CKM_XMSS and
765+
* CKM_XMSSMT all take the digest, not the raw message (PKCS#11 v3.2). */
766+
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigSign(const byte* hash,
767+
word32 hashSz, byte* out, word32* outSz, int type, void* key);
761768
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig,
762-
word32 sigSz, const byte* msg, word32 msgSz, int* res, int type,
769+
word32 sigSz, const byte* hash, word32 hashSz, int* res, int type,
763770
void* key);
764771
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigSigsLeft(int type, void* key,
765772
word32* sigsLeft);

0 commit comments

Comments
 (0)