Add crypto callbacks for LMS and XMSS

This commit is contained in:
Paul Adelsbach
2026-05-04 14:43:29 -07:00
parent 6a3eb6f0a8
commit b9eb7c1ff8
9 changed files with 1219 additions and 62 deletions
+186
View File
@@ -0,0 +1,186 @@
# LMS / XMSS Crypto Callback support
This document describes the wolfSSL-side groundwork that lets LMS / HSS
(RFC 8554) and XMSS / XMSS^MT (RFC 8391, both profiled in NIST SP 800-208)
participate in the `WOLF_CRYPTO_CB` framework. With this layer in place, the
wolfSSL PKCS#11 provider and the wolfHSM client can host stateful
hash-based keys on a device without the wolfSSL public API changing.
No HSM-side or PKCS#11-provider code lives in this layer. It only adds the
dispatcher surface, the per-key device binding, and the helpers a backend
needs to answer the request.
## Why route stateful hash-based keys through a device
LMS and XMSS are one-time-signature trees: the private key holds a counter
that must be incremented on every signature, and signing the same index twice
breaks the security proof. Moving that counter to a hardware module is the
clean way to make the scheme operationally safe — the HSM is the natural
owner of the index, and an attacker who steals a host snapshot cannot replay
old indices.
## PKCS#11 mapping
PKCS#11 v3.1 standardised HSS and v3.2 added XMSS / XMSS^MT. The CryptoCb
surface mirrors what those mechanisms expose:
| wolfSSL API | PKCS#11 analog | CryptoCb dispatcher |
|-----------------------|------------------------------------------------|----------------------------------------------|
| `wc_LmsKey_MakeKey` | `CKM_HSS_KEY_PAIR_GEN` | `wc_CryptoCb_PqcStatefulSigKeyGen` |
| `wc_LmsKey_Sign` | `CKM_HSS` (sign) | `wc_CryptoCb_PqcStatefulSigSign` |
| `wc_LmsKey_Verify` | `CKM_HSS` (verify) | `wc_CryptoCb_PqcStatefulSigVerify` |
| `wc_LmsKey_SigsLeft` | `CKA_HSS_KEYS_REMAINING` attribute | `wc_CryptoCb_PqcStatefulSigSigsLeft` |
| `wc_XmssKey_MakeKey` | `CKM_XMSS_KEY_PAIR_GEN` / `CKM_XMSSMT_KEY_PAIR_GEN` | `wc_CryptoCb_PqcStatefulSigKeyGen` |
| `wc_XmssKey_Sign` | `CKM_XMSS` / `CKM_XMSSMT` (sign) | `wc_CryptoCb_PqcStatefulSigSign` |
| `wc_XmssKey_Verify` | `CKM_XMSS` / `CKM_XMSSMT` (verify) | `wc_CryptoCb_PqcStatefulSigVerify` |
| `wc_XmssKey_SigsLeft` | XMSS remaining-sigs attribute | `wc_CryptoCb_PqcStatefulSigSigsLeft` |
The four dispatchers are shared between LMS and XMSS, following the
`wc_CryptoCb_PqcSign*` family used for Dilithium and Falcon. A new
discriminator enum `wc_PqcStatefulSignatureType` (`WC_PQC_STATEFUL_SIG_TYPE_LMS`,
`WC_PQC_STATEFUL_SIG_TYPE_XMSS`) tells the callback which of `LmsKey*` or
`XmssKey*` the `void* key` field is. XMSS vs XMSS^MT is decided inside the
callback via the existing `XmssKey::is_xmssmt` field.
`Reload`, `GetKid`, and `ExportPub` are not routed through CryptoCb, but each
is aware of HSM-backed keys: `Reload` short-circuits because state lives in
the device, `GetKid` logs a warning since `priv_raw` may be uninitialised,
and a new `ExportPub_ex` variant lets the caller bind the verify-only copy
to a `heap` / `devId` of their choosing so it dispatches through the same
device as the signing key. The external-backend variants (`ext_lms.c` /
`ext_xmss.c`, selected by `--with-liblms` / `--with-libxmss`) are
intentionally outside the scope of this layer and execute purely in
software.
## Per-key device binding
Each key carries the device-binding fields that other key types
(`RsaKey`, `ecc_key`, `dilithium_key`) already expose:
```c
struct LmsKey {
/* ... existing fields ... */
#ifdef WOLF_CRYPTO_CB
int devId; /* device identifier */
void* devCtx; /* opaque per-device state, owned by the callback */
#endif
#ifdef WOLF_PRIVATE_KEY_ID
byte id[LMS_MAX_ID_LEN]; /* device-side key identifier */
int idLen;
char label[LMS_MAX_LABEL_LEN]; /* device-side key label */
int labelLen;
#endif
};
```
`XmssKey` carries the equivalent set under the same macro guards, with
`XMSS_MAX_ID_LEN` / `XMSS_MAX_LABEL_LEN`. The `*_MAX_ID_LEN` and
`*_MAX_LABEL_LEN` constants default to 32 and can be overridden by
predefining the macros.
`devCtx`, `id`, and `label` are storage only — wolfSSL never reads or writes
them internally. Backends populate `devCtx` from the callback (typically the
first time they touch the key) and consume `id` / `label` to resolve the
on-device handle.
## Public API additions
```c
/* Bind a key to a device-side identifier or label. */
#ifdef WOLF_PRIVATE_KEY_ID
WOLFSSL_API int wc_LmsKey_InitId (LmsKey * key, const unsigned char * id,
int len, void * heap, int devId);
WOLFSSL_API int wc_LmsKey_InitLabel(LmsKey * key, const char * label,
void * heap, int devId);
WOLFSSL_API int wc_XmssKey_InitId (XmssKey* key, const unsigned char* id,
int len, void* heap, int devId);
WOLFSSL_API int wc_XmssKey_InitLabel(XmssKey* key, const char* label,
void* heap, int devId);
#endif
/* Hash a message according to the given algorithm. */
WOLFSSL_API int wc_LmsKey_HashMsg (const LmsKey * key, const byte * msg,
word32 msgSz, byte * hash,
word32 * hashSz);
WOLFSSL_API int wc_XmssKey_HashMsg(const XmssKey* key, const byte* msg,
word32 msgSz, byte* hash,
word32* hashSz);
/* Export a public key into a verify-only key with explicit heap and
* device bindings. */
WOLFSSL_API int wc_LmsKey_ExportPub_ex (LmsKey * keyDst,
const LmsKey * keySrc,
void * heap, int devId);
WOLFSSL_API int wc_XmssKey_ExportPub_ex(XmssKey* keyDst,
const XmssKey* keySrc,
void* heap, int devId);
```
The `Init*` helpers follow the `wc_InitRsaKey_Id` / `wc_InitRsaKey_Label`
shape: they validate length bounds, delegate the rest of init to
`wc_LmsKey_Init` / `wc_XmssKey_Init`, then copy id / label onto the key.
The `HashMsg` helpers honour the parameter set:
| Algorithm | Hash families covered |
|-----------|-----------------------------------------------------------------|
| LMS / HSS | SHA-256 (32 bytes), SHA-256/192 (24 bytes), SHAKE256 (32 / 24) |
| XMSS / MT | SHA-256, SHA-512, SHAKE128, SHAKE256 (per `params->hash`) |
`*hashSz` is in / out: callers pass the buffer size and receive the digest
length on success.
## Sign / verify input format
The CryptoCb dispatcher forwards the raw message to the callback. PKCS#11
v3.2 section 6.66.8 ("XMSS and XMSSMT without hashing") and the analogous
text for HSS specify that those mechanisms take a pre-computed digest
rather than the message. Backends that need that behaviour — typically
PKCS#11 providers — call `wc_LmsKey_HashMsg` or `wc_XmssKey_HashMsg` from
inside the callback to produce the algorithm-dictated digest. Backends
that take the full message (typically wolfHSM) consume `msg` / `msgSz`
directly. Picking one or the other is a callback decision; the dispatcher
is agnostic.
## Build configuration
| `./configure` flag(s) | Effect |
|--------------------------------------------------------|-------------------------------------------------------|
| `--enable-lms --enable-xmss --enable-cryptocb` | Primary target. Full dispatcher and round-trip tests. |
| `--enable-lms --enable-xmss` | New dispatcher code is fully `#ifdef`-elided. |
| `--enable-cryptocb` | LMS / XMSS-less build; nothing CryptoCb-side breaks. |
| `CPPFLAGS=-DWOLF_PRIVATE_KEY_ID …` | Adds `id` / `label` fields and the `Init*` helpers. |
## Verification
`./wolfcrypt/test/testwolfcrypt` exercises the full dispatcher round trip:
inside `cryptocb_test`, `lms_test` and `xmss_test` run with the harness's
registered `myCryptoDevCb`, which clears the key's `devId`, invokes the
software API recursively, then restores `devId`. Sign and verify both go
through the dispatcher, so the produced signatures self-verify within the
harness. With no device registered, `lms_test` and `xmss_test` remain on the
software path and produce bit-identical KAT output.
## Design notes
- **Shared dispatcher, separate type tag.** The eight LMS / XMSS operations
collapse to four shared dispatchers (`KeyGen`, `Sign`, `Verify`,
`SigsLeft`) keyed on `wc_PqcStatefulSignatureType`. The pattern matches
the `PqcSign` family used for Dilithium / Falcon and reduces the surface
area a backend has to implement.
- **Verify carries `int* res`.** Following the Ed25519 / ECC / PqcVerify
convention, the verify dispatcher reports validity through a separate
`*res` flag, so a backend can distinguish a transport error from a
forged signature. The wrapping wolfSSL function still translates
`res != 1` to `SIG_VERIFY_E` for callers that do not see `res`.
- **`SigsLeft` carries `word32* sigsLeft`.** PKCS#11 defines
`CKA_HSS_KEYS_REMAINING` as a `CK_ULONG`-sized attribute; the callback
uses `word32*` so an HSS key at its 2^32 limit can be expressed
unambiguously. The wolfSSL public API still returns `int` and clamps at
`0x7FFFFFFF`.
- **HSM-backed keys skip the software write / read callbacks.**
`wc_LmsKey_MakeKey` / `_Sign` and the XMSS equivalents dispatch through
CryptoCb *before* validating `write_private_key` / `read_private_key` /
`context`. A device-backed key does not need dummy software callbacks.
On `CRYPTOCB_UNAVAILABLE` fall-through the software validations are
re-applied as normal.
+148
View File
@@ -1017,6 +1017,154 @@ int wc_CryptoCb_Ed25519Verify(const byte* sig, word32 sigLen,
}
#endif /* HAVE_ED25519 */
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
int wc_CryptoCb_PqcStatefulSigGetDevId(int type, void* key)
{
int devId = INVALID_DEVID;
if (key == NULL)
return devId;
#if defined(WOLFSSL_HAVE_LMS)
if (type == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
devId = ((LmsKey*)key)->devId;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS)
if (type == WC_PQC_STATEFUL_SIG_TYPE_XMSS) {
devId = ((XmssKey*)key)->devId;
}
#endif
return devId;
}
int wc_CryptoCb_PqcStatefulSigKeyGen(int type, void* key, WC_RNG* rng)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
int devId = INVALID_DEVID;
CryptoCb* dev;
if (key == NULL)
return ret;
devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key);
if (devId == INVALID_DEVID)
return ret;
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN;
cryptoInfo.pk.pqc_stateful_sig_kg.rng = rng;
cryptoInfo.pk.pqc_stateful_sig_kg.key = key;
cryptoInfo.pk.pqc_stateful_sig_kg.type = type;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
int wc_CryptoCb_PqcStatefulSigSign(const byte* msg, word32 msgSz, byte* out,
word32* outSz, int type, void* key)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
int devId = INVALID_DEVID;
CryptoCb* dev;
if (key == NULL)
return ret;
devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key);
if (devId == INVALID_DEVID)
return ret;
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN;
cryptoInfo.pk.pqc_stateful_sig_sign.msg = msg;
cryptoInfo.pk.pqc_stateful_sig_sign.msgSz = msgSz;
cryptoInfo.pk.pqc_stateful_sig_sign.out = out;
cryptoInfo.pk.pqc_stateful_sig_sign.outSz = outSz;
cryptoInfo.pk.pqc_stateful_sig_sign.key = key;
cryptoInfo.pk.pqc_stateful_sig_sign.type = type;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig, word32 sigSz,
const byte* msg, word32 msgSz, int* res, int type, void* key)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
int devId = INVALID_DEVID;
CryptoCb* dev;
if (key == NULL)
return ret;
devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key);
if (devId == INVALID_DEVID)
return ret;
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY;
cryptoInfo.pk.pqc_stateful_sig_verify.sig = sig;
cryptoInfo.pk.pqc_stateful_sig_verify.sigSz = sigSz;
cryptoInfo.pk.pqc_stateful_sig_verify.msg = msg;
cryptoInfo.pk.pqc_stateful_sig_verify.msgSz = msgSz;
cryptoInfo.pk.pqc_stateful_sig_verify.res = res;
cryptoInfo.pk.pqc_stateful_sig_verify.key = key;
cryptoInfo.pk.pqc_stateful_sig_verify.type = type;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
int wc_CryptoCb_PqcStatefulSigSigsLeft(int type, void* key, word32* sigsLeft)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
int devId = INVALID_DEVID;
CryptoCb* dev;
if (key == NULL)
return ret;
devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key);
if (devId == INVALID_DEVID)
return ret;
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT;
cryptoInfo.pk.pqc_stateful_sig_sigs_left.key = key;
cryptoInfo.pk.pqc_stateful_sig_sigs_left.sigsLeft = sigsLeft;
cryptoInfo.pk.pqc_stateful_sig_sigs_left.type = type;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
#if defined(WOLFSSL_HAVE_MLKEM)
int wc_CryptoCb_PqcKemGetDevId(int type, void* key)
{
+279 -23
View File
@@ -28,6 +28,7 @@
#define FIPS_NO_WRAPPERS
#endif
#include <wolfssl/wolfcrypt/wc_lms.h>
#include <wolfssl/wolfcrypt/hash.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
@@ -36,6 +37,77 @@
#include <wolfcrypt/src/misc.c>
#endif
#ifdef WOLF_CRYPTO_CB
#include <wolfssl/wolfcrypt/cryptocb.h>
#endif
/* Compute the digest of msg using the hash function dictated by the LMS
* parameter set. Crypto-callback / HSM backends that follow PKCS#11 v3.2
* CKM_HSS semantics (pre-computed digest input) can call this from within
* their callback; backends that take the raw message (e.g. wolfHSM) can
* ignore it. *hashSz is in/out: it must be at least params->hash_len on
* entry and is set to the actual digest length on success.
*
* @param [in] key LMS key (must have a parameter set bound).
* @param [in] msg Message to hash.
* @param [in] msgSz Length of msg in bytes.
* @param [out] hash Buffer receiving the digest.
* @param [in,out] hashSz On entry, size of hash buffer. On success,
* the digest length.
* @return 0 on success.
* @return BAD_FUNC_ARG when an argument is NULL or the buffer is too
* small for the digest.
* @return NOT_COMPILED_IN when the param set's hash family is disabled.
*/
int wc_LmsKey_HashMsg(const LmsKey* key, const byte* msg, word32 msgSz,
byte* hash, word32* hashSz)
{
int ret = 0;
word32 needSz;
if ((key == NULL) || (msg == NULL) || (hash == NULL) || (hashSz == NULL))
return BAD_FUNC_ARG;
if (key->params == NULL)
return BAD_FUNC_ARG;
needSz = (word32)key->params->hash_len;
if (*hashSz < needSz)
return BAD_FUNC_ARG;
switch (key->params->lmsType & 0xF000) {
case LMS_SHA256: /* 32-byte SHA-256 */
case LMS_SHA256_192: /* SHA-256 truncated to 24 bytes */ {
byte full[WC_SHA256_DIGEST_SIZE];
ret = wc_Sha256Hash(msg, msgSz, full);
if (ret == 0)
XMEMCPY(hash, full, needSz);
break;
}
#ifdef WOLFSSL_LMS_SHAKE256
case LMS_SHAKE256: /* SHAKE256 with 32-byte output */
case LMS_SHAKE256_192: /* SHAKE256 with 24-byte output */ {
wc_Shake shake;
ret = wc_InitShake256(&shake, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_Shake256_Update(&shake, msg, msgSz);
if (ret == 0)
ret = wc_Shake256_Final(&shake, hash, needSz);
wc_Shake256_Free(&shake);
}
break;
}
#endif
default:
WOLFSSL_MSG("LMS: unsupported hash family for HashMsg");
ret = NOT_COMPILED_IN;
break;
}
if (ret == 0)
*hashSz = needSz;
return ret;
}
/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8.
*
@@ -642,6 +714,72 @@ int wc_LmsKey_Init(LmsKey* key, void* heap, int devId)
return ret;
}
#ifdef WOLF_PRIVATE_KEY_ID
/* Initialize an LmsKey and bind it to a device-side key identifier.
*
* @param [in,out] key LmsKey to initialize.
* @param [in] id Identifier bytes (may be NULL when len is 0).
* @param [in] len Length of id; must be in [0, LMS_MAX_ID_LEN].
* @param [in] heap Heap hint forwarded to wc_LmsKey_Init.
* @param [in] devId Device identifier.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL.
* @return BUFFER_E when len is negative or exceeds LMS_MAX_ID_LEN.
*/
int wc_LmsKey_InitId(LmsKey* key, const unsigned char* id, int len, void* heap,
int devId)
{
int ret = 0;
if (key == NULL)
ret = BAD_FUNC_ARG;
if (ret == 0 && (len < 0 || len > LMS_MAX_ID_LEN))
ret = BUFFER_E;
if (ret == 0)
ret = wc_LmsKey_Init(key, heap, devId);
if (ret == 0 && id != NULL && len != 0) {
XMEMCPY(key->id, id, (size_t)len);
key->idLen = len;
}
return ret;
}
/* Initialize an LmsKey and bind it to a device-side key label.
*
* @param [in,out] key LmsKey to initialize.
* @param [in] label NUL-terminated label string (must be non-empty).
* @param [in] heap Heap hint forwarded to wc_LmsKey_Init.
* @param [in] devId Device identifier.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when key or label is NULL.
* @return BUFFER_E when label is empty or longer than LMS_MAX_LABEL_LEN.
*/
int wc_LmsKey_InitLabel(LmsKey* key, const char* label, void* heap, int devId)
{
int ret = 0;
int labelLen = 0;
if (key == NULL || label == NULL)
ret = BAD_FUNC_ARG;
if (ret == 0) {
labelLen = (int)XSTRLEN(label);
if (labelLen == 0 || labelLen > LMS_MAX_LABEL_LEN)
ret = BUFFER_E;
}
if (ret == 0)
ret = wc_LmsKey_Init(key, heap, devId);
if (ret == 0) {
XMEMCPY(key->label, label, (size_t)labelLen);
key->labelLen = labelLen;
}
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID */
/* Get the string representation of the LMS parameter set.
*
* @param [in] lmsParm LMS parameter set identifier.
@@ -923,8 +1061,10 @@ int wc_LmsKey_SetContext(LmsKey* key, void* context)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (context == NULL)) {
/* Validate parameters. NULL context is allowed: callers with stub
* read/write callbacks (e.g. HSM-backed keys whose private state lives
* in the device) have no meaningful context to pass. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Setting context of an already working key is forbidden. */
@@ -969,16 +1109,34 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng)
WOLFSSL_MSG("error: LmsKey not ready for generation");
ret = BAD_STATE_E;
}
#ifdef WOLF_CRYPTO_CB
/* HSM-backed keys skip the software write/context callbacks because the
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
* software checks below still run. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
ret = wc_CryptoCb_PqcStatefulSigKeyGen(WC_PQC_STATEFUL_SIG_TYPE_LMS,
key, rng);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
/* On success, mirror the software path's terminal state so
* subsequent Sign/Verify calls don't fail with BAD_STATE_E. */
if (ret == 0) {
key->state = WC_LMS_STATE_OK;
}
return ret;
}
ret = 0; /* fall through to software path */
}
#endif
/* Check write callback set. */
if ((ret == 0) && (key->write_private_key == NULL)) {
WOLFSSL_MSG("error: LmsKey write callback is not set");
ret = BAD_FUNC_ARG;
}
/* Check callback context set. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: LmsKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque to wolfCrypt and may legitimately be NULL
* (e.g. callbacks that read/write a static buffer or HSM-backed keys
* with stub callbacks); no check needed here. */
if (ret == 0) {
const LmsParams* params = key->params;
@@ -1077,16 +1235,22 @@ int wc_LmsKey_Reload(LmsKey* key)
WOLFSSL_MSG("error: LmsKey not ready for reload");
ret = BAD_STATE_E;
}
#ifdef WOLF_CRYPTO_CB
/* State for HSM-backed keys lives in the device; no software reload. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
WOLFSSL_MSG("wc_LmsKey_Reload is a no-op for HSM-backed keys");
key->state = WC_LMS_STATE_OK;
return 0;
}
#endif
/* Check read callback present. */
if ((ret == 0) && (key->read_private_key == NULL)) {
WOLFSSL_MSG("error: LmsKey read callback is not set");
ret = BAD_FUNC_ARG;
}
/* Check context for callback set */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: LmsKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque; NULL is allowed. */
if (ret == 0) {
const LmsParams* params = key->params;
@@ -1236,16 +1400,26 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg,
WOLFSSL_MSG("error: LMS sig buffer too small");
ret = BUFFER_E;
}
#ifdef WOLF_CRYPTO_CB
/* HSM-backed keys skip the software write/context callbacks because the
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
* software checks below still run. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgSz, sig, sigSz,
WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return ret;
ret = 0; /* fall through to software path */
}
#endif
/* Check read and write callbacks available. */
if ((ret == 0) && (key->write_private_key == NULL)) {
WOLFSSL_MSG("error: LmsKey write/read callbacks are not set");
ret = BAD_FUNC_ARG;
}
/* Check read/write callback context available. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: LmsKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque; NULL is allowed. */
if (ret == 0) {
WC_DECLARE_VAR(state, LmsState, 1, 0);
@@ -1313,6 +1487,27 @@ int wc_LmsKey_SigsLeft(LmsKey* key)
/* NULL keys have no signatures remaining. */
if (key != NULL) {
#ifdef WOLF_CRYPTO_CB
if (key->devId != INVALID_DEVID) {
word32 sigsLeft = 0;
int cbRet = wc_CryptoCb_PqcStatefulSigSigsLeft(
WC_PQC_STATEFUL_SIG_TYPE_LMS, key, &sigsLeft);
if (cbRet == 0) {
/* Clamp to int range; callers treat 0 as "exhausted". */
return (sigsLeft > (word32)0x7FFFFFFF)
? 0x7FFFFFFF : (int)sigsLeft;
}
/* The device owns the private state; no safe software fallback
* exists because key->priv_raw does not reflect HSM state. */
if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
WOLFSSL_MSG("PqcStatefulSigSigsLeft returned an error");
}
else {
WOLFSSL_MSG("LMS SigsLeft not supported by device");
}
return 0;
}
#endif
ret = wc_hss_sigsleft(key->params, key->priv_raw);
}
@@ -1345,22 +1540,27 @@ int wc_LmsKey_GetPubLen(const LmsKey* key, word32* len)
}
/* Export a generated public key and parameter set from one LmsKey
* to another. Use this to prepare a signature verification LmsKey
* that is pub only.
* to another, with explicit heap and device bindings.
*
* Though the public key is all that is used to verify signatures,
* the parameter set is needed to calculate the signature length
* before hand.
* The destination is fully (re)initialized as a verify-only key;
* any prior state on keyDst is discarded.
*
* @param [out] keyDst LMS key to copy into.
* @param [in] keySrc LMS key to copy.
* @param [in] heap Heap hint for keyDst.
* @param [in] devId Device identifier for keyDst.
* Use INVALID_DEVID when not using a device.
* @return 0 on success.
* @return BAD_FUNC_ARG when keyDst or keySrc is NULL.
*/
int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
int wc_LmsKey_ExportPub_ex(LmsKey* keyDst, const LmsKey* keySrc,
void* heap, int devId)
{
int ret = 0;
(void)heap;
(void)devId;
if ((keyDst == NULL) || (keySrc == NULL)) {
ret = BAD_FUNC_ARG;
}
@@ -1371,6 +1571,13 @@ int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
keyDst->params = keySrc->params;
XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
#ifndef WOLFSSL_LMS_VERIFY_ONLY
keyDst->heap = heap;
#endif
#ifdef WOLF_CRYPTO_CB
keyDst->devId = devId;
#endif
/* Mark this key as verify only, to prevent misuse. */
keyDst->state = WC_LMS_STATE_VERIFYONLY;
}
@@ -1378,6 +1585,28 @@ int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
return ret;
}
/* Export a generated public key and parameter set from one LmsKey
* to another. Use this to prepare a signature verification LmsKey
* that is pub only.
*
* Though the public key is all that is used to verify signatures,
* the parameter set is needed to calculate the signature length
* before hand.
*
* The destination key is left with no heap hint and no device
* binding. Callers that need either should use
* wc_LmsKey_ExportPub_ex.
*
* @param [out] keyDst LMS key to copy into.
* @param [in] keySrc LMS key to copy.
* @return 0 on success.
* @return BAD_FUNC_ARG when keyDst or keySrc is NULL.
*/
int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
{
return wc_LmsKey_ExportPub_ex(keyDst, keySrc, NULL, INVALID_DEVID);
}
/* Exports the raw LMS public key buffer from key to out buffer.
* The out buffer should be large enough to hold the public key, and
* outLen should indicate the size of the buffer.
@@ -1505,6 +1734,9 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
if ((key == NULL) || (sig == NULL) || (msg == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (msgSz <= 0)) {
ret = BAD_FUNC_ARG;
}
/* Check state. */
if ((ret == 0) && (key->state != WC_LMS_STATE_OK) &&
(key->state != WC_LMS_STATE_VERIFYONLY)) {
@@ -1518,6 +1750,20 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
ret = BUFFER_E;
}
#ifdef WOLF_CRYPTO_CB
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
int res = 0;
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigSz, msg, (word32)msgSz,
&res, WC_PQC_STATEFUL_SIG_TYPE_LMS, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
if (ret == 0 && res != 1)
ret = SIG_VERIFY_E;
return ret;
}
ret = 0; /* fall through to software path */
}
#endif
if (ret == 0) {
WC_DECLARE_VAR(state, LmsState, 1, 0);
@@ -1562,6 +1808,16 @@ int wc_LmsKey_GetKid(LmsKey * key, const byte ** kid, word32* kidSz)
return BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
/* priv_raw is not populated for HSM-backed keys where the device owns
* the private state; the returned KID will be zero bytes. Extend the
* CryptoCb surface if device-side KID retrieval becomes a requirement. */
if (key->devId != INVALID_DEVID) {
WOLFSSL_MSG(
"wc_LmsKey_GetKid: priv_raw may be uninitialised for HSM keys");
}
#endif
/* SEED length is hash length. */
offset = HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN + key->params->hash_len;
*kid = key->priv_raw + offset;
+337 -27
View File
@@ -28,6 +28,7 @@
#define FIPS_NO_WRAPPERS
#endif
#include <wolfssl/wolfcrypt/wc_xmss.h>
#include <wolfssl/wolfcrypt/hash.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
@@ -36,6 +37,99 @@
#include <wolfcrypt/src/misc.c>
#endif
#ifdef WOLF_CRYPTO_CB
#include <wolfssl/wolfcrypt/cryptocb.h>
#endif
/* Compute the digest of msg using the hash function dictated by the XMSS
* parameter set. Crypto-callback / HSM backends that follow PKCS#11 v3.2
* CKM_XMSS / CKM_XMSSMT semantics (pre-computed digest input, see section
* 6.66.8 "XMSS and XMSSMT without hashing") can call this from within
* their callback; backends that take the raw message (e.g. wolfHSM) can
* ignore it. *hashSz is in/out: it must be at least params->n on entry
* and is set to the actual digest length on success.
*
* @param [in] key XMSS key (must have a parameter set bound).
* @param [in] msg Message to hash.
* @param [in] msgSz Length of msg in bytes.
* @param [out] hash Buffer receiving the digest.
* @param [in,out] hashSz On entry, size of hash buffer. On success,
* the digest length.
* @return 0 on success.
* @return BAD_FUNC_ARG when an argument is NULL or the buffer is too
* small for the digest.
* @return NOT_COMPILED_IN when the param set's hash family is disabled.
*/
int wc_XmssKey_HashMsg(const XmssKey* key, const byte* msg, word32 msgSz,
byte* hash, word32* hashSz)
{
int ret = 0;
word32 needSz;
if ((key == NULL) || (msg == NULL) || (hash == NULL) || (hashSz == NULL))
return BAD_FUNC_ARG;
if (key->params == NULL)
return BAD_FUNC_ARG;
needSz = (word32)key->params->n;
if (*hashSz < needSz)
return BAD_FUNC_ARG;
switch (key->params->hash) {
#ifdef WC_XMSS_SHA256
case WC_HASH_TYPE_SHA256: {
/* SHA2_*_192 variants set n=24, but wc_Hash rejects an output
* smaller than WC_SHA256_DIGEST_SIZE. Hash to a full buffer and
* copy the requested prefix, mirroring LMS_SHA256_192. */
byte full[WC_SHA256_DIGEST_SIZE];
ret = wc_Sha256Hash(msg, msgSz, full);
if (ret == 0)
XMEMCPY(hash, full, needSz);
break;
}
#endif
#ifdef WC_XMSS_SHA512
case WC_HASH_TYPE_SHA512:
ret = wc_Hash(WC_HASH_TYPE_SHA512, msg, msgSz, hash, needSz);
break;
#endif
#ifdef WC_XMSS_SHAKE128
case WC_HASH_TYPE_SHAKE128: {
wc_Shake shake;
ret = wc_InitShake128(&shake, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_Shake128_Update(&shake, msg, msgSz);
if (ret == 0)
ret = wc_Shake128_Final(&shake, hash, needSz);
wc_Shake128_Free(&shake);
}
break;
}
#endif
#ifdef WC_XMSS_SHAKE256
case WC_HASH_TYPE_SHAKE256: {
wc_Shake shake;
ret = wc_InitShake256(&shake, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_Shake256_Update(&shake, msg, msgSz);
if (ret == 0)
ret = wc_Shake256_Final(&shake, hash, needSz);
wc_Shake256_Free(&shake);
}
break;
}
#endif
default:
WOLFSSL_MSG("XMSS: unsupported hash for HashMsg");
ret = NOT_COMPILED_IN;
break;
}
if (ret == 0)
*hashSz = needSz;
return ret;
}
/***************************
* DIGEST init and free.
@@ -812,7 +906,7 @@ static WC_INLINE int wc_xmsskey_signupdate(XmssKey* key, byte* sig,
*
* @param [in] key The XMSS key to init.
* @param [in] heap Dynamic memory hint used by subsequent allocations.
* @param [in] devId Unused.
* @param [in] devId Device identifier (used with WOLF_CRYPTO_CB).
*
* @return 0 on success.
* @return BAD_FUNC_ARG when a parameter is NULL.
@@ -821,7 +915,9 @@ int wc_XmssKey_Init(XmssKey* key, void* heap, int devId)
{
int ret = 0;
#ifndef WOLF_CRYPTO_CB
(void) devId;
#endif
/* Validate parameters. */
if (key == NULL) {
@@ -832,12 +928,82 @@ int wc_XmssKey_Init(XmssKey* key, void* heap, int devId)
/* Zeroize key and set state to initialized. */
ForceZero(key, sizeof(XmssKey));
key->heap = heap;
#ifdef WOLF_CRYPTO_CB
key->devId = devId;
#endif
key->state = WC_XMSS_STATE_INITED;
}
return ret;
}
#ifdef WOLF_PRIVATE_KEY_ID
/* Initialize an XmssKey and bind it to a device-side key identifier.
*
* @param [in,out] key XmssKey to initialize.
* @param [in] id Identifier bytes (may be NULL when len is 0).
* @param [in] len Length of id; must be in [0, XMSS_MAX_ID_LEN].
* @param [in] heap Heap hint forwarded to wc_XmssKey_Init.
* @param [in] devId Device identifier.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL.
* @return BUFFER_E when len is negative or exceeds XMSS_MAX_ID_LEN.
*/
int wc_XmssKey_InitId(XmssKey* key, const unsigned char* id, int len,
void* heap, int devId)
{
int ret = 0;
if (key == NULL)
ret = BAD_FUNC_ARG;
if (ret == 0 && (len < 0 || len > XMSS_MAX_ID_LEN))
ret = BUFFER_E;
if (ret == 0)
ret = wc_XmssKey_Init(key, heap, devId);
if (ret == 0 && id != NULL && len != 0) {
XMEMCPY(key->id, id, (size_t)len);
key->idLen = len;
}
return ret;
}
/* Initialize an XmssKey and bind it to a device-side key label.
*
* @param [in,out] key XmssKey to initialize.
* @param [in] label NUL-terminated label string (must be non-empty).
* @param [in] heap Heap hint forwarded to wc_XmssKey_Init.
* @param [in] devId Device identifier.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when key or label is NULL.
* @return BUFFER_E when label is empty or longer than XMSS_MAX_LABEL_LEN.
*/
int wc_XmssKey_InitLabel(XmssKey* key, const char* label, void* heap,
int devId)
{
int ret = 0;
int labelLen = 0;
if (key == NULL || label == NULL)
ret = BAD_FUNC_ARG;
if (ret == 0) {
labelLen = (int)XSTRLEN(label);
if (labelLen == 0 || labelLen > XMSS_MAX_LABEL_LEN)
ret = BUFFER_E;
}
if (ret == 0)
ret = wc_XmssKey_Init(key, heap, devId);
if (ret == 0) {
XMEMCPY(key->label, label, (size_t)labelLen);
key->labelLen = labelLen;
}
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID */
/* Set the XMSS key parameter string.
*
* The input string must be one of the supported parm set names in
@@ -897,6 +1063,62 @@ int wc_XmssKey_SetParamStr(XmssKey* key, const char* str)
return ret;
}
/* Get the XMSS key parameter string for a key whose params have been set.
*
* Performs a reverse lookup from key->oid (and key->is_xmssmt) into the
* supported algorithm tables and returns a pointer to the static parameter
* string (e.g. "XMSS-SHA2_10_256" or "XMSSMT-SHA2_20/4_256"). The returned
* pointer remains valid for the lifetime of the program.
*
* @param [in] key XMSS key with params set via wc_XmssKey_SetParamStr.
* @param [out] str On success, set to the algorithm name.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when a parameter is NULL.
* @return BAD_STATE_E when params have not been set.
* @return NOT_COMPILED_IN when the OID is not in the supported tables.
*/
int wc_XmssKey_GetParamStr(const XmssKey* key, const char** str)
{
int ret = NOT_COMPILED_IN;
unsigned int i;
if ((key == NULL) || (str == NULL)) {
return BAD_FUNC_ARG;
}
if (key->state != WC_XMSS_STATE_PARMSET &&
key->state != WC_XMSS_STATE_OK &&
key->state != WC_XMSS_STATE_VERIFYONLY &&
key->state != WC_XMSS_STATE_NOSIGS) {
return BAD_STATE_E;
}
if (key->is_xmssmt) {
#if WOLFSSL_XMSS_MAX_HEIGHT >= 20
for (i = 0; i < WC_XMSSMT_ALG_LEN; i++) {
if (wc_xmssmt_alg[i].oid == key->oid) {
*str = wc_xmssmt_alg[i].str;
ret = 0;
break;
}
}
#endif
}
else {
#if WOLFSSL_XMSS_MIN_HEIGHT <= 20
for (i = 0; i < WC_XMSS_ALG_LEN; i++) {
if (wc_xmss_alg[i].oid == key->oid) {
*str = wc_xmss_alg[i].str;
ret = 0;
break;
}
}
#endif
}
return ret;
}
/* Force zeros and frees the XMSS key from memory.
*
* This does not touch the private key saved to non-volatile storage.
@@ -1009,8 +1231,10 @@ int wc_XmssKey_SetContext(XmssKey* key, void* context)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (context == NULL)) {
/* Validate parameters. NULL context is allowed: callers with stub
* read/write callbacks (e.g. HSM-backed keys whose private state lives
* in the device) have no meaningful context to pass. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Setting context of an already working key is forbidden. */
@@ -1068,16 +1292,33 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng)
WOLFSSL_MSG("error: XmssKey not ready for generation");
ret = BAD_STATE_E;
}
#ifdef WOLF_CRYPTO_CB
/* HSM-backed keys skip the software write/context callbacks because the
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
* software checks below still run. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
ret = wc_CryptoCb_PqcStatefulSigKeyGen(WC_PQC_STATEFUL_SIG_TYPE_XMSS,
key, rng);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
/* On success, mirror the software path's terminal state so
* subsequent Sign/Verify calls don't fail with BAD_STATE_E. */
if (ret == 0) {
key->state = WC_XMSS_STATE_OK;
}
return ret;
}
ret = 0; /* fall through to software path */
}
#endif
/* Ensure write callback available. */
if ((ret == 0) && (key->write_private_key == NULL)) {
WOLFSSL_MSG("error: XmssKey write callback is not set");
ret = BAD_FUNC_ARG;
}
/* Ensure read/write callback context available. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: XmssKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque to wolfCrypt and may legitimately be NULL
* (e.g. callbacks that read/write a static buffer or HSM-backed keys
* with stub callbacks); no check needed here. */
if (ret == 0) {
/* Allocate sk array. */
@@ -1193,17 +1434,23 @@ int wc_XmssKey_Reload(XmssKey* key)
WOLFSSL_MSG("error: XmssKey not ready for reload");
ret = BAD_STATE_E;
}
#ifdef WOLF_CRYPTO_CB
/* State for HSM-backed keys lives in the device; no software reload. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
WOLFSSL_MSG("wc_XmssKey_Reload is a no-op for HSM-backed keys");
key->state = WC_XMSS_STATE_OK;
return 0;
}
#endif
/* Ensure read and write callbacks are available. */
if ((ret == 0) && ((key->write_private_key == NULL) ||
(key->read_private_key == NULL))) {
WOLFSSL_MSG("error: XmssKey write/read callbacks are not set");
ret = BAD_FUNC_ARG;
}
/* Ensure read and write callback context is available. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: XmssKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque; NULL is allowed. */
if (ret == 0) {
/* Allocate sk array. */
@@ -1315,17 +1562,27 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg,
WOLFSSL_MSG("error: XMSS sig buffer too small");
ret = BUFFER_E;
}
#ifdef WOLF_CRYPTO_CB
/* HSM-backed keys skip the software write/context callbacks because the
* device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the
* software checks below still run. */
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgLen, sig, sigLen,
WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return ret;
ret = 0; /* fall through to software path */
}
#endif
/* Check read and write callbacks available. */
if ((ret == 0) && ((key->write_private_key == NULL) ||
(key->read_private_key == NULL))) {
WOLFSSL_MSG("error: XmssKey write/read callbacks are not set");
ret = BAD_FUNC_ARG;
}
/* Check read/write callback context available. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: XmssKey context is not set");
ret = BAD_FUNC_ARG;
}
/* Callback context is opaque; NULL is allowed. */
if (ret == 0) {
*sigLen = key->params->sig_len;
@@ -1344,14 +1601,36 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg,
*/
int wc_XmssKey_SigsLeft(XmssKey* key)
{
int ret;
int ret = 0;
/* Validate parameter. */
if (key == NULL) {
ret = 0;
if (key == NULL)
return 0;
#ifdef WOLF_CRYPTO_CB
if (key->devId != INVALID_DEVID) {
word32 sigsLeft = 0;
int cbRet = wc_CryptoCb_PqcStatefulSigSigsLeft(
WC_PQC_STATEFUL_SIG_TYPE_XMSS, key, &sigsLeft);
if (cbRet == 0) {
/* Clamp to int range; callers treat 0 as "exhausted". */
return (sigsLeft > (word32)0x7FFFFFFF)
? 0x7FFFFFFF : (int)sigsLeft;
}
/* The device owns the private state; no safe software fallback
* exists because key->sk does not reflect HSM state. */
if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
WOLFSSL_MSG("PqcStatefulSigSigsLeft returned an error");
}
else {
WOLFSSL_MSG("XMSS SigsLeft not supported by device");
}
return 0;
}
#endif
/* Validate state. */
else if (key->state == WC_XMSS_STATE_NOSIGS) {
if (key->state == WC_XMSS_STATE_NOSIGS) {
WOLFSSL_MSG("error: XMSS signatures exhausted");
ret = 0;
}
@@ -1412,10 +1691,15 @@ int wc_XmssKey_GetPubLen(const XmssKey* key, word32* len)
* @return BAD_FUNC_ARG when a key is NULL.
* @return Other negative when digest algorithm initialization failed.
*/
int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc)
int wc_XmssKey_ExportPub_ex(XmssKey* keyDst, const XmssKey* keySrc,
void* heap, int devId)
{
int ret = 0;
#ifndef WOLF_CRYPTO_CB
(void)devId;
#endif
/* Validate parameters. */
if ((keyDst == NULL) || (keySrc == NULL)) {
ret = BAD_FUNC_ARG;
@@ -1432,14 +1716,23 @@ int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc)
keyDst->oid = keySrc->oid;
keyDst->is_xmssmt = keySrc->is_xmssmt;
keyDst->params = keySrc->params;
keyDst->heap = keySrc->heap;
}
if (ret == 0) {
keyDst->heap = heap;
#ifdef WOLF_CRYPTO_CB
keyDst->devId = devId;
#endif
/* Mark keyDst as verify only, to prevent misuse. */
keyDst->state = WC_XMSS_STATE_VERIFYONLY;
}
return 0;
return ret;
}
int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc)
{
return wc_XmssKey_ExportPub_ex(keyDst, keySrc,
(keySrc != NULL) ? keySrc->heap : NULL, INVALID_DEVID);
}
/* Exports the raw XMSS public key buffer from key to out buffer.
@@ -1609,6 +1902,9 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
if ((key == NULL) || (sig == NULL) || (m == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (mLen <= 0)) {
ret = BAD_FUNC_ARG;
}
/* Validate state. */
if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) &&
(key->state != WC_XMSS_STATE_VERIFYONLY)) {
@@ -1623,6 +1919,20 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
ret = BUFFER_E;
}
#ifdef WOLF_CRYPTO_CB
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
int res = 0;
ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigLen, m, (word32)mLen,
&res, WC_PQC_STATEFUL_SIG_TYPE_XMSS, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
if (ret == 0 && res != 1)
ret = SIG_VERIFY_E;
return ret;
}
ret = 0; /* fall through to software path */
}
#endif
if (ret == 0) {
WC_DECLARE_VAR(state, XmssState, 1, 0);
+121 -12
View File
@@ -52993,7 +52993,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t xmss_test(void)
if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); }
/* Export the pub to a verify key. */
ret = wc_XmssKey_ExportPub(&verifyKey, &signingKey);
ret = wc_XmssKey_ExportPub_ex(&verifyKey, &signingKey, NULL, devId);
if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); }
/* Repeat a few times to check that:
@@ -53666,7 +53666,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void)
ERROR_OUT(WC_TEST_RET_ENC_I(kidSz), out);
}
ret = wc_LmsKey_ExportPub(&verifyKey, &signingKey);
ret = wc_LmsKey_ExportPub_ex(&verifyKey, &signingKey, NULL, devId);
if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); }
ret = wc_LmsKey_GetSigLen(&verifyKey, &sigSz);
@@ -53681,7 +53681,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void)
{
word32 smallSz = 1;
wc_lms_write_private_key_cb saved_write_cb;
void* saved_ctx;
/* Undersized sig buffer should return BUFFER_E. */
ret = wc_LmsKey_Sign(&signingKey, sig, &smallSz, (byte *) msg, msgSz);
@@ -53698,15 +53697,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
/* NULL context should return BAD_FUNC_ARG. */
saved_ctx = signingKey.context;
signingKey.context = NULL;
ret = wc_LmsKey_Sign(&signingKey, sig, &sigSz, (byte *) msg, msgSz);
signingKey.context = saved_ctx;
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
ret = 0;
}
@@ -68583,6 +68573,117 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
}
#endif
#endif /* HAVE_ED25519 */
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN) {
int pqcType = info->pk.pqc_stateful_sig_kg.type;
(void)pqcType;
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
LmsKey* lk = (LmsKey*)info->pk.pqc_stateful_sig_kg.key;
lk->devId = INVALID_DEVID;
ret = wc_LmsKey_MakeKey(lk, info->pk.pqc_stateful_sig_kg.rng);
lk->devId = devIdArg;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_XMSS) {
XmssKey* xk = (XmssKey*)info->pk.pqc_stateful_sig_kg.key;
xk->devId = INVALID_DEVID;
ret = wc_XmssKey_MakeKey(xk, info->pk.pqc_stateful_sig_kg.rng);
xk->devId = devIdArg;
}
#endif
}
else if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN) {
int pqcType = info->pk.pqc_stateful_sig_sign.type;
word32 sigSz = *info->pk.pqc_stateful_sig_sign.outSz;
(void)pqcType;
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
LmsKey* lk = (LmsKey*)info->pk.pqc_stateful_sig_sign.key;
lk->devId = INVALID_DEVID;
ret = wc_LmsKey_Sign(lk,
info->pk.pqc_stateful_sig_sign.out, &sigSz,
info->pk.pqc_stateful_sig_sign.msg,
(int)info->pk.pqc_stateful_sig_sign.msgSz);
lk->devId = devIdArg;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_XMSS) {
XmssKey* xk = (XmssKey*)info->pk.pqc_stateful_sig_sign.key;
xk->devId = INVALID_DEVID;
ret = wc_XmssKey_Sign(xk,
info->pk.pqc_stateful_sig_sign.out, &sigSz,
info->pk.pqc_stateful_sig_sign.msg,
(int)info->pk.pqc_stateful_sig_sign.msgSz);
xk->devId = devIdArg;
}
#endif
*info->pk.pqc_stateful_sig_sign.outSz = sigSz;
}
else if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY) {
int pqcType = info->pk.pqc_stateful_sig_verify.type;
int verifyRet = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
#if defined(WOLFSSL_HAVE_LMS)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
LmsKey* lk = (LmsKey*)info->pk.pqc_stateful_sig_verify.key;
lk->devId = INVALID_DEVID;
verifyRet = wc_LmsKey_Verify(lk,
info->pk.pqc_stateful_sig_verify.sig,
info->pk.pqc_stateful_sig_verify.sigSz,
info->pk.pqc_stateful_sig_verify.msg,
(int)info->pk.pqc_stateful_sig_verify.msgSz);
lk->devId = devIdArg;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_XMSS) {
XmssKey* xk = (XmssKey*)info->pk.pqc_stateful_sig_verify.key;
xk->devId = INVALID_DEVID;
verifyRet = wc_XmssKey_Verify(xk,
info->pk.pqc_stateful_sig_verify.sig,
info->pk.pqc_stateful_sig_verify.sigSz,
info->pk.pqc_stateful_sig_verify.msg,
(int)info->pk.pqc_stateful_sig_verify.msgSz);
xk->devId = devIdArg;
}
#endif
if (info->pk.pqc_stateful_sig_verify.res != NULL) {
*info->pk.pqc_stateful_sig_verify.res =
(verifyRet == 0) ? 1 : 0;
}
/* SIG_VERIFY_E is a validity signal, not a crypto error, so
* translate it back to success for the dispatcher. */
if (verifyRet == WC_NO_ERR_TRACE(SIG_VERIFY_E))
verifyRet = 0;
ret = verifyRet;
}
else if (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT) {
int pqcType = info->pk.pqc_stateful_sig_sigs_left.type;
int count = 0;
(void)pqcType;
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_LMS) {
LmsKey* lk = (LmsKey*)info->pk.pqc_stateful_sig_sigs_left.key;
lk->devId = INVALID_DEVID;
count = wc_LmsKey_SigsLeft(lk);
lk->devId = devIdArg;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (pqcType == WC_PQC_STATEFUL_SIG_TYPE_XMSS) {
XmssKey* xk = (XmssKey*)info->pk.pqc_stateful_sig_sigs_left.key;
xk->devId = INVALID_DEVID;
count = wc_XmssKey_SigsLeft(xk);
xk->devId = devIdArg;
}
#endif
if (info->pk.pqc_stateful_sig_sigs_left.sigsLeft != NULL)
*info->pk.pqc_stateful_sig_sigs_left.sigsLeft = (word32)count;
ret = 0;
}
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
#ifdef WOLFSSL_HAVE_MLKEM
if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) {
if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_KYBER) &&
@@ -70016,6 +70117,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
if (ret == 0)
ret = dilithium_test();
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (ret == 0)
ret = xmss_test();
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
if (ret == 0)
ret = lms_test();
#endif
#ifdef HAVE_ED25519
PRIVATE_KEY_UNLOCK();
if (ret == 0)
+58
View File
@@ -86,6 +86,12 @@
#if defined(HAVE_FALCON)
#include <wolfssl/wolfcrypt/falcon.h>
#endif
#if defined(WOLFSSL_HAVE_LMS)
#include <wolfssl/wolfcrypt/wc_lms.h>
#endif
#if defined(WOLFSSL_HAVE_XMSS)
#include <wolfssl/wolfcrypt/wc_xmss.h>
#endif
#ifdef WOLF_CRYPTO_CB_CMD
@@ -344,6 +350,41 @@ typedef struct wc_CryptoInfo {
int type; /* enum wc_PqcSignatureType */
} pqc_sig_check;
#endif
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
struct {
WC_RNG* rng;
void* key;
int type; /* enum wc_PqcStatefulSignatureType */
} pqc_stateful_sig_kg;
struct {
/* Raw message. Backends following the PKCS#11 v3.2
* CKM_HSS / CKM_XMSS convention of operating on a
* pre-computed digest can call wc_LmsKey_HashMsg /
* wc_XmssKey_HashMsg from inside the callback to obtain
* the algorithm-dictated digest of msg. */
const byte* msg;
word32 msgSz;
byte* out;
word32* outSz;
void* key;
int type; /* enum wc_PqcStatefulSignatureType */
} pqc_stateful_sig_sign;
struct {
const byte* sig;
word32 sigSz;
/* Raw message. See sign note. */
const byte* msg;
word32 msgSz;
int* res;
void* key;
int type; /* enum wc_PqcStatefulSignatureType */
} pqc_stateful_sig_verify;
struct {
void* key;
word32* sigsLeft;
int type; /* enum wc_PqcStatefulSignatureType */
} pqc_stateful_sig_sigs_left;
#endif
#ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES
};
#endif
@@ -711,6 +752,23 @@ WOLFSSL_LOCAL int wc_CryptoCb_Ed25519Verify(const byte* sig, word32 sigLen,
const byte* context, byte contextLen);
#endif /* HAVE_ED25519 */
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigGetDevId(int type, void* key);
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigKeyGen(int type, void* key,
WC_RNG* rng);
/* The raw message is forwarded to the callback. Backends that follow the
* PKCS#11 v3.2 CKM_HSS / CKM_XMSS convention (digest input) can call
* wc_LmsKey_HashMsg / wc_XmssKey_HashMsg from inside the callback. */
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigSign(const byte* msg,
word32 msgSz, byte* out, word32* outSz, int type, void* key);
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig,
word32 sigSz, const byte* msg, word32 msgSz, int* res, int type,
void* key);
WOLFSSL_LOCAL int wc_CryptoCb_PqcStatefulSigSigsLeft(int type, void* key,
word32* sigsLeft);
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
#if defined(WOLFSSL_HAVE_MLKEM)
WOLFSSL_LOCAL int wc_CryptoCb_PqcKemGetDevId(int type, void* key);
+27
View File
@@ -1573,6 +1573,14 @@ enum wc_PkType {
WC_PK_TYPE_EC_GET_SIG_SIZE = 29,
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_EC_GET_SIG_SIZE
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN = 30,
WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN = 31,
WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY = 32,
WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT = 33,
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT
#endif
WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX
};
@@ -1607,6 +1615,25 @@ enum wc_PkType {
};
#endif
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
/* Post quantum stateful hash-based signature algorithms. */
enum wc_PqcStatefulSignatureType {
WC_PQC_STATEFUL_SIG_TYPE_NONE = 0,
#define _WC_PQC_STATEFUL_SIG_TYPE_MAX WC_PQC_STATEFUL_SIG_TYPE_NONE
#if defined(WOLFSSL_HAVE_LMS)
WC_PQC_STATEFUL_SIG_TYPE_LMS = 1,
#undef _WC_PQC_STATEFUL_SIG_TYPE_MAX
#define _WC_PQC_STATEFUL_SIG_TYPE_MAX WC_PQC_STATEFUL_SIG_TYPE_LMS
#endif
#if defined(WOLFSSL_HAVE_XMSS)
WC_PQC_STATEFUL_SIG_TYPE_XMSS = 2,
#undef _WC_PQC_STATEFUL_SIG_TYPE_MAX
#define _WC_PQC_STATEFUL_SIG_TYPE_MAX WC_PQC_STATEFUL_SIG_TYPE_XMSS
#endif
WC_PQC_STATEFUL_SIG_TYPE_MAX = _WC_PQC_STATEFUL_SIG_TYPE_MAX
};
#endif
/* settings detection for compile vs runtime math incompatibilities */
enum {
+27
View File
@@ -736,6 +736,13 @@ typedef struct HssPrivKey {
#endif
} HssPrivKey;
#ifndef LMS_MAX_ID_LEN
#define LMS_MAX_ID_LEN 32
#endif
#ifndef LMS_MAX_LABEL_LEN
#define LMS_MAX_LABEL_LEN 32
#endif
typedef struct LmsKey {
/* Public key. */
ALIGN16 byte pub[HSS_PUBLIC_KEY_LEN(LMS_MAX_NODE_LEN)];
@@ -764,6 +771,16 @@ typedef struct LmsKey {
#ifdef WOLF_CRYPTO_CB
/* Device Identifier. */
int devId;
/* Per-device opaque context, populated by the callback. */
void* devCtx;
#endif
#ifdef WOLF_PRIVATE_KEY_ID
/* Optional device-side key identifier. */
byte id[LMS_MAX_ID_LEN];
int idLen;
/* Optional device-side key label. */
char label[LMS_MAX_LABEL_LEN];
int labelLen;
#endif
} LmsKey;
@@ -772,6 +789,12 @@ typedef struct LmsKey {
#endif
WOLFSSL_API int wc_LmsKey_Init(LmsKey * key, void * heap, int devId);
#ifdef WOLF_PRIVATE_KEY_ID
WOLFSSL_API int wc_LmsKey_InitId(LmsKey * key, const unsigned char * id,
int len, void * heap, int devId);
WOLFSSL_API int wc_LmsKey_InitLabel(LmsKey * key, const char * label,
void * heap, int devId);
#endif
WOLFSSL_API int wc_LmsKey_SetLmsParm(LmsKey * key, enum wc_LmsParm lmsParm);
WOLFSSL_API int wc_LmsKey_SetParameters(LmsKey * key, int levels,
int height, int winternitz);
@@ -794,12 +817,16 @@ WOLFSSL_API void wc_LmsKey_Free(LmsKey * key);
WOLFSSL_API int wc_LmsKey_GetSigLen(const LmsKey * key, word32 * len);
WOLFSSL_API int wc_LmsKey_GetPubLen(const LmsKey * key, word32 * len);
WOLFSSL_API int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc);
WOLFSSL_API int wc_LmsKey_ExportPub_ex(LmsKey * keyDst, const LmsKey * keySrc,
void * heap, int devId);
WOLFSSL_API int wc_LmsKey_ExportPubRaw(const LmsKey * key, byte * out,
word32 * outLen);
WOLFSSL_API int wc_LmsKey_ImportPubRaw(LmsKey * key, const byte * in,
word32 inLen);
WOLFSSL_API int wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz,
const byte * msg, int msgSz);
WOLFSSL_API int wc_LmsKey_HashMsg(const LmsKey * key, const byte * msg,
word32 msgSz, byte * hash, word32 * hashSz);
WOLFSSL_API const char * wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm);
WOLFSSL_API const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsRc);
+36
View File
@@ -337,6 +337,13 @@ typedef struct XmssParams {
word8 bds_k;
} XmssParams;
#ifndef XMSS_MAX_ID_LEN
#define XMSS_MAX_ID_LEN 32
#endif
#ifndef XMSS_MAX_LABEL_LEN
#define XMSS_MAX_LABEL_LEN 32
#endif
typedef struct XmssKey {
/* Public key. */
unsigned char pk[2 * WC_XMSS_MAX_N];
@@ -362,6 +369,20 @@ typedef struct XmssKey {
void* heap;
/* State of key. */
enum wc_XmssState state;
#ifdef WOLF_CRYPTO_CB
/* Device Identifier. */
int devId;
/* Per-device opaque context, populated by the callback. */
void* devCtx;
#endif
#ifdef WOLF_PRIVATE_KEY_ID
/* Optional device-side key identifier. */
byte id[XMSS_MAX_ID_LEN];
int idLen;
/* Optional device-side key label. */
char label[XMSS_MAX_LABEL_LEN];
int labelLen;
#endif
} XmssKey;
typedef struct XmssState {
@@ -403,7 +424,14 @@ typedef struct XmssState {
#endif
WOLFSSL_API int wc_XmssKey_Init(XmssKey* key, void* heap, int devId);
#ifdef WOLF_PRIVATE_KEY_ID
WOLFSSL_API int wc_XmssKey_InitId(XmssKey* key, const unsigned char* id,
int len, void* heap, int devId);
WOLFSSL_API int wc_XmssKey_InitLabel(XmssKey* key, const char* label,
void* heap, int devId);
#endif
WOLFSSL_API int wc_XmssKey_SetParamStr(XmssKey* key, const char* str);
WOLFSSL_API int wc_XmssKey_GetParamStr(const XmssKey* key, const char** str);
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
WOLFSSL_API int wc_XmssKey_SetWriteCb(XmssKey* key,
wc_xmss_write_private_key_cb write_cb);
@@ -421,12 +449,20 @@ WOLFSSL_API void wc_XmssKey_Free(XmssKey* key);
WOLFSSL_API int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len);
WOLFSSL_API int wc_XmssKey_GetPubLen(const XmssKey* key, word32* len);
WOLFSSL_API int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc);
WOLFSSL_API int wc_XmssKey_ExportPub_ex(XmssKey* keyDst, const XmssKey* keySrc,
void* heap, int devId);
WOLFSSL_API int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out,
word32* outLen);
WOLFSSL_API int wc_XmssKey_ImportPubRaw(XmssKey* key, const byte* in,
word32 inLen);
WOLFSSL_API int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigSz,
const byte* msg, int msgSz);
/* Compute the digest of a message with the hash function dictated by the
* XMSS parameter set. Useful for crypto-callback / HSM backends that follow
* the PKCS#11 v3.2 CKM_XMSS / CKM_XMSSMT convention of taking a
* pre-computed digest. */
WOLFSSL_API int wc_XmssKey_HashMsg(const XmssKey* key, const byte* msg,
word32 msgSz, byte* hash, word32* hashSz);
WOLFSSL_LOCAL int wc_xmssmt_keygen(XmssState *state, const unsigned char* seed,
unsigned char *sk, unsigned char *pk);