Merge pull request #10406 from Frauschi/lms_xmss_certs

Support RFC 9802 LMS and XMSS in X.509 verification
pull/10438/head
Sean Parkinson 2026-05-08 11:48:34 +10:00 committed by GitHub
commit 69a378461a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 1311 additions and 41 deletions

View File

@ -699,6 +699,10 @@ add_option(WOLFSSL_LMSSHA256192
"Enable the LMS SHA_256_192 truncated variant (default: disabled)"
"no" "yes;no")
add_option(WOLFSSL_LMSNOSHA256256
"Disable the LMS SHA_256_256 standard variant (default: disabled)"
"no" "yes;no")
if (WOLFSSL_LMS)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_HAVE_LMS")
@ -706,9 +710,13 @@ if (WOLFSSL_LMS)
if (WOLFSSL_LMSSHA256192)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_LMS_SHA256_192")
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_LMS_SHA256_256")
set_wolfssl_definitions("WOLFSSL_LMS_SHA256_192" RESULT)
endif()
if (WOLFSSL_LMSNOSHA256256)
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_LMS_SHA256_256")
set_wolfssl_definitions("WOLFSSL_NO_LMS_SHA256_256" RESULT)
endif()
endif()

View File

@ -161,6 +161,8 @@ include certs/falcon/include.am
include certs/rsapss/include.am
include certs/dilithium/include.am
include certs/slhdsa/include.am
include certs/lms/include.am
include certs/xmss/include.am
include certs/rpk/include.am
include certs/acert/include.am
include certs/mldsa/include.am

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
# vim:ft=automake
# All paths should be given relative to the root
#
EXTRA_DIST += \
certs/lms/bc_lms_sha256_h5_w4_root.der \
certs/lms/bc_lms_sha256_h10_w8_root.der \
certs/lms/bc_hss_L2_H5_W8_root.der \
certs/lms/bc_hss_L3_H5_W4_root.der \
certs/lms/bc_lms_chain_ca.der \
certs/lms/bc_lms_chain_leaf.der \
certs/lms/bc_lms_native_bc_root.der

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
# vim:ft=automake
# All paths should be given relative to the root
#
EXTRA_DIST += \
certs/xmss/bc_xmss_sha2_10_256_root.der \
certs/xmss/bc_xmss_sha2_16_256_root.der \
certs/xmss/bc_xmssmt_sha2_20_2_256_root.der \
certs/xmss/bc_xmssmt_sha2_20_4_256_root.der \
certs/xmss/bc_xmssmt_sha2_40_8_256_root.der \
certs/xmss/bc_xmss_chain_ca.der \
certs/xmss/bc_xmss_chain_leaf.der

View File

@ -314,6 +314,9 @@ my @slhdsa_shake_192s = (2, 16, 840, 1, 101, 3, 4, 3, 28);
my @slhdsa_shake_192f = (2, 16, 840, 1, 101, 3, 4, 3, 29);
my @slhdsa_shake_256s = (2, 16, 840, 1, 101, 3, 4, 3, 30);
my @slhdsa_shake_256f = (2, 16, 840, 1, 101, 3, 4, 3, 31);
my @hss_lms = ( 1, 2, 840, 113549, 1, 9, 16, 3, 17 );
my @xmss = ( 1, 3, 6, 1, 5, 5, 7, 6, 34 );
my @xmssmt = ( 1, 3, 6, 1, 5, 5, 7, 6, 35 );
my @keys = (
{ name => "ANON", oid => \@anon },
@ -348,6 +351,9 @@ my @keys = (
{ name => "SLH_DSA_SHAKE_192F", oid => \@slhdsa_shake_192f },
{ name => "SLH_DSA_SHAKE_256S", oid => \@slhdsa_shake_256s },
{ name => "SLH_DSA_SHAKE_256F", oid => \@slhdsa_shake_256f },
{ name => "HSS_LMS", oid => \@hss_lms },
{ name => "XMSS", oid => \@xmss },
{ name => "XMSSMT", oid => \@xmssmt },
);
print_sum_enum("Key", "k", \@keys);
@ -1161,6 +1167,12 @@ my @sig_types = (
same => 1 },
{ name => "CTC_SLH_DSA_SHAKE_256F", oid => \@slhdsa_shake_256f,
same => 1 },
{ name => "CTC_HSS_LMS", oid => \@hss_lms,
same => 1 },
{ name => "CTC_XMSS", oid => \@xmss,
same => 1 },
{ name => "CTC_XMSSMT", oid => \@xmssmt,
same => 1 },
);
print_enum("Ctc_SigType", "", \@sig_types, 32, 48);

View File

@ -37806,6 +37806,8 @@ int stopOnFail = 0;
/*----------------------------------------------------------------------------*/
int test_wc_LmsKey_sign_verify(void);
int test_wc_LmsKey_reload_cache(void);
int test_rfc9802_lms_x509_verify(void);
int test_rfc9802_xmss_x509_verify(void);
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
@ -37971,6 +37973,722 @@ int test_wc_LmsKey_reload_cache(void)
return EXPECT_RESULT();
}
/*----------------------------------------------------------------------------*/
/* RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509) tests */
/*----------------------------------------------------------------------------*/
/* For every committed self-signed test certificate confirm:
* - wc_ParseCert succeeds on the RFC 9802 AlgorithmIdentifier encoding
* (OID-only SEQUENCE, no NULL parameters)
* - keyOID and signatureOID are set to the expected values
* - loading as a trust anchor and verifying the same bytes through
* wolfSSL_CertManagerVerifyBuffer exercises the ConfirmSignature
* path and succeeds on a valid cert
* - flipping a byte in the signature AND flipping a byte in the
* TBSCertificate both cause verification to fail.
*
* Test vectors are in certs/lms/ and certs/xmss/, generated with Bouncy
* Castle 1.81. BC's default XMSS / XMSS^MT X.509 encoding uses pre-
* standard ISARA OIDs and wraps the raw RFC 8391 pub key in an OCTET
* STRING, so the fixtures were produced with a small generator that
* overrides the AlgorithmIdentifier and SPKI to match RFC 9802. */
#if (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) && \
!defined(NO_FILESYSTEM) && !defined(NO_CERTS)
/* Sanity bound on a test fixture cert. The largest BC-generated
* fixture we ship (XMSS^MT 40/8) is ~19 KiB; 1 MiB is well above
* any realistic RFC 9802 cert and catches a wild XFTELL. Typed as
* long to match XFTELL's return so the size comparison below isn't
* a mixed long-vs-int compare. */
#define RFC9802_TEST_MAX_CERT_SIZE ((long)(1L << 20))
/* Load a whole file into a freshly-allocated buffer. Caller frees. */
static int rfc9802_load_file(const char* path, byte** out, int* outLen)
{
EXPECT_DECLS;
XFILE f = XBADFILE;
long sz = 0;
size_t got = 0;
byte* buf = NULL;
*out = NULL;
*outLen = 0;
ExpectTrue((f = XFOPEN(path, "rb")) != XBADFILE);
if (f == XBADFILE)
return TEST_FAIL;
if (XFSEEK(f, 0, XSEEK_END) == 0)
sz = XFTELL(f);
(void)XFSEEK(f, 0, XSEEK_SET);
ExpectIntGT(sz, 0);
ExpectIntLT(sz, RFC9802_TEST_MAX_CERT_SIZE);
/* Hard-fail before XMALLOC if XFSEEK / XFTELL produced an unusable
* size: ExpectInt* records the failure but doesn't short-circuit,
* so without this guard a -1 from XFTELL would cast to a multi-GiB
* (size_t) allocation, and a 0 would request a zero-byte malloc. */
if (sz <= 0 || sz >= RFC9802_TEST_MAX_CERT_SIZE) {
XFCLOSE(f);
return TEST_FAIL;
}
ExpectNotNull(buf = (byte*)XMALLOC((size_t)sz, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
if (buf != NULL) {
got = XFREAD(buf, 1, (size_t)sz, f);
ExpectIntEQ(got, (size_t)sz);
/* On a short read the caller would otherwise proceed with a
* partially-initialized buffer and produce cascading parse
* failures driven by the uninitialized tail. Free here so the
* caller's `if (buf == NULL) return TEST_FAIL;` short-circuits
* cleanly with a single recorded failure. */
if (got != (size_t)sz) {
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
buf = NULL;
sz = 0;
}
}
XFCLOSE(f);
*out = buf;
*outLen = (int)sz;
return EXPECT_RESULT();
}
static int rfc9802_verify_one_cert(const char* path, word32 expectedKeyOID,
word32 expectedSigOID)
{
EXPECT_DECLS;
byte* buf = NULL;
byte* tampered = NULL;
int bytes = 0;
DecodedCert cert;
WOLFSSL_CERT_MANAGER* cm = NULL;
word32 certBegin = 0;
word32 sigIndex = 0;
ExpectIntEQ(rfc9802_load_file(path, &buf, &bytes), TEST_SUCCESS);
if (buf == NULL)
return TEST_FAIL;
/* Parse + check OIDs, capture certBegin and sigIndex for later tamper. */
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
ExpectIntEQ((int)cert.keyOID, (int)expectedKeyOID);
ExpectIntEQ((int)cert.signatureOID, (int)expectedSigOID);
certBegin = cert.certBegin;
sigIndex = cert.sigIndex;
wc_FreeDecodedCert(&cert);
/* Full verify against a self-installed trust anchor. */
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, buf, (long)bytes,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
ExpectNotNull(tampered = (byte*)XMALLOC((size_t)bytes, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
/* Negative 1: flip a byte inside the signatureValue BIT STRING.
* Everything after sigIndex is the signatureAlgorithm + the BIT
* STRING payload, so flipping the last byte is always inside the
* signature content. */
if (tampered != NULL) {
XMEMCPY(tampered, buf, (size_t)bytes);
tampered[bytes - 1] ^= 0x01;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, tampered,
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
}
/* Negative 2: flip a byte at the midpoint of the TBSCertificate. The
* TBS is the first element of the outer Certificate SEQUENCE and
* its bytes lie between (certBegin + outerSeqHeader) and sigIndex.
* Picking the midpoint ensures we're inside TBS regardless of the
* fixture's DN / extensions layout. */
if (tampered != NULL && sigIndex > certBegin + 8u) {
word32 midTbs = certBegin + 8 + ((sigIndex - (certBegin + 8)) / 2);
XMEMCPY(tampered, buf, (size_t)bytes);
tampered[midTbs] ^= 0x01;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, tampered,
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
}
/* The fixtures MUST carry a KeyUsage extension with at least one of
* digitalSignature / nonRepudiation / keyCertSign / cRLSign set per
* RFC 9802 sec 3. Re-parse and assert that wolfSSL recorded a non-
* empty set of KeyUsage bits from one of those values. */
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
ExpectIntEQ(cert.extKeyUsageSet, 1);
ExpectIntNE(cert.extKeyUsage & (KEYUSE_DIGITAL_SIG | KEYUSE_CONTENT_COMMIT |
KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN), 0);
wc_FreeDecodedCert(&cert);
XFREE(tampered, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
#endif
/* Direct wolfCrypt-level negative tests for the parameter-derivation
* helpers used by the RFC 9802 parse path. These exercise failure modes
* (unknown algorithm bytes, truncated inputs, mismatches) that a real
* cert body wouldn't easily reach. */
#if defined(WOLFSSL_HAVE_LMS)
static int rfc9802_lms_import_negative(void)
{
EXPECT_DECLS;
LmsKey key;
/* 60-byte buffer matches HSS_PUBLIC_KEY_LEN(32), just like a valid
* SHA-256/M32/H5 key; the algorithm-type bytes are junk so param
* derivation must fail cleanly. */
byte junk[60];
XMEMSET(junk, 0, sizeof(junk));
/* levels=1, lmsType=0xFFFFFFFF, lmOtsType=0xFFFFFFFF. */
junk[3] = 1;
XMEMSET(junk + 4, 0xFF, 4);
XMEMSET(junk + 8, 0xFF, 4);
/* Unknown algorithm types must be rejected. */
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, sizeof(junk)),
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
wc_LmsKey_Free(&key);
/* Too-short buffer: only L + lmsType, no lmOtsType. */
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, 8),
WC_NO_ERR_TRACE(BUFFER_E));
wc_LmsKey_Free(&key);
#if !defined(WOLFSSL_NO_LMS_SHA256_256)
/* The two cases below pin specific SHA-256/M32 parameter codes
* (L1_H5_W8, L1_H5_W4, L1_H10_W2). Skip them in builds where the
* SHA-256/M32 family is disabled -- the family-agnostic checks
* above (junk algorithm types, too-short buffer, GetSigLen on
* unconfigured key) still cover the universal invariants. */
/* Pre-set params that disagree with the raw key's algorithm bytes:
* configure H=5/W=8 but feed buffer that claims H=10 / W=2. */
XMEMSET(junk, 0, sizeof(junk));
junk[3] = 1; /* levels=1 */
junk[7] = 6; /* lmsType = LMS_SHA256_M32_H10 = 6 */
junk[11] = 2; /* lmOtsType = LMOTS_SHA256_N32_W2 = 2 */
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_LmsKey_SetParameters(&key, 1, 5, 8), 0);
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, sizeof(junk)),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
wc_LmsKey_Free(&key);
#endif /* !WOLFSSL_NO_LMS_SHA256_256 */
/* GetSigLen on a key with no params set must not NULL-deref the
* params pointer; it must return BAD_FUNC_ARG instead. */
{
word32 sigLen = 0;
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_LmsKey_GetSigLen(&key, &sigLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
wc_LmsKey_Free(&key);
}
#if !defined(WOLFSSL_NO_LMS_SHA256_256)
/* Partial-write invariant: a length mismatch after a successful
* auto-derive must leave key->params NULL. Build a buffer whose
* leading u32str(L) || lmsType || lmOtsType identifies a known
* parameter set, but truncate to one byte less than the real pub
* key length so the post-derive length check fails. */
{
byte truncated[59]; /* HSS_PUBLIC_KEY_LEN(32) is 60 */
XMEMSET(truncated, 0, sizeof(truncated));
truncated[3] = 1; /* L = 1 */
truncated[7] = 5; /* lmsType = LMS_SHA256_M32_H5 */
truncated[11] = 4; /* lmOtsType = LMOTS_SHA256_N32_W4 */
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectNull(key.params);
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, truncated,
sizeof(truncated)), WC_NO_ERR_TRACE(BUFFER_E));
ExpectNull(key.params);
wc_LmsKey_Free(&key);
}
#endif /* !WOLFSSL_NO_LMS_SHA256_256 */
return EXPECT_RESULT();
}
#endif
#if defined(WOLFSSL_HAVE_XMSS)
static int rfc9802_xmss_import_negative(void)
{
EXPECT_DECLS;
XmssKey key;
byte junk[8];
XMEMSET(junk, 0, sizeof(junk));
/* Too-short buffer. */
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, 2, 0),
WC_NO_ERR_TRACE(BUFFER_E));
wc_XmssKey_Free(&key);
/* Unknown OID (all-zero) for both XMSS and XMSS^MT. */
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, sizeof(junk), 0),
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
wc_XmssKey_Free(&key);
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, sizeof(junk), 1),
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
wc_XmssKey_Free(&key);
/* NULL key / input. */
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(NULL, junk, sizeof(junk), 0),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, NULL, 8, 0),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
wc_XmssKey_Free(&key);
/* GetSigLen on a key with no params set must not NULL-deref the
* params pointer; it must return BAD_FUNC_ARG instead. */
{
word32 sigLen = 0;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_GetSigLen(&key, &sigLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
wc_XmssKey_Free(&key);
}
/* Once params have been configured (state != INITED), the OID
* prefix in the raw key MUST match key->oid and is_xmssmt MUST
* match key->is_xmssmt. Set XMSS-SHA2_10_256 and feed a valid-
* sized buffer whose 4-byte OID prefix is bogus -> BAD_FUNC_ARG. */
{
byte mismatch[XMSS_SHA256_PUBLEN];
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
XMEMSET(mismatch, 0, sizeof(mismatch));
mismatch[3] = 0x77; /* nonsense OID */
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
sizeof(mismatch), 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Same buffer with the correct OID, but is_xmssmt hint
* contradicts the configured family -> BAD_FUNC_ARG. */
mismatch[3] = 0x01; /* WC_XMSS_OID_SHA2_10_256 */
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
sizeof(mismatch), 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
wc_XmssKey_Free(&key);
}
/* Partial-write invariant: a length mismatch after a successful
* auto-derive must leave the key in its INITED state, with
* key->params NULL. */
{
byte truncated[XMSS_SHA256_PUBLEN - 1];
XMEMSET(truncated, 0, sizeof(truncated));
truncated[3] = 0x01;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectNull(key.params);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, truncated,
sizeof(truncated), 0), WC_NO_ERR_TRACE(BUFFER_E));
ExpectNull(key.params);
wc_XmssKey_Free(&key);
}
/* is_xmssmt disambiguation: XMSS oid=1 and XMSS^MT oid=1 share
* the wire-numeric value but resolve to different parameter sets.
* Importing the same 68-byte buffer with hint=0 vs hint=1 must
* land in different tables and produce distinct is_xmssmt. */
{
byte buf[XMSS_SHA256_PUBLEN];
XMEMSET(buf, 0, sizeof(buf));
buf[3] = 0x01;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
ExpectIntEQ((int)key.is_xmssmt, 0);
wc_XmssKey_Free(&key);
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 1), 0);
ExpectIntEQ((int)key.is_xmssmt, 1);
wc_XmssKey_Free(&key);
}
/* Lenient state: re-importing the same pub key into a VERIFYONLY
* key (params set, no private material) succeeds. The second
* call exercises the lenient-state branch. */
{
byte buf[XMSS_SHA256_PUBLEN];
XMEMSET(buf, 0, sizeof(buf));
buf[3] = 0x01;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
wc_XmssKey_Free(&key);
}
/* Strict signature-length check: wc_XmssKey_Verify rejects any
* sigLen != key->params->sig_len. This guards every consumer
* (RFC 9802 X.509, PKCS#7, CMS, ...) against a longer wrapper that
* happens to start with a valid signature. Construct a key in
* VERIFYONLY state, then verify with sig_len + 1 and sig_len - 1
* byte buffers; both must fail with BUFFER_E before any crypto
* runs. The buffer contents are irrelevant since the length check
* fires first. */
{
byte pub[XMSS_SHA256_PUBLEN];
byte* sigBuf = NULL;
word32 sigLen = 0;
const byte msg[1] = { 0 };
XMEMSET(pub, 0, sizeof(pub));
pub[3] = 0x01;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, pub, sizeof(pub), 0), 0);
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
ExpectIntEQ(wc_XmssKey_GetSigLen(&key, &sigLen), 0);
ExpectIntGT(sigLen, 0);
ExpectNotNull(sigBuf = (byte*)XMALLOC((size_t)sigLen + 1, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
if (sigBuf != NULL) {
XMEMSET(sigBuf, 0, (size_t)sigLen + 1);
ExpectIntEQ(wc_XmssKey_Verify(&key, sigBuf, sigLen + 1,
msg, (int)sizeof(msg)), WC_NO_ERR_TRACE(BUFFER_E));
ExpectIntEQ(wc_XmssKey_Verify(&key, sigBuf, sigLen - 1,
msg, (int)sizeof(msg)), WC_NO_ERR_TRACE(BUFFER_E));
XFREE(sigBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
wc_XmssKey_Free(&key);
}
/* BAD_STATE_E branch: WC_XMSS_STATE_OK must be rejected. Reaching
* OK normally requires a successful private-key Reload / sign,
* which is unavailable in WOLFSSL_XMSS_VERIFY_ONLY builds. Force
* the state directly to exercise the rejection without coupling
* this helper to the signing test fixture; sk stays NULL so Free
* is still safe. */
{
byte pub[XMSS_SHA256_PUBLEN];
XMEMSET(pub, 0, sizeof(pub));
pub[3] = 0x01;
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
key.state = WC_XMSS_STATE_OK;
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, pub, sizeof(pub), 0),
WC_NO_ERR_TRACE(BAD_STATE_E));
wc_XmssKey_Free(&key);
}
return EXPECT_RESULT();
}
#endif
/* Walk the AlgorithmIdentifier SEQUENCE that begins at sigIndex and
* locate the byte offset of the last byte of its OID content. Handles
* both short-form (length < 128) and long-form DER length encodings,
* so a future fixture-regenerator that emits longer OIDs / SEQUENCEs
* still drives this test rather than tripping the loud-fail branch.
*
* Returns 0 on success with *oidLastByte set; returns -1 on any DER
* shape mismatch. */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
static int rfc9802_find_sig_alg_oid_last_byte(const byte* buf, word32 bufLen,
word32 sigIndex, word32* oidLastByte)
{
word32 idx = sigIndex;
word32 oidContentLen = 0;
/* AlgorithmIdentifier ::= SEQUENCE { algorithm OID, ... } */
if (idx >= bufLen || buf[idx] != 0x30)
return -1;
idx++;
/* Skip SEQUENCE length (short or long form). */
if (idx >= bufLen)
return -1;
if (buf[idx] < 0x80) {
idx++;
}
else {
word32 nbytes = (word32)(buf[idx] & 0x7F);
if (nbytes == 0 || nbytes > 4 || idx + 1 + nbytes > bufLen)
return -1;
idx += 1 + nbytes;
}
/* algorithm OID tag. */
if (idx >= bufLen || buf[idx] != 0x06)
return -1;
idx++;
/* OID length (short or long form). */
if (idx >= bufLen)
return -1;
if (buf[idx] < 0x80) {
oidContentLen = buf[idx];
idx++;
}
else {
word32 nbytes = (word32)(buf[idx] & 0x7F);
word32 i;
if (nbytes == 0 || nbytes > 4 || idx + 1 + nbytes > bufLen)
return -1;
for (i = 0; i < nbytes; i++)
oidContentLen = (oidContentLen << 8) | buf[idx + 1 + i];
idx += 1 + nbytes;
}
if (oidContentLen == 0 || idx + oidContentLen > bufLen)
return -1;
*oidLastByte = idx + oidContentLen - 1;
return 0;
}
/* Helper: load fixture, locate last byte of outer signatureAlgorithm
* OID, patch it from `expected` to `swap`, and assert that verifying
* the patched cert against itself as a trust anchor fails. */
static int rfc9802_assert_oid_patch_breaks_verify(const char* path,
byte expectedLastByte, byte patchedLastByte)
{
EXPECT_DECLS;
byte* buf = NULL;
int bytes = 0;
DecodedCert cert;
WOLFSSL_CERT_MANAGER* cm = NULL;
word32 sigIndex = 0;
word32 lastOidByte = 0;
ExpectIntEQ(rfc9802_load_file(path, &buf, &bytes), TEST_SUCCESS);
if (buf == NULL)
return TEST_FAIL;
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
sigIndex = cert.sigIndex;
wc_FreeDecodedCert(&cert);
ExpectIntEQ(rfc9802_find_sig_alg_oid_last_byte(buf, (word32)bytes,
sigIndex, &lastOidByte), 0);
/* Sanity-check the fixture matches the family the caller asserted,
* so a future regenerator swapping fixtures fails loudly here
* rather than silently testing the wrong direction. */
ExpectIntEQ((int)buf[lastOidByte], (int)expectedLastByte);
if (lastOidByte < (word32)bytes &&
buf[lastOidByte] == expectedLastByte) {
buf[lastOidByte] = patchedLastByte;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
/* After the patch the cert's outer signatureAlgorithm and SPKI
* disagree. Verification must fail somewhere (at parse, at
* load, or at ConfirmSignature). The load is best-effort -
* some shape changes get caught there, others only at verify. */
(void)wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
WOLFSSL_FILETYPE_ASN1);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, buf,
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
}
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
/* X.509-level negative: swap the outer signatureAlgorithm OID byte so
* the cert declares XMSS where the SPKI is XMSS^MT, and vice versa.
* SigOidMatchesKeyOid must reject both directions before any crypto. */
static int rfc9802_xmss_sig_oid_mismatch(void)
{
EXPECT_DECLS;
/* XMSS sigOID ends 0x22; XMSS^MT sigOID ends 0x23. Patch each
* direction so the asymmetric-key path is exercised both ways -
* a regression that only stripped the check from one branch of
* SigOidMatchesKeyOid would otherwise be missed. */
ExpectIntEQ(rfc9802_assert_oid_patch_breaks_verify(
"./certs/xmss/bc_xmss_sha2_10_256_root.der",
/* expected XMSS */ 0x22, /* patched to XMSS^MT */ 0x23),
TEST_SUCCESS);
ExpectIntEQ(rfc9802_assert_oid_patch_breaks_verify(
"./certs/xmss/bc_xmssmt_sha2_20_2_256_root.der",
/* expected XMSS^MT */ 0x23, /* patched to XMSS */ 0x22),
TEST_SUCCESS);
return EXPECT_RESULT();
}
#endif
/* Exercise a real CA -> leaf certificate chain, not just self-signed.
* Loads the CA as a trust anchor and verifies the leaf against it. */
#if defined(WOLFSSL_HAVE_LMS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
static int rfc9802_lms_chain_verify(void)
{
EXPECT_DECLS;
byte* caBuf = NULL;
byte* leafBuf = NULL;
int caLen = 0;
int leafLen = 0;
WOLFSSL_CERT_MANAGER* cm = NULL;
ExpectIntEQ(rfc9802_load_file("./certs/lms/bc_lms_chain_ca.der",
&caBuf, &caLen), TEST_SUCCESS);
ExpectIntEQ(rfc9802_load_file("./certs/lms/bc_lms_chain_leaf.der",
&leafBuf, &leafLen), TEST_SUCCESS);
ExpectNotNull(cm = wolfSSL_CertManagerNew());
/* Only the CA is a trust anchor; the leaf is verified against it. */
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caBuf, (long)caLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
/* Without loading the CA the leaf must NOT verify. */
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(caBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
#endif
/* Mirror of rfc9802_lms_chain_verify but for an XMSS CA -> leaf pair. */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
static int rfc9802_xmss_chain_verify(void)
{
EXPECT_DECLS;
byte* caBuf = NULL;
byte* leafBuf = NULL;
int caLen = 0;
int leafLen = 0;
WOLFSSL_CERT_MANAGER* cm = NULL;
ExpectIntEQ(rfc9802_load_file("./certs/xmss/bc_xmss_chain_ca.der",
&caBuf, &caLen), TEST_SUCCESS);
ExpectIntEQ(rfc9802_load_file("./certs/xmss/bc_xmss_chain_leaf.der",
&leafBuf, &leafLen), TEST_SUCCESS);
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caBuf, (long)caLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(caBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
#endif
int test_rfc9802_lms_x509_verify(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_LMS)
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
!defined(WOLFSSL_NO_LMS_SHA256_256)
/* Mixed single-level LMS and multi-level HSS fixtures. The HSS
* public key carries only the top-level LMS/LM-OTS types, so
* wc_LmsKey_ImportPubRaw's auto-derive path searches the map
* by (levels, lmsType, lmOtsType). The bc_lms_native_bc_root
* fixture is generated through Bouncy Castle's stock
* JcaContentSignerBuilder("LMS") + JcaX509v3CertificateBuilder
* with no overrides; including it here is the cross-impl interop
* gate (BC's native LMS X.509 path is RFC 9802-compliant for HSS/
* LMS, so wolfSSL must accept it end-to-end).
*
* All fixtures use the SHA-256/M32 family, so the whole block
* is gated on that family being compiled in. Truncated SHA-256/192
* or SHAKE-only builds skip this block. */
static const char* const lmsFiles[] = {
"./certs/lms/bc_lms_sha256_h5_w4_root.der",
"./certs/lms/bc_lms_sha256_h10_w8_root.der",
"./certs/lms/bc_hss_L2_H5_W8_root.der",
"./certs/lms/bc_hss_L3_H5_W4_root.der",
"./certs/lms/bc_lms_native_bc_root.der",
};
size_t i;
for (i = 0; i < sizeof(lmsFiles) / sizeof(lmsFiles[0]); i++) {
ExpectIntEQ(rfc9802_verify_one_cert(lmsFiles[i],
HSS_LMSk, CTC_HSS_LMS), TEST_SUCCESS);
}
ExpectIntEQ(rfc9802_lms_chain_verify(), TEST_SUCCESS);
#endif /* !NO_FILESYSTEM && !NO_CERTS && !WOLFSSL_NO_LMS_SHA256_256 */
/* Pure wolfCrypt-level negative tests don't need filesystem or cert
* support, so they run for any LMS-enabled build. */
ExpectIntEQ(rfc9802_lms_import_negative(), TEST_SUCCESS);
#endif
return EXPECT_RESULT();
}
int test_rfc9802_xmss_x509_verify(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_XMSS)
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
static const char* const xmssFiles[] = {
"./certs/xmss/bc_xmss_sha2_10_256_root.der",
"./certs/xmss/bc_xmss_sha2_16_256_root.der",
};
static const char* const xmssmtFiles[] = {
"./certs/xmss/bc_xmssmt_sha2_20_2_256_root.der",
"./certs/xmss/bc_xmssmt_sha2_20_4_256_root.der",
"./certs/xmss/bc_xmssmt_sha2_40_8_256_root.der",
};
size_t i;
for (i = 0; i < sizeof(xmssFiles) / sizeof(xmssFiles[0]); i++) {
ExpectIntEQ(rfc9802_verify_one_cert(xmssFiles[i],
XMSSk, CTC_XMSS), TEST_SUCCESS);
}
for (i = 0; i < sizeof(xmssmtFiles) / sizeof(xmssmtFiles[0]); i++) {
ExpectIntEQ(rfc9802_verify_one_cert(xmssmtFiles[i],
XMSSMTk, CTC_XMSSMT), TEST_SUCCESS);
}
ExpectIntEQ(rfc9802_xmss_sig_oid_mismatch(), TEST_SUCCESS);
ExpectIntEQ(rfc9802_xmss_chain_verify(), TEST_SUCCESS);
#endif /* !NO_FILESYSTEM && !NO_CERTS */
/* Pure wolfCrypt-level negative tests don't need filesystem or cert
* support, so they run for any XMSS-enabled build. */
ExpectIntEQ(rfc9802_xmss_import_negative(), TEST_SUCCESS);
#endif
return EXPECT_RESULT();
}
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
static int test_sniffer_chain_input_overflow(void)
{
@ -39245,6 +39963,10 @@ TEST_CASE testCases[] = {
TEST_DECL_GROUP("lms", test_wc_LmsKey_sign_verify),
TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_cache),
/* RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509) */
TEST_DECL_GROUP("lms", test_rfc9802_lms_x509_verify),
TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_verify),
/* PEM and DER APIs. */
TEST_DECL(test_wc_PemToDer),
TEST_DECL(test_wc_AllocDer),

View File

@ -4644,6 +4644,17 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32* inOutI
/* SLH-DSA-SHAKE-256f: 2.16.840.1.101.3.4.3.31 */
static const byte sigSlhDsa_Shake_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 31};
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
/* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
static const byte sigHssLmsOid[] =
{42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
/* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
static const byte sigXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
/* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
static const byte sigXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
#endif /* WOLFSSL_HAVE_XMSS */
/* keyType */
#ifndef NO_DSA
@ -4975,6 +4986,17 @@ static int SlhDsaParamToKeyType(enum SlhDsaParam param)
}
#endif /* WOLFSSL_CERT_GEN */
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
/* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
static const byte keyHssLmsOid[] =
{42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
/* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
static const byte keyXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
/* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
static const byte keyXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
#endif /* WOLFSSL_HAVE_XMSS */
/* curveType */
#ifdef HAVE_ECC
@ -5867,6 +5889,22 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz)
*oidSz = sizeof(sigSlhDsa_Shake_256fOid);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case CTC_HSS_LMS:
oid = sigHssLmsOid;
*oidSz = sizeof(sigHssLmsOid);
break;
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case CTC_XMSS:
oid = sigXmssOid;
*oidSz = sizeof(sigXmssOid);
break;
case CTC_XMSSMT:
oid = sigXmssMtOid;
*oidSz = sizeof(sigXmssMtOid);
break;
#endif /* WOLFSSL_HAVE_XMSS */
default:
break;
}
@ -6016,6 +6054,22 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz)
*oidSz = sizeof(keySlhDsa_Shake_256fOid);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
oid = keyHssLmsOid;
*oidSz = sizeof(keyHssLmsOid);
break;
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
oid = keyXmssOid;
*oidSz = sizeof(keyXmssOid);
break;
case XMSSMTk:
oid = keyXmssMtOid;
*oidSz = sizeof(keyXmssMtOid);
break;
#endif /* WOLFSSL_HAVE_XMSS */
default:
break;
}
@ -12462,7 +12516,8 @@ void wc_FreeDecodedCert(DecodedCert* cert)
}
#if defined(HAVE_ED25519) || defined(HAVE_ED448) || defined(HAVE_FALCON) || \
defined(HAVE_DILITHIUM) || defined(WOLFSSL_HAVE_SLHDSA)
defined(HAVE_DILITHIUM) || defined(WOLFSSL_HAVE_SLHDSA) || \
defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
/* Store the key data under the BIT_STRING in dynamically allocated data.
*
* @param [in, out] cert Certificate object.
@ -13387,6 +13442,22 @@ static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx,
ret = StoreKey(cert, source, &srcIdx, maxIdx);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
cert->pkCurveOID = HSS_LMSk;
ret = StoreKey(cert, source, &srcIdx, maxIdx);
break;
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
cert->pkCurveOID = XMSSk;
ret = StoreKey(cert, source, &srcIdx, maxIdx);
break;
case XMSSMTk:
cert->pkCurveOID = XMSSMTk;
ret = StoreKey(cert, source, &srcIdx, maxIdx);
break;
#endif /* WOLFSSL_HAVE_XMSS */
#ifndef NO_DSA
case DSAk:
cert->publicKey = source + pubIdx;
@ -15806,14 +15877,16 @@ static WC_INLINE int IsSigAlgoECDSA(word32 algoOID)
}
#endif
/* Determines if OID is for an EC signing algorithm including ECDSA and EdDSA
* and post-quantum algorithms.
/* Determines whether the signature algorithm's AlgorithmIdentifier omits
* the trailing NULL parameters element. True for ECC / EdDSA / SM2 and
* for the post-quantum families (Falcon, ML-DSA , SLH-DSA, LMS, XMSS).
*
* @param [in] algoOID Algorithm OID.
* @return 1 when is EC signing algorithm.
* @return 1 when the algorithm encodes its AlgorithmIdentifier without
* a NULL parameters element.
* @return 0 otherwise.
*/
static WC_INLINE int IsSigAlgoECC(word32 algoOID)
static WC_INLINE int IsSigAlgoNoParams(word32 algoOID)
{
(void)algoOID;
@ -15864,6 +15937,13 @@ static WC_INLINE int IsSigAlgoECC(word32 algoOID)
|| (algoOID == SLH_DSA_SHA2_192Sk)
|| (algoOID == SLH_DSA_SHA2_256Sk)
#endif
#ifdef WOLFSSL_HAVE_LMS
|| (algoOID == HSS_LMSk)
#endif
#ifdef WOLFSSL_HAVE_XMSS
|| (algoOID == XMSSk)
|| (algoOID == XMSSMTk)
#endif
);
}
@ -15908,7 +15988,7 @@ static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
SetASN_OID(&dataASN[ALGOIDASN_IDX_OID], (word32)algoOID, (word32)type);
/* Hashes, signatures not ECC and keys not RSA output NULL tag. */
if (!(type == oidHashType ||
(type == oidSigType && !IsSigAlgoECC((word32)algoOID)) ||
(type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
(type == oidKeyType && algoOID == RSAk))) {
/* Don't put out NULL DER item. */
dataASN[ALGOIDASN_IDX_NULL].noOut = 1;
@ -16219,6 +16299,25 @@ void FreeSignatureCtx(SignatureCtx* sigCtx)
#endif
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
wc_LmsKey_Free(sigCtx->key.lms);
#ifndef WOLFSSL_NO_MALLOC
XFREE(sigCtx->key.lms, sigCtx->heap, DYNAMIC_TYPE_LMS);
sigCtx->key.lms = NULL;
#endif
break;
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
case XMSSMTk:
wc_XmssKey_Free(sigCtx->key.xmss);
#ifndef WOLFSSL_NO_MALLOC
XFREE(sigCtx->key.xmss, sigCtx->heap, DYNAMIC_TYPE_XMSS);
sigCtx->key.xmss = NULL;
#endif
break;
#endif /* WOLFSSL_HAVE_XMSS */
default:
break;
} /* switch (keyOID) */
@ -16417,6 +16516,17 @@ static int HashForSignature(const byte* buf, word32 bufSz, word32 sigOID,
/* Hashes done in signing operation. */
break;
#endif
#ifdef WOLFSSL_HAVE_LMS
case CTC_HSS_LMS:
/* RFC 9802 sec 2: no digest is applied before signing. */
break;
#endif
#ifdef WOLFSSL_HAVE_XMSS
case CTC_XMSS:
case CTC_XMSSMT:
/* RFC 9802 sec 2: no digest is applied before signing. */
break;
#endif
default:
ret = HASH_TYPE_E;
@ -16614,6 +16724,16 @@ static int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID)
case SLH_DSA_SHA2_256Sk:
return (sigOID == CTC_SLH_DSA_SHA2_256S);
#endif
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
return (sigOID == CTC_HSS_LMS);
#endif
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
return (sigOID == CTC_XMSS);
case XMSSMTk:
return (sigOID == CTC_XMSSMT);
#endif
}
/* Default to reject unknown key types */
@ -17142,6 +17262,54 @@ int ConfirmSignature(SignatureCtx* sigCtx,
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
{
sigCtx->verify = 0;
#ifndef WOLFSSL_NO_MALLOC
sigCtx->key.lms = (LmsKey*)XMALLOC(sizeof(LmsKey),
sigCtx->heap, DYNAMIC_TYPE_LMS);
if (sigCtx->key.lms == NULL) {
ERROR_OUT(MEMORY_E, exit_cs);
}
#endif
if ((ret = wc_LmsKey_Init(sigCtx->key.lms,
sigCtx->heap, sigCtx->devId)) < 0) {
goto exit_cs;
}
if ((ret = wc_LmsKey_ImportPubRaw(sigCtx->key.lms,
key, keySz)) < 0) {
WOLFSSL_MSG("ASN Key import error HSS/LMS");
goto exit_cs;
}
break;
}
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
case XMSSMTk:
{
int is_xmssmt = (keyOID == XMSSMTk);
sigCtx->verify = 0;
#ifndef WOLFSSL_NO_MALLOC
sigCtx->key.xmss = (XmssKey*)XMALLOC(sizeof(XmssKey),
sigCtx->heap, DYNAMIC_TYPE_XMSS);
if (sigCtx->key.xmss == NULL) {
ERROR_OUT(MEMORY_E, exit_cs);
}
#endif
if ((ret = wc_XmssKey_Init(sigCtx->key.xmss,
sigCtx->heap, sigCtx->devId)) < 0) {
goto exit_cs;
}
if ((ret = wc_XmssKey_ImportPubRaw_ex(sigCtx->key.xmss,
key, keySz, is_xmssmt)) < 0) {
WOLFSSL_MSG("ASN Key import error XMSS/XMSS^MT");
goto exit_cs;
}
break;
}
#endif /* WOLFSSL_HAVE_XMSS */
default:
WOLFSSL_MSG("Verify Key type unknown");
ret = ASN_UNKNOWN_OID_E;
@ -17354,6 +17522,25 @@ int ConfirmSignature(SignatureCtx* sigCtx,
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
{
ret = wc_LmsKey_Verify(sigCtx->key.lms, sig, sigSz,
buf, (int)bufSz);
sigCtx->verify = (ret == 0);
break;
}
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
case XMSSMTk:
{
ret = wc_XmssKey_Verify(sigCtx->key.xmss, sig, sigSz,
buf, (int)bufSz);
sigCtx->verify = (ret == 0);
break;
}
#endif /* WOLFSSL_HAVE_XMSS */
default:
break;
} /* switch (keyOID) */
@ -17576,6 +17763,33 @@ int ConfirmSignature(SignatureCtx* sigCtx,
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
case HSS_LMSk:
{
if (sigCtx->verify == 1) {
ret = 0;
}
else {
WOLFSSL_MSG("HSS/LMS Verify didn't match");
ret = ASN_SIG_CONFIRM_E;
}
break;
}
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
case XMSSk:
case XMSSMTk:
{
if (sigCtx->verify == 1) {
ret = 0;
}
else {
WOLFSSL_MSG("XMSS/XMSS^MT Verify didn't match");
ret = ASN_SIG_CONFIRM_E;
}
break;
}
#endif /* WOLFSSL_HAVE_XMSS */
default:
break;
} /* switch (keyOID) */
@ -21188,7 +21402,7 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
ret = ASN_SIG_OID_E;
}
/* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
else if (IsSigAlgoECC(cert->signatureOID)) {
else if (IsSigAlgoNoParams(cert->signatureOID)) {
#ifndef WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED
if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS_NULL].tag != 0) {
WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
@ -27875,7 +28089,7 @@ int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz,
}
}
if (ret == 0) {
if (IsSigAlgoECC((word32)sigAlgoType)) {
if (IsSigAlgoNoParams((word32)sigAlgoType)) {
/* ECDSA and EdDSA doesn't have NULL tagged item. */
dataASN[SIGASN_IDX_SIGALGO_NULL].noOut = 1;
}
@ -28086,7 +28300,7 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
(word32)cert->sigType, oidSigType);
}
if (IsSigAlgoECC((word32)cert->sigType)) {
if (IsSigAlgoNoParams((word32)cert->sigType)) {
/* No NULL tagged item with ECDSA and EdDSA signature OIDs. */
dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
}
@ -37976,7 +38190,7 @@ int ParseX509Acert(DecodedAcert* acert, int verify)
}
/* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
if (IsSigAlgoECC(acert->signatureOID)) {
if (IsSigAlgoNoParams(acert->signatureOID)) {
if ((dataASN[ACERT_IDX_SIGALGO_PARAMS_NULL].tag != 0)
#ifdef WC_RSA_PSS
|| (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0)

View File

@ -2947,7 +2947,7 @@ static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
word32 length = 0;
tagSz = ((type == oidHashType ||
(type == oidSigType && !IsSigAlgoECC((word32)algoOID)) ||
(type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
(type == oidKeyType && algoOID == RSAk)) &&
(absentParams == FALSE)) ? 2U : 0U;
algoName = OidFromId((word32)algoOID, (word32)type, &algoSz);

View File

@ -1578,39 +1578,127 @@ int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen)
/* Imports a raw public key buffer from in array to LmsKey key.
*
* The LMS parameters must be set first with wc_LmsKey_SetLmsParm or
* wc_LmsKey_SetParameters, and inLen must match the length returned
* by wc_LmsKey_GetPubLen.
* If the LMS parameters have already been configured (via
* wc_LmsKey_SetLmsParm or wc_LmsKey_SetParameters), the levels /
* lms_algorithm_type / lmots_algorithm_type encoded in the raw key are
* checked for consistency and inLen must match wc_LmsKey_GetPubLen.
*
* Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
* If the parameters have not yet been set (key->params == NULL), they
* are derived from the raw public key prefix (RFC 8554 sec 3.3 / sec
* 6.1: u32str(L) || lms_algorithm_type || lmots_algorithm_type) and
* matched against the static parameter map. The candidate is held in
* a local until the length check passes, so a length mismatch leaves
* key->params NULL.
*
* Accepts a key in INITED, PARMSET or VERIFYONLY state. WC_LMS_STATE_OK
* is rejected because the key already has private material loaded and
* silently overwriting key->pub would create an inconsistent priv/pub
* pair.
*
* @param [in, out] key LMS key to put public key in.
* @param [in] in Buffer holding encoded public key.
* @param [in] inLen Length of encoded public key in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or in is NULL.
* @return BUFFER_E when inLen does not match public key length by parameters.
* @return BAD_FUNC_ARG when key or in is NULL, or when the raw key's
* levels / lmsType / lmOtsType disagree with pre-set params.
* @return BAD_STATE_E when wrong state for operation.
* @return BUFFER_E when inLen is too small to contain the LMS type
* fields, or doesn't match the public key length determined
* by parameters.
* @return NOT_COMPILED_IN when the derived parameter set isn't built in.
*/
int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
{
int ret = 0;
int ret = 0;
const LmsParams* matched = NULL;
/* Validate parameters. */
if ((key == NULL) || (in == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Reject states where re-importing the public bytes would desync
* the key. INITED (params unset, will derive), PARMSET (params set
* but no private material) and VERIFYONLY (already a pub-only key)
* are all safe. OK means a private key is loaded; silently
* overwriting key->pub would create a priv/pub mismatch. Mirrors
* the wc_XmssKey_ImportPubRaw_ex post-condition added in the same
* RFC 9802 series. */
if ((ret == 0) &&
(inLen != (word32)HSS_PUBLIC_KEY_LEN(key->params->hash_len))) {
/* Something inconsistent. Parameters weren't set, or input
* pub key is wrong.*/
return BUFFER_E;
(key->state != WC_LMS_STATE_INITED) &&
(key->state != WC_LMS_STATE_PARMSET) &&
(key->state != WC_LMS_STATE_VERIFYONLY)) {
WOLFSSL_MSG("error: LMS key not ready for import");
ret = BAD_STATE_E;
}
/* Need at least L || lmsType || lmOtsType to derive or validate. */
if ((ret == 0) && (inLen < (word32)(LMS_L_LEN + 2 * LMS_TYPE_LEN))) {
ret = BUFFER_E;
}
if (ret == 0) {
XMEMCPY(key->pub, in, inLen);
word32 levels = 0;
word32 lmsType = 0;
word32 lmOtsType = 0;
if (key->state != WC_LMS_STATE_OK)
key->state = WC_LMS_STATE_VERIFYONLY;
/* RFC 8554 sec 3.3 / sec 6.1: HSS public key = u32str(L) || pub[0],
* where pub[0] starts with lms_algorithm_type || lmots_algorithm_type.
*/
ato32(in + 0, &levels);
ato32(in + LMS_L_LEN, &lmsType);
ato32(in + LMS_L_LEN + LMS_TYPE_LEN, &lmOtsType);
/* The wire format carries only the RFC type code (low 12 bits);
* params->lmsType / lmOtsType also pack a wolfSSL-internal hash
* family flag in the high 4 bits (LMS_HASH_MASK). Compare on the
* RFC code only -- safe as long as low-12-bit codes stay globally
* distinct across hash families (see wc_lms_impl.c step 3.d-e
* note). */
if (key->params == NULL) {
/* Auto-derive: find matching entry in the static map. Hold
* the candidate in a local until the length check passes to
* avoid leaving key->params half-set on failure. */
int i;
ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
for (i = 0; i < WC_LMS_MAP_LEN; i++) {
if (((word32)wc_lms_map[i].params.levels == levels) &&
((word32)(wc_lms_map[i].params.lmsType & LMS_H_W_MASK) ==
lmsType) &&
((word32)(wc_lms_map[i].params.lmOtsType& LMS_H_W_MASK) ==
lmOtsType)) {
matched = &wc_lms_map[i].params;
ret = 0;
break;
}
}
if (ret != 0) {
WOLFSSL_MSG("error: LMS params from pub key not supported");
}
}
else {
/* Validate against pre-set params. */
if (((word32)key->params->levels != levels) ||
((word32)(key->params->lmsType & LMS_H_W_MASK) != lmsType) ||
((word32)(key->params->lmOtsType & LMS_H_W_MASK) != lmOtsType)){
WOLFSSL_MSG("error: LMS pub key doesn't match set params");
ret = BAD_FUNC_ARG;
}
else {
matched = key->params;
}
}
}
if ((ret == 0) &&
(inLen != (word32)HSS_PUBLIC_KEY_LEN(matched->hash_len))) {
ret = BUFFER_E;
}
if (ret == 0) {
/* Commit params (no-op when already set) and copy the key.
* State is INITED/PARMSET/VERIFYONLY here (OK is rejected
* above), so promoting to VERIFYONLY is always correct. */
key->params = matched;
XMEMCPY(key->pub, in, inLen);
key->state = WC_LMS_STATE_VERIFYONLY;
}
return ret;
@ -1625,14 +1713,15 @@ int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
* @param [in] key LMS key.
* @param [out] len Length of a signature in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or len is NULL.
* @return BAD_FUNC_ARG when key or len is NULL, or when the LMS
* parameters have not been configured on the key.
*/
int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (len == NULL)) {
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
ret = BAD_FUNC_ARG;
}

View File

@ -1688,10 +1688,156 @@ int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out, word32* outLen)
return ret;
}
/* Imports a raw public key buffer from in array to XmssKey key, taking
* an is_xmssmt hint to disambiguate the XMSS / XMSS^MT OID namespaces
* when params have not yet been configured on the key.
*
* Accepts a key in INITED, PARMSET or VERIFYONLY state. WC_XMSS_STATE_OK
* is rejected because the key already has private material loaded and
* silently overwriting key->pk would create an inconsistent priv/pub
* pair. When state is INITED, params are derived from the 4-byte OID
* prefix at the start of the raw key (RFC 8391 Appendix B.1 / C.1)
* using is_xmssmt to pick the XMSS or XMSS^MT table; key->oid,
* key->is_xmssmt and key->params are populated only after the public
* key length check passes, so a length mismatch leaves the key in its
* original state. When params have already been set, the 4-byte OID
* prefix and the is_xmssmt hint are checked for consistency.
*
* @param [in, out] key XMSS key.
* @param [in] in Array holding public key.
* @param [in] inLen Length of array in bytes.
* @param [in] is_xmssmt 0 to search the XMSS table, non-zero to
* search the XMSS^MT table.
*
* @return 0 on success.
* @return BAD_FUNC_ARG when a parameter is NULL or the OID prefix /
* is_xmssmt hint contradicts pre-set params.
* @return BUFFER_E if array is incorrect size.
* @return BAD_STATE_E when wrong state for operation.
* @return NOT_COMPILED_IN when the derived parameter set isn't built in.
*/
int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
int is_xmssmt)
{
int ret = 0;
word32 oid = 0;
const XmssParams* matched = NULL;
/* Validate parameters. */
if ((key == NULL) || (in == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (inLen < XMSS_OID_LEN)) {
ret = BUFFER_E;
}
/* Reject states where the key is unusable for re-import. INITED
* means params are unset (we'll derive them); PARMSET / VERIFYONLY
* means params are set without a working private key (we just
* overwrite the pub bytes). OK means a private key is already
* loaded; overwriting key->pk silently would desync priv/pub. */
if ((ret == 0) &&
(key->state != WC_XMSS_STATE_INITED) &&
(key->state != WC_XMSS_STATE_PARMSET) &&
(key->state != WC_XMSS_STATE_VERIFYONLY)) {
WOLFSSL_MSG("error: XMSS key not ready for import");
ret = BAD_STATE_E;
}
if (ret == 0) {
/* OID is encoded big-endian in the first 4 bytes. */
ato32(in, &oid);
if (key->state == WC_XMSS_STATE_INITED) {
/* Auto-derive params from OID prefix, using is_xmssmt hint.
* Hold the candidate in a local; commit to the key only
* after the length check below succeeds. The compile-time
* gates here mirror the wc_xmss_alg / wc_xmssmt_alg table
* definitions exactly, so a build with one family disabled
* still rejects pubkeys for that family with NOT_COMPILED_IN
* rather than referring to undefined WC_*_ALG_LEN. */
ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
if (is_xmssmt) {
#if WOLFSSL_XMSS_MAX_HEIGHT >= 20
unsigned int i;
for (i = 0; i < WC_XMSSMT_ALG_LEN; i++) {
if (wc_xmssmt_alg[i].oid == oid) {
matched = &wc_xmssmt_alg[i].params;
ret = 0;
break;
}
}
#else
/* XMSS^MT disabled at compile time; ret stays at
* NOT_COMPILED_IN. */
(void)oid;
#endif
}
else {
#if WOLFSSL_XMSS_MIN_HEIGHT <= 20
unsigned int i;
for (i = 0; i < WC_XMSS_ALG_LEN; i++) {
if (wc_xmss_alg[i].oid == oid) {
matched = &wc_xmss_alg[i].params;
ret = 0;
break;
}
}
#else
/* XMSS disabled at compile time; ret stays at
* NOT_COMPILED_IN. */
(void)oid;
#endif
}
if (ret != 0) {
WOLFSSL_MSG("error: XMSS OID from pub key not supported");
}
}
else {
/* Params already set; OID prefix and family must match. */
if (oid != key->oid) {
WOLFSSL_MSG("error: XMSS pub OID doesn't match set params");
ret = BAD_FUNC_ARG;
}
else if ((is_xmssmt ? 1 : 0) != key->is_xmssmt) {
WOLFSSL_MSG("error: XMSS is_xmssmt hint contradicts set params");
ret = BAD_FUNC_ARG;
}
else {
matched = key->params;
}
}
}
/* Length check using the candidate (auto-derived) or pre-set
* params, without committing yet. */
if ((ret == 0) && (inLen != (word32)(XMSS_OID_LEN + matched->pk_len))) {
ret = BUFFER_E;
}
if (ret == 0) {
/* Commit (no-op when params were already set) and copy pub
* bytes (skipping the OID prefix). */
if (key->state == WC_XMSS_STATE_INITED) {
key->params = matched;
key->oid = oid;
key->is_xmssmt = is_xmssmt ? 1 : 0;
}
XMEMCPY(key->pk, in + XMSS_OID_LEN, matched->pk_len);
key->state = WC_XMSS_STATE_VERIFYONLY;
}
return ret;
}
/* Imports a raw public key buffer from in array to XmssKey key.
*
* The XMSS parameters must be set first with wc_XmssKey_SetParamStr,
* and inLen must match the length returned by wc_XmssKey_GetPubLen.
* If the caller only has the raw public-key bytes and has not yet
* configured the parameter set, use wc_XmssKey_ImportPubRaw_ex which
* derives parameters from the OID prefix at the start of the buffer.
*
* @param [in, out] key XMSS key.
* @param [in] in Array holding public key.
@ -1760,12 +1906,13 @@ int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len)
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (len == NULL)) {
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Validate state. */
if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) &&
(key->state != WC_XMSS_STATE_PARMSET)) {
(key->state != WC_XMSS_STATE_PARMSET) &&
(key->state != WC_XMSS_STATE_VERIFYONLY)) {
ret = BAD_STATE_E;
}
@ -1796,7 +1943,8 @@ int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len)
* @return SIG_VERIFY_E when signature did not verify message.
* @return BAD_FUNC_ARG when a parameter is NULL.
* @return BAD_STATE_E when wrong state for operation.
* @return BUFFER_E when sigLen is too small.
* @return BUFFER_E when sigLen does not exactly match the parameter-set
* signature length (use wc_XmssKey_GetSigLen).
*/
int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
const byte* m, int mLen)
@ -1815,9 +1963,10 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
WOLFSSL_MSG("error: XMSS key not ready for verification");
ret = BAD_STATE_E;
}
/* Check the signature is the big enough. */
if ((ret == 0) && (sigLen < key->params->sig_len)) {
/* Signature buffer too small. */
/* Check the signature length is exactly the parameter-set size.
* XMSS / XMSS^MT signatures are fixed-length per parameter set, so
* any buffer that's longer or shorter than sig_len is malformed. */
if ((ret == 0) && (sigLen != key->params->sig_len)) {
ret = BUFFER_E;
}

View File

@ -70,6 +70,12 @@ that can be serialized and deserialized in a cross-platform way.
#ifdef WOLFSSL_HAVE_SLHDSA
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif
#ifdef WOLFSSL_HAVE_LMS
#include <wolfssl/wolfcrypt/wc_lms.h>
#endif
#ifdef WOLFSSL_HAVE_XMSS
#include <wolfssl/wolfcrypt/wc_xmss.h>
#endif
#ifdef HAVE_FALCON
#include <wolfssl/wolfcrypt/falcon.h>
#endif
@ -1542,7 +1548,8 @@ struct SignatureCtx {
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
!defined(NO_DSA) || defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_SLHDSA)
defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_LMS) || \
defined(WOLFSSL_HAVE_XMSS)
int verify;
#endif
union {
@ -1602,6 +1609,20 @@ struct SignatureCtx {
SlhDsaKey* slhdsa;
#endif
#endif
#ifdef WOLFSSL_HAVE_LMS
#ifdef WOLFSSL_NO_MALLOC
LmsKey lms[1];
#else
LmsKey* lms;
#endif
#endif
#ifdef WOLFSSL_HAVE_XMSS
#ifdef WOLFSSL_NO_MALLOC
XmssKey xmss[1];
#else
XmssKey* xmss;
#endif
#endif
#ifndef WOLFSSL_NO_MALLOC
void* ptr;
#endif
@ -1864,13 +1885,15 @@ struct DecodedCert {
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_SLHDSA)
defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_LMS) || \
defined(WOLFSSL_HAVE_XMSS)
word32 pkCurveOID; /* Public Key's curve OID */
#ifdef WOLFSSL_CUSTOM_CURVES
int pkCurveSize; /* Public Key's curve size */
#endif
#endif /* HAVE_ECC || HAVE_ED25519 || HAVE_ED448 || HAVE_DILITHIUM ||
* HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
* HAVE_FALCON || WOLFSSL_HAVE_SLHDSA || WOLFSSL_HAVE_LMS ||
* WOLFSSL_HAVE_XMSS */
const byte* beforeDate;
int beforeDateLen;
const byte* afterDate;

View File

@ -219,7 +219,13 @@ enum Key_Sum {
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
SLH_DSA_SHAKE_256Sk = 444, /* 2.16.840.1.101.3.4.3.30 */
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
SLH_DSA_SHAKE_256Fk = 445 /* 2.16.840.1.101.3.4.3.31 */
SLH_DSA_SHAKE_256Fk = 445, /* 2.16.840.1.101.3.4.3.31 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
HSS_LMSk = 688, /* 1.2.840.113549.1.9.16.3.17 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
XMSSk = 107, /* 1.3.6.1.5.5.7.6.34 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
XMSSMTk = 108 /* 1.3.6.1.5.5.7.6.35 */
#else
/* 0x00 */
ANONk = 0x7fffffff, /* 0.0 */
@ -284,7 +290,13 @@ enum Key_Sum {
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
SLH_DSA_SHAKE_256Sk = 0x7db37ae4, /* 2.16.840.1.101.3.4.3.30 */
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
SLH_DSA_SHAKE_256Fk = 0x7db37ae5 /* 2.16.840.1.101.3.4.3.31 */
SLH_DSA_SHAKE_256Fk = 0x7db37ae5, /* 2.16.840.1.101.3.4.3.31 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
HSS_LMSk = 0x70a78832, /* 1.2.840.113549.1.9.16.3.17 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
XMSSk = 0x2707012e, /* 1.3.6.1.5.5.7.6.34 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
XMSSMTk = 0x2607012e /* 1.3.6.1.5.5.7.6.35 */
#endif
};
@ -1627,7 +1639,13 @@ enum Ctc_SigType {
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
CTC_SLH_DSA_SHAKE_256S = 444, /* 2.16.840.1.101.3.4.3.30 */
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
CTC_SLH_DSA_SHAKE_256F = 445 /* 2.16.840.1.101.3.4.3.31 */
CTC_SLH_DSA_SHAKE_256F = 445, /* 2.16.840.1.101.3.4.3.31 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
CTC_HSS_LMS = 688, /* 1.2.840.113549.1.9.16.3.17 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
CTC_XMSS = 107, /* 1.3.6.1.5.5.7.6.34 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
CTC_XMSSMT = 108 /* 1.3.6.1.5.5.7.6.35 */
#else
/* 0x2a,0x86,0x48,0xce,0x38,0x04,0x03 */
CTC_SHAwDSA = 0x314b8212, /* 1.2.840.10040.4.3 */
@ -1720,7 +1738,13 @@ enum Ctc_SigType {
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
CTC_SLH_DSA_SHAKE_256S = 0x7db37ae4, /* 2.16.840.1.101.3.4.3.30 */
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
CTC_SLH_DSA_SHAKE_256F = 0x7db37ae5 /* 2.16.840.1.101.3.4.3.31 */
CTC_SLH_DSA_SHAKE_256F = 0x7db37ae5, /* 2.16.840.1.101.3.4.3.31 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
CTC_HSS_LMS = 0x70a78832, /* 1.2.840.113549.1.9.16.3.17 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
CTC_XMSS = 0x2707012e, /* 1.3.6.1.5.5.7.6.34 */
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
CTC_XMSSMT = 0x2607012e /* 1.3.6.1.5.5.7.6.35 */
#endif
};

View File

@ -1381,6 +1381,7 @@ enum {
DYNAMIC_TYPE_SHA = 106,
DYNAMIC_TYPE_SLHDSA = 107,
DYNAMIC_TYPE_OCSP_RESPONSE = 108,
DYNAMIC_TYPE_XMSS = 109,
DYNAMIC_TYPE_SNIFFER_SERVER = 1000,
DYNAMIC_TYPE_SNIFFER_SESSION = 1001,
DYNAMIC_TYPE_SNIFFER_PB = 1002,

View File

@ -457,6 +457,8 @@ 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_ImportPubRaw_ex(XmssKey* key, const byte* in,
word32 inLen, int is_xmssmt);
WOLFSSL_API int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigSz,
const byte* msg, int msgSz);