Merge pull request #10466 from Frauschi/slhdsa_cryptocb

Add CryptoCb support for SLH-DSA
This commit is contained in:
David Garske
2026-05-19 13:59:40 -07:00
committed by GitHub
7 changed files with 668 additions and 14 deletions
+97
View File
@@ -42,6 +42,103 @@
int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param,
void* heap, int devId);
/*!
\ingroup SLH_DSA
\brief Initializes an SLH-DSA key with a device-side key identifier (id).
Equivalent to wc_SlhDsaKey_Init() but also stashes a binary id that a
crypto callback can use to look up the underlying key material on the
device. Only available when wolfSSL is built with WOLF_PRIVATE_KEY_ID.
The id is copied into the key object; the caller may free its buffer
immediately after this call returns.
\return 0 on success.
\return BAD_FUNC_ARG if key is NULL, or if id is NULL while len > 0.
\return BUFFER_E if len is negative or greater than SLHDSA_MAX_ID_LEN.
\param [in,out] key Pointer to the SlhDsaKey to initialize.
\param [in] param Parameter set to use (see wc_SlhDsaKey_Init).
\param [in] id Pointer to the device-side key identifier bytes. May be
NULL if len is 0.
\param [in] len Number of bytes in id. Must be in [0, SLHDSA_MAX_ID_LEN].
\param [in] heap Pointer to heap hint for dynamic memory allocation.
May be NULL.
\param [in] devId Device identifier for the crypto callback. Should be
a registered cb devId, not INVALID_DEVID, for the id to be meaningful.
_Example_
\code
SlhDsaKey key;
unsigned char id[8] = { 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08 };
int ret;
ret = wc_SlhDsaKey_Init_id(&key, SLHDSA_SHAKE128F, id, sizeof(id),
NULL, devId);
if (ret != 0) {
// error initializing key with id
}
// ... use key, the cb resolves id -> device key ...
wc_SlhDsaKey_Free(&key);
\endcode
\sa wc_SlhDsaKey_Init
\sa wc_SlhDsaKey_Init_label
\sa wc_SlhDsaKey_Free
*/
int wc_SlhDsaKey_Init_id(SlhDsaKey* key, enum SlhDsaParam param,
const unsigned char* id, int len, void* heap, int devId);
/*!
\ingroup SLH_DSA
\brief Initializes an SLH-DSA key with a device-side key label.
Equivalent to wc_SlhDsaKey_Init() but also stashes a label string that
a crypto callback can use to look up the underlying key material on
the device. Only available when wolfSSL is built with
WOLF_PRIVATE_KEY_ID.
The label length is taken via XSTRLEN, so embedded NUL bytes terminate
the label. The copy stored in key->label is NOT guaranteed to be
NUL-terminated (when the input is exactly SLHDSA_MAX_LABEL_LEN bytes
the entire array is consumed). Consumers must read at most
key->labelLen bytes — do not pass key->label to C-string APIs.
\return 0 on success.
\return BAD_FUNC_ARG if key or label is NULL.
\return BUFFER_E if label is empty or longer than SLHDSA_MAX_LABEL_LEN.
\param [in,out] key Pointer to the SlhDsaKey to initialize.
\param [in] param Parameter set to use (see wc_SlhDsaKey_Init).
\param [in] label NUL-terminated device-side key label string.
\param [in] heap Pointer to heap hint for dynamic memory allocation.
May be NULL.
\param [in] devId Device identifier for the crypto callback. Should be
a registered cb devId, not INVALID_DEVID, for the label to be
meaningful.
_Example_
\code
SlhDsaKey key;
int ret;
ret = wc_SlhDsaKey_Init_label(&key, SLHDSA_SHAKE128F,
"device-key-1", NULL, devId);
if (ret != 0) {
// error initializing key with label
}
// ... use key, the cb resolves label -> device key ...
wc_SlhDsaKey_Free(&key);
\endcode
\sa wc_SlhDsaKey_Init
\sa wc_SlhDsaKey_Init_id
\sa wc_SlhDsaKey_Free
*/
int wc_SlhDsaKey_Init_label(SlhDsaKey* key, enum SlhDsaParam param,
const char* label, void* heap, int devId);
/*!
\ingroup SLH_DSA
+8 -2
View File
@@ -1288,7 +1288,8 @@ int wc_CryptoCb_PqcDecapsulate(const byte* ciphertext, word32 ciphertextLen,
}
#endif /* WOLFSSL_HAVE_MLKEM */
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || \
defined(WOLFSSL_HAVE_SLHDSA)
int wc_CryptoCb_PqcSigGetDevId(int type, void* key)
{
int devId = INVALID_DEVID;
@@ -1307,6 +1308,11 @@ int wc_CryptoCb_PqcSigGetDevId(int type, void* key)
devId = ((falcon_key*) key)->devId;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
if (type == WC_PQC_SIG_TYPE_SLHDSA) {
devId = ((SlhDsaKey*) key)->devId;
}
#endif
return devId;
}
@@ -1456,7 +1462,7 @@ int wc_CryptoCb_PqcSignatureCheckPrivKey(void* key, int type,
return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* HAVE_FALCON || HAVE_DILITHIUM */
#endif /* HAVE_FALCON || HAVE_DILITHIUM || WOLFSSL_HAVE_SLHDSA */
#ifndef NO_AES
#ifdef HAVE_AESGCM
+219 -3
View File
@@ -6557,9 +6557,14 @@ int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param, void* heap,
/* Set heap hint to use with all allocations. */
key->heap = heap;
#ifdef WOLF_CRYPTO_CB
/* Set device id. */
/* Set device context and id. */
key->devCtx = NULL;
key->devId = devId;
#endif
#ifdef WOLF_PRIVATE_KEY_ID
key->idLen = 0;
key->labelLen = 0;
#endif
#ifdef WOLFSSL_SLHDSA_SHA2
if (SLHDSA_IS_SHA2(param)) {
@@ -6595,14 +6600,106 @@ int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param, void* heap,
return ret;
}
#ifdef WOLF_PRIVATE_KEY_ID
/* Initialize an SLH-DSA key with a device key id.
*
* @param [in] key SLH-DSA key.
* @param [in] param SLH-DSA parameter set to use.
* @param [in] id Device-side key handle bytes.
* @param [in] len Length of id in bytes.
* @param [in] heap Dynamic memory allocation hint.
* @param [in] devId Device Id.
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL or when id is NULL with len > 0.
* @return BUFFER_E when len is negative or larger than SLHDSA_MAX_ID_LEN.
*/
int wc_SlhDsaKey_Init_id(SlhDsaKey* key, enum SlhDsaParam param,
const unsigned char* id, int len, void* heap, int devId)
{
int ret = 0;
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Reject id == NULL with len > 0. */
if ((ret == 0) && (id == NULL) && (len > 0)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && ((len < 0) || (len > SLHDSA_MAX_ID_LEN))) {
ret = BUFFER_E;
}
if (ret == 0) {
ret = wc_SlhDsaKey_Init(key, param, heap, devId);
}
if ((ret == 0) && (id != NULL) && (len > 0)) {
XMEMCPY(key->id, id, (size_t)len);
key->idLen = len;
}
return ret;
}
/* Initialize an SLH-DSA key with a device key label.
*
* Label length is taken via XSTRLEN; embedded NULs terminate the label.
*
* @param [in] key SLH-DSA key.
* @param [in] param SLH-DSA parameter set to use.
* @param [in] label NUL-terminated device-side key label.
* @param [in] heap Dynamic memory allocation hint.
* @param [in] devId Device Id.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or label is NULL.
* @return BUFFER_E when label is empty or longer than SLHDSA_MAX_LABEL_LEN.
*/
int wc_SlhDsaKey_Init_label(SlhDsaKey* key, enum SlhDsaParam param,
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 > SLHDSA_MAX_LABEL_LEN)) {
ret = BUFFER_E;
}
}
if (ret == 0) {
ret = wc_SlhDsaKey_Init(key, param, heap, devId);
}
if (ret == 0) {
XMEMCPY(key->label, label, (size_t)labelLen);
key->labelLen = labelLen;
}
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID */
/* Free the SLH-DSA key.
*
* @param [in] key SLH-DSA key. Cannot be used after this call.
*/
void wc_SlhDsaKey_Free(SlhDsaKey* key)
{
/* Check we have a valid key to free. */
if ((key != NULL) && (key->params != NULL)) {
if (key == NULL)
return;
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
if ((key->params != NULL) && (key->devId != INVALID_DEVID)) {
(void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK,
WC_PK_TYPE_PQC_SIG_KEYGEN,
WC_PQC_SIG_TYPE_SLHDSA,
(void*)key);
}
#endif
if (key->params != NULL) {
/* Ensure the private key data is zeroized. */
ForceZero(key->sk, (size_t)key->params->n * 2);
#ifdef WOLFSSL_SLHDSA_SHA2
@@ -6641,6 +6738,17 @@ void wc_SlhDsaKey_Free(SlhDsaKey* key)
wc_Shake256_Free(&key->hash.shk.shake);
}
}
#ifdef WOLF_PRIVATE_KEY_ID
key->idLen = 0;
key->labelLen = 0;
#endif
#ifdef WOLF_CRYPTO_CB
key->devCtx = NULL;
key->devId = INVALID_DEVID;
#endif
/* Marks the key freed; subsequent Frees become no-ops. */
key->params = NULL;
}
/* Set the HashAddress based on message digest data.
@@ -6752,6 +6860,24 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng)
if ((key == NULL) || (key->params == NULL) || (rng == NULL)) {
ret = BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
/* size is the SlhDsaParam enum (S/F variants are distinct). */
ret = wc_CryptoCb_MakePqcSignatureKey(rng,
WC_PQC_SIG_TYPE_SLHDSA, (int)key->params->param, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return ret;
/* fall-through when unavailable */
ret = 0;
}
}
#endif
if (ret == 0) {
/* Steps 1-5: Generate the 3 random hashes. */
ret = wc_RNG_GenerateBlock(rng, key->sk, 3U * key->params->n);
@@ -7251,6 +7377,23 @@ int wc_SlhDsaKey_Sign(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) {
ret = MISSING_KEY;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
ret = wc_CryptoCb_PqcSign(msg, msgSz, sig, sigSz, ctx, ctxSz,
WC_HASH_TYPE_NONE, rng, WC_PQC_SIG_TYPE_SLHDSA, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return ret;
/* fall-through when unavailable */
ret = 0;
}
}
#endif
if (ret == 0) {
/* Generate n bytes of random. */
ret = wc_RNG_GenerateBlock(rng, addRnd, key->params->n);
@@ -7459,6 +7602,27 @@ int wc_SlhDsaKey_Verify(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PUBLIC) == 0) {
ret = MISSING_KEY;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
int res = 0;
ret = wc_CryptoCb_PqcVerify(sig, sigSz, msg, msgSz, ctx, ctxSz,
WC_HASH_TYPE_NONE, &res, WC_PQC_SIG_TYPE_SLHDSA, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
if (ret != 0)
return ret;
return (res == 1) ? 0 : SIG_VERIFY_E;
}
/* fall-through when unavailable */
ret = 0;
}
}
#endif
if (ret == 0) {
byte md[SLHDSA_MAX_MD];
byte n = key->params->n;
@@ -8131,6 +8295,30 @@ int wc_SlhDsaKey_SignHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PRIVATE) == 0) {
ret = MISSING_KEY;
}
/* The cryptocb path below casts hashType to word32 to fit the
* wc_CryptoInfo.preHashType field. Reject negative enum values here so
* the cast can't smuggle a huge unsigned value past the callback (the
* downstream prehash validator only inspects values it knows about). */
else if ((int)hashType < 0) {
ret = BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
ret = wc_CryptoCb_PqcSign(hash, hashSz, sig, sigSz, ctx, ctxSz,
(word32)hashType, rng, WC_PQC_SIG_TYPE_SLHDSA, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return ret;
/* fall-through when unavailable */
ret = 0;
}
}
#endif
if (ret == 0) {
/* Generate n bytes of random. */
ret = wc_RNG_GenerateBlock(rng, addRnd, key->params->n);
@@ -8233,6 +8421,34 @@ int wc_SlhDsaKey_VerifyHash(SlhDsaKey* key, const byte* ctx, byte ctxSz,
else if ((key->flags & WC_SLHDSA_FLAG_PUBLIC) == 0) {
ret = MISSING_KEY;
}
/* The cryptocb path below casts hashType to word32 to fit the
* wc_CryptoInfo.preHashType field. Reject negative enum values here so
* the cast can't smuggle a huge unsigned value past the callback (the
* downstream prehash validator only inspects values it knows about). */
else if ((int)hashType < 0) {
ret = BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
int res = 0;
ret = wc_CryptoCb_PqcVerify(sig, sigSz, hash, hashSz, ctx, ctxSz,
(word32)hashType, &res, WC_PQC_SIG_TYPE_SLHDSA, key);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
if (ret != 0)
return ret;
return (res == 1) ? 0 : SIG_VERIFY_E;
}
/* fall-through when unavailable */
ret = 0;
}
}
#endif
if (ret == 0) {
/* Alg 25, Steps 4-19: Validate caller-supplied pre-hashed digest length
* and select OID for the chosen hash algorithm. */
+304 -4
View File
@@ -57342,7 +57342,11 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
ret = wc_SlhDsaKey_Init(key, param, NULL, INVALID_DEVID);
/* Use the module-global devId so that when cryptocb_test() runs this
* test with a registered callback, MakeKey/Sign/Verify route through
* the cryptocb dispatcher. In standalone runs devId == INVALID_DEVID
* and the calls go straight to the SW implementation. */
ret = wc_SlhDsaKey_Init(key, param, NULL, devId);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
@@ -57365,7 +57369,7 @@ static wc_test_ret_t slhdsa_test_param(enum SlhDsaParam param)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
ret = wc_SlhDsaKey_Init(key_vfy, param, NULL, INVALID_DEVID);
ret = wc_SlhDsaKey_Init(key_vfy, param, NULL, devId);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
@@ -57506,6 +57510,163 @@ out:
#define SLHDSA_TEST_HAVE_ANY_PARAM
#endif
#if defined(WOLF_PRIVATE_KEY_ID) && \
(defined(WOLFSSL_SLHDSA_PARAM_128S) || defined(WOLFSSL_SLHDSA_PARAM_128F) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F))
/* Exercise wc_SlhDsaKey_Init_id / _Init_label argument validation and
* id/label storage round-trip. Independent of any cryptocb device. */
static wc_test_ret_t slhdsa_id_label_test(void)
{
wc_test_ret_t ret;
SlhDsaKey key;
static const unsigned char id[] = {
0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18,
0x29, 0x3a, 0x4b, 0x5c, 0x6d, 0x7e, 0x8f, 0x90
};
static const char label[] = "slh-dsa-test-label";
enum SlhDsaParam param =
#ifdef WOLFSSL_SLHDSA_PARAM_128S
SLHDSA_SHAKE128S;
#elif defined(WOLFSSL_SLHDSA_PARAM_128F)
SLHDSA_SHAKE128F;
#elif defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
SLHDSA_SHA2_128S;
#else
SLHDSA_SHA2_128F;
#endif
/* Zero the stack key so rejection-path tests below don't read
* uninitialized fields if a future Init refactor inspects key state
* before zeroizing it. */
XMEMSET(&key, 0, sizeof(key));
/* NULL key rejected. */
ret = wc_SlhDsaKey_Init_id(NULL, param, id, (int)sizeof(id), HEAP_HINT,
INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_EC(ret);
/* (id == NULL, len > 0) is the silent-contradiction case the original
* review flagged; must be rejected. */
ret = wc_SlhDsaKey_Init_id(&key, param, NULL, 8, HEAP_HINT, INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_EC(ret);
/* Length over the cap rejected with BUFFER_E. */
ret = wc_SlhDsaKey_Init_id(&key, param, id, SLHDSA_MAX_ID_LEN + 1,
HEAP_HINT, INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BUFFER_E))
return WC_TEST_RET_ENC_EC(ret);
/* Negative length rejected. */
ret = wc_SlhDsaKey_Init_id(&key, param, id, -1, HEAP_HINT, INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BUFFER_E))
return WC_TEST_RET_ENC_EC(ret);
/* Successful init copies the id and stores its length. */
ret = wc_SlhDsaKey_Init_id(&key, param, id, (int)sizeof(id), HEAP_HINT,
INVALID_DEVID);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (key.idLen != (int)sizeof(id))
ret = WC_TEST_RET_ENC_NC;
if ((ret == 0) && (XMEMCMP(key.id, id, sizeof(id)) != 0))
ret = WC_TEST_RET_ENC_NC;
wc_SlhDsaKey_Free(&key);
if (ret != 0)
return ret;
XMEMSET(&key, 0, sizeof(key));
/* (id != NULL, len == 0) is accepted as a no-op. */
ret = wc_SlhDsaKey_Init_id(&key, param, id, 0, HEAP_HINT, INVALID_DEVID);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (key.idLen != 0)
ret = WC_TEST_RET_ENC_NC;
wc_SlhDsaKey_Free(&key);
if (ret != 0)
return ret;
XMEMSET(&key, 0, sizeof(key));
/* Boundary: exactly SLHDSA_MAX_ID_LEN bytes must be accepted and round
* trip byte-for-byte. */
{
unsigned char id_max[SLHDSA_MAX_ID_LEN];
int i;
for (i = 0; i < SLHDSA_MAX_ID_LEN; i++)
id_max[i] = (unsigned char)(0x40 + i);
ret = wc_SlhDsaKey_Init_id(&key, param, id_max, SLHDSA_MAX_ID_LEN,
HEAP_HINT, INVALID_DEVID);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (key.idLen != SLHDSA_MAX_ID_LEN)
ret = WC_TEST_RET_ENC_NC;
if ((ret == 0) &&
(XMEMCMP(key.id, id_max, SLHDSA_MAX_ID_LEN) != 0))
ret = WC_TEST_RET_ENC_NC;
wc_SlhDsaKey_Free(&key);
if (ret != 0)
return ret;
XMEMSET(&key, 0, sizeof(key));
}
/* Init_label: NULL label / NULL key rejected. */
ret = wc_SlhDsaKey_Init_label(NULL, param, label, HEAP_HINT,
INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_EC(ret);
ret = wc_SlhDsaKey_Init_label(&key, param, NULL, HEAP_HINT,
INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_EC(ret);
/* Empty label is rejected. */
ret = wc_SlhDsaKey_Init_label(&key, param, "", HEAP_HINT, INVALID_DEVID);
if (ret != WC_NO_ERR_TRACE(BUFFER_E))
return WC_TEST_RET_ENC_EC(ret);
/* Successful init copies the label and stores its length. */
ret = wc_SlhDsaKey_Init_label(&key, param, label, HEAP_HINT,
INVALID_DEVID);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (key.labelLen != (int)XSTRLEN(label))
ret = WC_TEST_RET_ENC_NC;
if ((ret == 0) &&
(XMEMCMP(key.label, label, (size_t)key.labelLen) != 0))
ret = WC_TEST_RET_ENC_NC;
wc_SlhDsaKey_Free(&key);
if (ret != 0)
return ret;
XMEMSET(&key, 0, sizeof(key));
/* Boundary: a SLHDSA_MAX_LABEL_LEN-char label (33-byte buffer with the
* trailing NUL) must be accepted. The stored copy fills the whole
* key->label array and is NOT NUL-terminated; callers must use
* key->labelLen. */
{
char label_max[SLHDSA_MAX_LABEL_LEN + 1];
int i;
for (i = 0; i < SLHDSA_MAX_LABEL_LEN; i++)
label_max[i] = 'L';
label_max[SLHDSA_MAX_LABEL_LEN] = '\0';
ret = wc_SlhDsaKey_Init_label(&key, param, label_max, HEAP_HINT,
INVALID_DEVID);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (key.labelLen != SLHDSA_MAX_LABEL_LEN)
ret = WC_TEST_RET_ENC_NC;
if ((ret == 0) &&
(XMEMCMP(key.label, label_max, SLHDSA_MAX_LABEL_LEN) != 0))
ret = WC_TEST_RET_ENC_NC;
wc_SlhDsaKey_Free(&key);
}
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID && (SHAKE128S|F || SHA2_128S|F) */
wc_test_ret_t slhdsa_test(void)
{
int ret = 0;
@@ -58598,7 +58759,10 @@ wc_test_ret_t slhdsa_test(void)
}
#endif
ret = wc_SlhDsaKey_Init(key_vfy, SLHDSA_SHAKE128S, NULL, INVALID_DEVID);
/* Use module-global devId so this verify routes through the cryptocb
* when registered. In VERIFY_ONLY builds this is the only path through
* slhdsa_test() that exercises the cb. */
ret = wc_SlhDsaKey_Init(key_vfy, SLHDSA_SHAKE128S, NULL, devId);
if (ret != 0) {
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
}
@@ -59317,6 +59481,22 @@ wc_test_ret_t slhdsa_test(void)
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
#if defined(WOLF_PRIVATE_KEY_ID) && \
(defined(WOLFSSL_SLHDSA_PARAM_128S) || defined(WOLFSSL_SLHDSA_PARAM_128F) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F))
/* Init_id/Init_label/Free are available in VERIFY_ONLY builds, so this
* runs regardless of VERIFY_ONLY. Fall through to cleanup on failure
* (no `goto out;` because `out:` is not defined in 128F-only +
* VERIFY_ONLY builds, and no allocations precede this point that
* would need an early bail-out). */
if (ret == 0) {
ret = slhdsa_id_label_test();
if (ret != 0)
wc_test_render_error_message("SLHDSA_ID_LABEL", ret);
}
#endif
#ifdef SLHDSA_TEST_HAVE_ANY_PARAM
out:
#endif
@@ -71995,6 +72175,95 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
ret = 0;
}
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
#if defined(WOLFSSL_HAVE_SLHDSA)
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
if (info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN) {
int pqcType = info->pk.pqc_sig_kg.type;
(void)pqcType;
if (pqcType == WC_PQC_SIG_TYPE_SLHDSA) {
SlhDsaKey* sk = (SlhDsaKey*)info->pk.pqc_sig_kg.key;
sk->devId = INVALID_DEVID;
ret = wc_SlhDsaKey_MakeKey(sk, info->pk.pqc_sig_kg.rng);
sk->devId = devIdArg;
myCtx->exampleVar++;
}
}
else if (info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN) {
int pqcType = info->pk.pqc_sign.type;
(void)pqcType;
if (pqcType == WC_PQC_SIG_TYPE_SLHDSA) {
SlhDsaKey* sk = (SlhDsaKey*)info->pk.pqc_sign.key;
enum wc_HashType phType =
(enum wc_HashType)info->pk.pqc_sign.preHashType;
sk->devId = INVALID_DEVID;
if (phType == WC_HASH_TYPE_NONE) {
ret = wc_SlhDsaKey_Sign(sk,
info->pk.pqc_sign.context,
info->pk.pqc_sign.contextLen,
info->pk.pqc_sign.in,
info->pk.pqc_sign.inlen,
info->pk.pqc_sign.out,
info->pk.pqc_sign.outlen,
info->pk.pqc_sign.rng);
}
else {
ret = wc_SlhDsaKey_SignHash(sk,
info->pk.pqc_sign.context,
info->pk.pqc_sign.contextLen,
info->pk.pqc_sign.in,
info->pk.pqc_sign.inlen,
phType,
info->pk.pqc_sign.out,
info->pk.pqc_sign.outlen,
info->pk.pqc_sign.rng);
}
sk->devId = devIdArg;
myCtx->exampleVar++;
}
}
else
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
if (info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY) {
int pqcType = info->pk.pqc_verify.type;
(void)pqcType;
if (pqcType == WC_PQC_SIG_TYPE_SLHDSA) {
SlhDsaKey* sk = (SlhDsaKey*)info->pk.pqc_verify.key;
enum wc_HashType phType =
(enum wc_HashType)info->pk.pqc_verify.preHashType;
int verifyRet = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
sk->devId = INVALID_DEVID;
if (phType == WC_HASH_TYPE_NONE) {
verifyRet = wc_SlhDsaKey_Verify(sk,
info->pk.pqc_verify.context,
info->pk.pqc_verify.contextLen,
info->pk.pqc_verify.msg,
info->pk.pqc_verify.msglen,
info->pk.pqc_verify.sig,
info->pk.pqc_verify.siglen);
}
else {
verifyRet = wc_SlhDsaKey_VerifyHash(sk,
info->pk.pqc_verify.context,
info->pk.pqc_verify.contextLen,
info->pk.pqc_verify.msg,
info->pk.pqc_verify.msglen,
phType,
info->pk.pqc_verify.sig,
info->pk.pqc_verify.siglen);
}
sk->devId = devIdArg;
if (info->pk.pqc_verify.res != NULL) {
*info->pk.pqc_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;
myCtx->exampleVar++;
}
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#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) &&
@@ -72778,15 +73047,25 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
break;
}
#endif
#ifdef HAVE_DILITHIUM
#if defined(HAVE_DILITHIUM) || defined(WOLFSSL_HAVE_SLHDSA)
case WC_PK_TYPE_PQC_SIG_KEYGEN:
{
#ifdef HAVE_DILITHIUM
if (info->free.subType == WC_PQC_SIG_TYPE_DILITHIUM) {
dilithium_key* dil = (dilithium_key*)info->free.obj;
dil->devId = INVALID_DEVID;
wc_dilithium_free(dil);
ret = 0;
}
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
if (info->free.subType == WC_PQC_SIG_TYPE_SLHDSA) {
SlhDsaKey* slh = (SlhDsaKey*)info->free.obj;
slh->devId = INVALID_DEVID;
wc_SlhDsaKey_Free(slh);
ret = 0;
}
#endif
break;
}
#endif
@@ -73454,6 +73733,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
if (ret == 0)
ret = dilithium_test();
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
if (ret == 0) {
/* Reuse exampleVar as a hit counter for the SLH-DSA cb branches.
* baseline holds the value seen by every other cb at this point;
* we restore it after, so subsequent tests are unaffected.
* Confirms the SLH-DSA cb path was actually exercised; a silent
* SW fallback would otherwise mask a regression in the dispatch.
*
* Only enforce when slhdsa_test() actually runs a cb-routed op:
* !VERIFY_ONLY runs slhdsa_test_param (uses devId), or
* PARAM_128S enables the in-tree KAT verify (also uses devId). */
int baseline = myCtx.exampleVar;
ret = slhdsa_test();
#if !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) || \
defined(WOLFSSL_SLHDSA_PARAM_128S)
if ((ret == 0) && (myCtx.exampleVar == baseline))
ret = WC_TEST_RET_ENC_NC;
#endif
myCtx.exampleVar = baseline;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (ret == 0)
ret = xmss_test();
+8 -3
View File
@@ -86,6 +86,9 @@
#if defined(HAVE_FALCON)
#include <wolfssl/wolfcrypt/falcon.h>
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif
#if defined(WOLFSSL_HAVE_LMS)
#include <wolfssl/wolfcrypt/wc_lms.h>
#endif
@@ -312,7 +315,8 @@ typedef struct wc_CryptoInfo {
int type; /* enum wc_PqcKemType */
} pqc_decaps;
#endif
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || \
defined(WOLFSSL_HAVE_SLHDSA)
struct {
WC_RNG* rng;
int size;
@@ -776,7 +780,8 @@ WOLFSSL_LOCAL int wc_CryptoCb_PqcDecapsulate(const byte* ciphertext,
int type, void* key);
#endif /* WOLFSSL_HAVE_MLKEM */
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)
#if defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || \
defined(WOLFSSL_HAVE_SLHDSA)
WOLFSSL_LOCAL int wc_CryptoCb_PqcSigGetDevId(int type, void* key);
WOLFSSL_LOCAL int wc_CryptoCb_MakePqcSignatureKey(WC_RNG* rng, int type,
@@ -792,7 +797,7 @@ WOLFSSL_LOCAL int wc_CryptoCb_PqcVerify(const byte* sig, word32 siglen,
WOLFSSL_LOCAL int wc_CryptoCb_PqcSignatureCheckPrivKey(void* key, int type,
const byte* pubKey, word32 pubKeySz);
#endif /* HAVE_FALCON || HAVE_DILITHIUM */
#endif /* HAVE_FALCON || HAVE_DILITHIUM || WOLFSSL_HAVE_SLHDSA */
#ifndef NO_AES
#ifdef HAVE_AESGCM
+9 -2
View File
@@ -1559,7 +1559,8 @@ enum wc_PkType {
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_PQC_KEM_DECAPS
#endif
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_SLHDSA)
WC_PK_TYPE_PQC_SIG_KEYGEN = 21,
WC_PK_TYPE_PQC_SIG_SIGN = 22,
WC_PK_TYPE_PQC_SIG_VERIFY = 23,
@@ -1597,7 +1598,8 @@ enum wc_PkType {
};
#endif
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON)
#if defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_SLHDSA)
/* Post quantum signature algorithms */
enum wc_PqcSignatureType {
WC_PQC_SIG_TYPE_NONE = 0,
@@ -1611,6 +1613,11 @@ enum wc_PkType {
WC_PQC_SIG_TYPE_FALCON = 2,
#undef _WC_PQC_SIG_TYPE_MAX
#define _WC_PQC_SIG_TYPE_MAX WC_PQC_SIG_TYPE_FALCON
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
WC_PQC_SIG_TYPE_SLHDSA = 3,
#undef _WC_PQC_SIG_TYPE_MAX
#define _WC_PQC_SIG_TYPE_MAX WC_PQC_SIG_TYPE_SLHDSA
#endif
WC_PQC_SIG_TYPE_MAX = _WC_PQC_SIG_TYPE_MAX
};
+23
View File
@@ -24,6 +24,10 @@
#include <wolfssl/wolfcrypt/types.h>
#ifdef WOLF_CRYPTO_CB
#include <wolfssl/wolfcrypt/cryptocb.h>
#endif
#if FIPS_VERSION3_GE(7,0,0)
#include <wolfssl/wolfcrypt/fips.h>
#endif
@@ -591,6 +595,11 @@ typedef struct SlhDsaParameters {
#define WC_SLHDSA_FLAG_BOTH_KEYS (WC_SLHDSA_FLAG_PRIVATE | \
WC_SLHDSA_FLAG_PUBLIC)
#ifdef WOLF_PRIVATE_KEY_ID
#define SLHDSA_MAX_ID_LEN 32
#define SLHDSA_MAX_LABEL_LEN 32
#endif
/* SLH-DSA key data and state. */
typedef struct SlhDsaKey {
/* Parameters. */
@@ -600,9 +609,17 @@ typedef struct SlhDsaKey {
/* Dynamic memory hint. */
void* heap;
#ifdef WOLF_CRYPTO_CB
/* Device context (opaque, owned by the registered callback). */
void* devCtx;
/* Device Identifier. */
int devId;
#endif
#ifdef WOLF_PRIVATE_KEY_ID
byte id[SLHDSA_MAX_ID_LEN];
int idLen;
char label[SLHDSA_MAX_LABEL_LEN];
int labelLen;
#endif
/* sk_seed | sk_prf | pk_seed, pk_root */
byte sk[32 * 4];
@@ -641,6 +658,12 @@ typedef struct SlhDsaKey {
WOLFSSL_API int wc_SlhDsaKey_Init(SlhDsaKey* key, enum SlhDsaParam param,
void* heap, int devId);
#ifdef WOLF_PRIVATE_KEY_ID
WOLFSSL_API int wc_SlhDsaKey_Init_id(SlhDsaKey* key, enum SlhDsaParam param,
const unsigned char* id, int len, void* heap, int devId);
WOLFSSL_API int wc_SlhDsaKey_Init_label(SlhDsaKey* key, enum SlhDsaParam param,
const char* label, void* heap, int devId);
#endif
WOLFSSL_API void wc_SlhDsaKey_Free(SlhDsaKey* key);
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY