mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
Add wc_MlDsaKey_MakePublicKey
This commit is contained in:
committed by
Paul Adelsbach
parent
1dd5318124
commit
476ce9a5cf
@@ -267,6 +267,43 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng);
|
||||
*/
|
||||
int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed);
|
||||
|
||||
/*!
|
||||
\ingroup ML_DSA
|
||||
|
||||
\brief Derives public key for a wc_MlDsaKey with private key set.
|
||||
If the public key is already set, it is only checked against the
|
||||
private key's tr (H(pk)); use wc_MlDsaKey_CheckKey() for a full key pair
|
||||
check. The private key is still required: a public-only key returns
|
||||
BAD_FUNC_ARG.
|
||||
Available when WC_MLDSA_HAVE_MAKE_PUBLIC_KEY is defined: that is, when
|
||||
WOLFSSL_MLDSA_ASSIGN_KEY, WOLFSSL_MLDSA_NO_MAKE_KEY and
|
||||
WOLFSSL_MLDSA_VERIFY_ONLY are not defined.
|
||||
|
||||
Derivation is always done in software from the local private key bytes,
|
||||
including for a key initialized with a devId: a key whose private key was
|
||||
imported with wc_MlDsaKey_ImportPrivRaw() (or generated in software)
|
||||
derives normally. A key that only references a private key held on a
|
||||
device (wc_MlDsaKey_InitId() / wc_MlDsaKey_InitLabel()) has no local
|
||||
private bytes, so it returns BAD_FUNC_ARG.
|
||||
On PUBLIC_KEY_E an already set public key is left in place; set the
|
||||
matching public key, or re-initialize the key, before deriving.
|
||||
|
||||
\return 0 on success or already set.
|
||||
\return BAD_FUNC_ARG if invalid args or the private key is not set.
|
||||
\return MEMORY_E on allocation failure.
|
||||
\return PUBLIC_KEY_E if the public key does not match the private key's
|
||||
tr or, when derived, its t0.
|
||||
\return NO_VALID_DEVID with WOLF_CRYPTO_CB_ONLY_MLDSA.
|
||||
\return Other negative on error.
|
||||
|
||||
\param [in,out] key Pointer to wc_MlDsaKey.
|
||||
|
||||
\sa wc_MlDsaKey_ImportPrivRaw
|
||||
\sa wc_MlDsaKey_MakeKey
|
||||
\sa wc_MlDsaKey_CheckKey
|
||||
*/
|
||||
int wc_MlDsaKey_MakePublicKey(wc_MlDsaKey* key);
|
||||
|
||||
/*!
|
||||
\ingroup ML_DSA
|
||||
|
||||
|
||||
@@ -1338,6 +1338,7 @@ static int mldsa_vec_encode_eta_bits(const sword32* s, byte d, byte eta,
|
||||
#endif /* !WOLFSSL_MLDSA_NO_MAKE_KEY */
|
||||
|
||||
#if !defined(WOLFSSL_MLDSA_NO_SIGN) || defined(WOLFSSL_MLDSA_CHECK_KEY) || \
|
||||
defined(WC_MLDSA_HAVE_MAKE_PUBLIC_KEY) || \
|
||||
(!defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
|
||||
defined(WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM))
|
||||
|
||||
@@ -1485,6 +1486,7 @@ static int mldsa_decode_eta_4_bits(const byte* p, sword32* s)
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSL_MLDSA_CHECK_KEY) || \
|
||||
defined(WC_MLDSA_HAVE_MAKE_PUBLIC_KEY) || \
|
||||
(!defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \
|
||||
defined(WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM)) || \
|
||||
(!defined(WOLFSSL_MLDSA_NO_SIGN) && \
|
||||
@@ -1579,7 +1581,8 @@ static int mldsa_vec_decode_eta_bits(const byte* p, byte eta, sword32* s,
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif /* !WOLFSSL_MLDSA_NO_SIGN || WOLFSSL_MLDSA_CHECK_KEY */
|
||||
#endif /* !WOLFSSL_MLDSA_NO_SIGN || WOLFSSL_MLDSA_CHECK_KEY ||
|
||||
* WC_MLDSA_HAVE_MAKE_PUBLIC_KEY */
|
||||
|
||||
#ifndef WOLFSSL_MLDSA_NO_MAKE_KEY
|
||||
/* Encode t into t0 and t1.
|
||||
@@ -13022,6 +13025,367 @@ int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed)
|
||||
{
|
||||
return mldsa_key_from_seed_dev(key, seed, 1);
|
||||
}
|
||||
|
||||
#ifdef WC_MLDSA_HAVE_MAKE_PUBLIC_KEY
|
||||
|
||||
#ifndef WOLF_CRYPTO_CB_ONLY_MLDSA
|
||||
/* Encoded size of one polynomial of t0 and of t1. */
|
||||
#define MLDSA_T0_POLY_ENC_SZ (MLDSA_D * MLDSA_N / 8)
|
||||
#define MLDSA_T1_POLY_ENC_SZ (MLDSA_U * MLDSA_N / 8)
|
||||
/* Buffer headroom required for AVX2 encoder. Do not reduce to 0. */
|
||||
#define MLDSA_POLY_ENC_SLACK 8
|
||||
#ifndef WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM
|
||||
/* Polynomials encoded per call. Two keeps the paired AVX-512 encoder, which
|
||||
* needs an even count, in use. */
|
||||
#define MLDSA_T_ENC_POLYS 2
|
||||
#else
|
||||
/* t is calculated a polynomial at a time. */
|
||||
#define MLDSA_T_ENC_POLYS 1
|
||||
#endif
|
||||
/* Encode scratch size for MLDSA_T_ENC_POLYS polynomials of t0. */
|
||||
#define MLDSA_T0_ENC_SCRATCH_SZ \
|
||||
((MLDSA_T_ENC_POLYS * MLDSA_T0_POLY_ENC_SZ) + MLDSA_POLY_ENC_SLACK)
|
||||
|
||||
/* Encode t into t1 and check its t0 against the private key's t0.
|
||||
*
|
||||
* t1 is written straight into the public key, which has the encoder's
|
||||
* headroom as in key generation. t0 is encoded into scratch,
|
||||
* MLDSA_T_ENC_POLYS polynomials at a time.
|
||||
*
|
||||
* @param [in] t Vector t (k polynomials), made positive.
|
||||
* @param [in] k Number of polynomials in t.
|
||||
* @param [in] t0p Encoded t0 from the private key to compare against.
|
||||
* @param [out] t1 Buffer to write encoded t1 into.
|
||||
* @param [in, out] t0Enc Encode scratch of MLDSA_T0_ENC_SCRATCH_SZ bytes.
|
||||
* @param [in, out] diff Non-zero bits are accumulated on a t0 mismatch.
|
||||
* @return 0 on success.
|
||||
* @return Negative when encoding fails.
|
||||
*/
|
||||
static int mldsa_encode_t_check(const sword32* t, byte k, const byte* t0p,
|
||||
byte* t1, byte* t0Enc, int* diff)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
unsigned int n;
|
||||
|
||||
for (i = 0; i < (unsigned int)k; i += n) {
|
||||
n = (unsigned int)k - i;
|
||||
if (n > MLDSA_T_ENC_POLYS) {
|
||||
n = MLDSA_T_ENC_POLYS;
|
||||
}
|
||||
ret = mldsa_vec_encode_t0_t1(t + ((size_t)i * MLDSA_N), (byte)n, t0Enc,
|
||||
t1 + ((size_t)i * MLDSA_T1_POLY_ENC_SZ));
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Accumulate rather than break early so the work stays independent
|
||||
* of where a mismatch falls. */
|
||||
*diff |= ConstantCompare(t0Enc,
|
||||
t0p + ((size_t)i * MLDSA_T0_POLY_ENC_SZ),
|
||||
(int)(n * MLDSA_T0_POLY_ENC_SZ));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check the public key against the private key's tr, which is H(pk).
|
||||
*
|
||||
* @param [in, out] key ML-DSA key with both halves set.
|
||||
* @return 0 when the public key belongs to the private key.
|
||||
* @return PUBLIC_KEY_E when it does not.
|
||||
* @return Negative on hash error.
|
||||
*/
|
||||
static int mldsa_check_pub_tr(wc_MlDsaKey* key)
|
||||
{
|
||||
int ret;
|
||||
byte trCalc[MLDSA_TR_SZ];
|
||||
const byte* tr = key->k + MLDSA_PUB_SEED_SZ + MLDSA_K_SZ;
|
||||
|
||||
ret = mldsa_shake256(&key->shake, key->p, key->params->pkSz, trCalc,
|
||||
MLDSA_TR_SZ);
|
||||
if ((ret == 0) && (ConstantCompare(trCalc, tr, MLDSA_TR_SZ) != 0)) {
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Derive the public key (rho || t1) from the private key.
|
||||
*
|
||||
* @param [in, out] key ML-DSA key with the private key set and no public key.
|
||||
* @return 0 on success.
|
||||
* @return PUBLIC_KEY_E when the derived key does not match the private key.
|
||||
* @return MEMORY_E when dynamic memory allocation fails.
|
||||
* @return Other negative when an error occurs.
|
||||
*/
|
||||
static int mldsa_make_pub_key(wc_MlDsaKey* key)
|
||||
{
|
||||
int ret = 0;
|
||||
const wc_MlDsaParams* params = key->params;
|
||||
sword32* s1 = NULL;
|
||||
sword32* t = NULL;
|
||||
sword32* a = NULL;
|
||||
#ifndef WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM
|
||||
sword32* s2 = NULL;
|
||||
#else
|
||||
sword32* s2t = NULL;
|
||||
sword64* t64 = NULL;
|
||||
byte* h = NULL;
|
||||
#endif
|
||||
WC_DECLARE_VAR(t0Enc, byte, MLDSA_T0_ENC_SCRATCH_SZ, NULL);
|
||||
unsigned int allocSz = 0;
|
||||
int diff = 0;
|
||||
|
||||
#ifdef WOLFSSL_MLDSA_DYNAMIC_KEYS
|
||||
ret = mldsa_alloc_pub_buf(key);
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM
|
||||
#ifdef WC_MLDSA_CACHE_MATRIX_A
|
||||
#ifndef WC_MLDSA_FIXED_ARRAY
|
||||
/* Importing or generating the private key allocates the matrix cache,
|
||||
* so this only guards against a key without one. */
|
||||
if ((ret == 0) && (key->a == NULL)) {
|
||||
key->a = (sword32*)XMALLOC(params->aSz, key->heap,
|
||||
DYNAMIC_TYPE_MLDSA);
|
||||
if (key->a == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
else {
|
||||
key->aSet = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
a = key->a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* s1, s2 and t are always local: the private-vector cache holds the
|
||||
* NTT-domain vectors the next sign reuses, so it is left intact. */
|
||||
if (ret == 0) {
|
||||
/* s1-l, s2-k, t-k. Note: t has same size as s2 */
|
||||
allocSz = (unsigned int)params->s1Sz + params->s2Sz +
|
||||
params->s2Sz;
|
||||
#ifndef WC_MLDSA_CACHE_MATRIX_A
|
||||
/* A-KxL */
|
||||
allocSz += params->aSz;
|
||||
#endif
|
||||
|
||||
s1 = (sword32*)XMALLOC(allocSz, key->heap, DYNAMIC_TYPE_MLDSA);
|
||||
if (s1 == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
else {
|
||||
s2 = s1 + params->s1Sz / sizeof(*s1);
|
||||
t = s2 + params->s2Sz / sizeof(*s2);
|
||||
#ifndef WC_MLDSA_CACHE_MATRIX_A
|
||||
a = t + params->s2Sz / sizeof(*t);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Matrix A is expanded a polynomial at a time, so any cache is left
|
||||
* alone. */
|
||||
if (ret == 0) {
|
||||
/* s1-l, t-1, s2-1, a-1, [t64], h. h is last as its size may be
|
||||
* odd. */
|
||||
allocSz = (unsigned int)params->s1Sz +
|
||||
3U * (unsigned int)MLDSA_POLY_SIZE +
|
||||
(unsigned int)MLDSA_REJ_NTT_POLY_H_SIZE;
|
||||
#ifdef WOLFSSL_MLDSA_SMALL_MEM_POLY64
|
||||
/* t64 */
|
||||
allocSz += 2U * (unsigned int)MLDSA_POLY_SIZE;
|
||||
#endif
|
||||
|
||||
s1 = (sword32*)XMALLOC(allocSz, key->heap, DYNAMIC_TYPE_MLDSA);
|
||||
if (s1 == NULL) {
|
||||
ret = MEMORY_E;
|
||||
}
|
||||
else {
|
||||
t = s1 + params->s1Sz / sizeof(*s1);
|
||||
s2t = t + MLDSA_N;
|
||||
a = s2t + MLDSA_N;
|
||||
#ifdef WOLFSSL_MLDSA_SMALL_MEM_POLY64
|
||||
t64 = (sword64*)(a + MLDSA_N);
|
||||
h = (byte*)(t64 + MLDSA_N);
|
||||
#else
|
||||
h = (byte*)(a + MLDSA_N);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret == 0) {
|
||||
/* MLDSA_T_ENC_POLYS polynomials of encode scratch, so up to about
|
||||
* 850 bytes on the stack unless WOLFSSL_SMALL_STACK asks for it to
|
||||
* be allocated. */
|
||||
WC_ALLOC_VAR_EX(t0Enc, byte, MLDSA_T0_ENC_SCRATCH_SZ, key->heap,
|
||||
DYNAMIC_TYPE_MLDSA, ret = MEMORY_E);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
const byte* rho = key->k;
|
||||
const byte* s1p = key->k + MLDSA_PUB_SEED_SZ + MLDSA_K_SZ +
|
||||
MLDSA_TR_SZ;
|
||||
const byte* s2p = s1p + params->s1EncSz;
|
||||
const byte* t0p = s2p + params->s2EncSz;
|
||||
byte* t1 = key->p + MLDSA_PUB_SEED_SZ;
|
||||
|
||||
XMEMCPY(key->p, rho, MLDSA_PUB_SEED_SZ);
|
||||
#ifdef WC_MLDSA_CACHE_PUB_VECTORS
|
||||
/* Clear cached NTT(t1) since key->p is being rewritten,
|
||||
* preventing reused keys from verifying against a stale t1. */
|
||||
key->pubVecSet = 0;
|
||||
#endif
|
||||
|
||||
ret = mldsa_vec_decode_eta_bits(s1p, params->eta, s1, params->l);
|
||||
#ifndef WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM
|
||||
if (ret == 0) {
|
||||
ret = mldsa_vec_decode_eta_bits(s2p, params->eta, s2,
|
||||
params->k);
|
||||
}
|
||||
|
||||
/* Expand matrix A, unless already cached. */
|
||||
#ifdef WC_MLDSA_CACHE_MATRIX_A
|
||||
if ((ret == 0) && (!key->aSet))
|
||||
#else
|
||||
if (ret == 0)
|
||||
#endif
|
||||
{
|
||||
ret = mldsa_expand_a(&key->shake, rho, params->k, params->l,
|
||||
a, key->heap);
|
||||
#ifdef WC_MLDSA_CACHE_MATRIX_A
|
||||
key->aSet = (ret == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Calculate t = NTT-1(A o NTT(s1)) + s2 */
|
||||
if (ret == 0) {
|
||||
ret = mldsa_calc_t(t, a, s1, s2, params->k, params->l);
|
||||
}
|
||||
/* Make t positive for decomposing. */
|
||||
if (ret == 0) {
|
||||
ret = mldsa_vec_make_pos(t, params->k);
|
||||
}
|
||||
/* Encode t1 into the public key and check the derived t0 against
|
||||
* the private key's authentic t0. (Since t0/t1 split t,
|
||||
* derivation faults are caught here). */
|
||||
if (ret == 0) {
|
||||
ret = mldsa_encode_t_check(t, params->k, t0p, t1, t0Enc, &diff);
|
||||
}
|
||||
#else
|
||||
if (ret == 0) {
|
||||
ret = mldsa_vec_ntt_small_full(s1, params->l);
|
||||
}
|
||||
if (ret == 0) {
|
||||
byte aseed[MLDSA_GEN_A_SEED_SZ];
|
||||
word32 s2Stride = (word32)params->s2EncSz / params->k;
|
||||
unsigned int r;
|
||||
|
||||
XMEMCPY(aseed, rho, MLDSA_PUB_SEED_SZ);
|
||||
for (r = 0; (ret == 0) && (r < params->k); r++) {
|
||||
/* Calculate row r of t = NTT-1(A o NTT(s1)) + s2 */
|
||||
ret = mldsa_calc_t_row(&key->shake, aseed, (byte)r, s1,
|
||||
params->l, s2p + (r * s2Stride), params->eta, t, s2t, a,
|
||||
h, t64);
|
||||
/* Encode row r of t1 into the public key and check row r
|
||||
* of the derived t0 against the private key's t0. */
|
||||
if (ret == 0) {
|
||||
ret = mldsa_encode_t_check(t, 1,
|
||||
t0p + (r * MLDSA_T0_POLY_ENC_SZ),
|
||||
t1 + (r * MLDSA_T1_POLY_ENC_SZ), t0Enc, &diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((ret == 0) && (diff != 0)) {
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
/* Once t0 matches, also verify the derived public key against the
|
||||
* private key's authentic 'tr' hash. Combined with the t0 check,
|
||||
* every bit of derived t is verified. */
|
||||
if (ret == 0) {
|
||||
ret = mldsa_check_pub_tr(key);
|
||||
}
|
||||
if (ret == 0) {
|
||||
key->pubKeySet = 1;
|
||||
}
|
||||
else {
|
||||
/* Don't leave a partial or mismatched public key behind. */
|
||||
XMEMSET(key->p, 0, params->pkSz);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Cleanup --------------------------------------------------- */
|
||||
if (s1 != NULL) {
|
||||
#ifndef WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM
|
||||
/* s1, s2 and t at the front are secret; any A after is public. */
|
||||
ForceZero(s1, (unsigned int)params->s1Sz + (2U * params->s2Sz));
|
||||
#else
|
||||
/* The public a and h sit between and after the secret s1, t, s2 and
|
||||
* t64, so zeroize the entire allocation. */
|
||||
ForceZero(s1, allocSz);
|
||||
#endif
|
||||
XFREE(s1, key->heap, DYNAMIC_TYPE_MLDSA);
|
||||
}
|
||||
/* t0 is private key material, so scrub the encode buffer. */
|
||||
if (WC_VAR_OK(t0Enc)) {
|
||||
ForceZero(t0Enc, MLDSA_T0_ENC_SCRATCH_SZ);
|
||||
}
|
||||
WC_FREE_VAR_EX(t0Enc, key->heap, DYNAMIC_TYPE_MLDSA);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* !WOLF_CRYPTO_CB_ONLY_MLDSA */
|
||||
|
||||
/* Derive the public key (rho || t1) from the private key.
|
||||
*
|
||||
* When the public key is already set it is only checked against the private
|
||||
* key's tr.
|
||||
*
|
||||
* @param [in, out] key ML-DSA key with the private key set.
|
||||
* @return 0 on success, or when the set public key matches tr.
|
||||
* @return BAD_FUNC_ARG when key is NULL, or the private key or parameters
|
||||
* are not set.
|
||||
* @return PUBLIC_KEY_E when the public key does not match the private key.
|
||||
* @return NO_VALID_DEVID when the software implementation is not compiled
|
||||
* in.
|
||||
* @return MEMORY_E when dynamic memory allocation fails.
|
||||
* @return Other negative when an error occurs.
|
||||
*/
|
||||
int wc_MlDsaKey_MakePublicKey(wc_MlDsaKey* key)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Derivation needs the local private key bytes: a devId-bound key with
|
||||
* imported private material derives in software, while a key that only
|
||||
* references a device-held private key (id/label) has prvKeySet == 0. */
|
||||
if ((key == NULL) || (!key->prvKeySet) || (key->params == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA
|
||||
/* No software check or derivation, and no device operation for it. */
|
||||
if (ret == 0) {
|
||||
ret = NO_VALID_DEVID;
|
||||
}
|
||||
#else
|
||||
/* A private-only import leaves any earlier public key set, so check
|
||||
* that it belongs to this private key before reporting success. */
|
||||
if ((ret == 0) && key->pubKeySet) {
|
||||
ret = mldsa_check_pub_tr(key);
|
||||
}
|
||||
else if (ret == 0) {
|
||||
ret = mldsa_make_pub_key(key);
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* WC_MLDSA_HAVE_MAKE_PUBLIC_KEY */
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_MLDSA_NO_SIGN
|
||||
|
||||
@@ -111,6 +111,12 @@
|
||||
#define WC_MLDSA_CACHE_MATRIX_A
|
||||
#endif
|
||||
|
||||
/* wc_MlDsaKey_MakePublicKey() is available. */
|
||||
#if !defined(WOLFSSL_MLDSA_ASSIGN_KEY) && \
|
||||
!defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
|
||||
#define WC_MLDSA_HAVE_MAKE_PUBLIC_KEY
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -734,6 +740,10 @@ WOLFSSL_API
|
||||
int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng);
|
||||
WOLFSSL_API
|
||||
int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed);
|
||||
#ifdef WC_MLDSA_HAVE_MAKE_PUBLIC_KEY
|
||||
WOLFSSL_API
|
||||
int wc_MlDsaKey_MakePublicKey(wc_MlDsaKey* key);
|
||||
#endif
|
||||
|
||||
/* Legacy sign API without context parameter (pre-FIPS 204).
|
||||
* Only available when WOLFSSL_MLDSA_NO_CTX is defined.
|
||||
|
||||
Reference in New Issue
Block a user