Support RFC 9802 LMS and XMSS in X.509 certificate and CSR generation

Extend wc_MakeCert_ex/wc_SignCert_ex/wc_MakeCertReq_ex to issue HSS/LMS and
XMSS/XMSS^MT certificates and PKCS#10 requests, building on the existing
RFC 9802 verification support. New LMS_TYPE/XMSS_TYPE/XMSSMT_TYPE selectors,
wc_{Lms,Xmss}Key_PublicKeyToDer SPKI encoders, runtime signature-buffer
sizing, and sigType/key consistency checks. Generation is ASN.1-template
only, matching where the verification path lives.

Tests generate self-signed roots, CSRs and a CA->ECC-leaf chain in-process
and verify them, replacing the patched Bouncy Castle fixtures (only the stock
RFC 9802-aligned LMS interop anchor is kept).
This commit is contained in:
Tobias Frauenschläger
2026-06-02 14:21:09 +02:00
parent fdbfb66c4b
commit e05a453944
26 changed files with 945 additions and 355 deletions
-1
View File
@@ -162,7 +162,6 @@ include certs/falcon/include.am
include certs/rsapss/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.
+5 -6
View File
@@ -2,11 +2,10 @@
# All paths should be given relative to the root
#
# bc_lms_native_bc_root.der is stock Bouncy Castle output (RFC 9802-aligned
# HSS/LMS) kept as the cross-implementation interop anchor. wolfSSL's own
# LMS certificate/CSR/chain generation is exercised in-process by the
# test_rfc9802_lms_x509_gen unit test, so no other committed fixtures are
# needed here.
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.
-12
View File
@@ -1,12 +0,0 @@
# 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
+7 -5
View File
@@ -14249,8 +14249,10 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert)
#endif
}
if (dCert->signature != NULL && dCert->sigLength != 0 &&
dCert->sigLength <= MAX_ENCODED_SIG_SZ) {
/* Store a copy of the signature for later retrieval. The buffer is sized
* to the exact parsed length (itself bounded by the cert DER), so no fixed
* ceiling is applied -- a ceiling would drop large LMS/XMSS signatures. */
if (dCert->signature != NULL && dCert->sigLength != 0) {
x509->sig.buffer = (byte*)XMALLOC(
dCert->sigLength, x509->heap, DYNAMIC_TYPE_SIGNATURE);
if (x509->sig.buffer == NULL) {
@@ -14594,9 +14596,9 @@ int CopyDecodedAcertToX509(WOLFSSL_X509_ACERT* x509, DecodedAcert* dAcert)
CopyDateToASN1_TIME(dAcert->afterDate, dAcert->afterDateLen,
&x509->notAfter);
/* Copy the signature. */
if (dAcert->signature != NULL && dAcert->sigLength != 0 &&
dAcert->sigLength <= MAX_ENCODED_SIG_SZ) {
/* Copy the signature. Sized to the exact parsed length (bounded by the
* cert DER); no fixed ceiling, so large LMS/XMSS signatures are kept. */
if (dAcert->signature != NULL && dAcert->sigLength != 0) {
x509->sig.buffer = (byte*)XMALLOC(
dAcert->sigLength, x509->heap, DYNAMIC_TYPE_SIGNATURE);
if (x509->sig.buffer == NULL) {
+586 -296
View File
@@ -30,6 +30,9 @@
#include <wolfssl/ssl.h>
#include <wolfssl/wolfcrypt/asn.h>
#ifdef HAVE_ECC
#include <wolfssl/wolfcrypt/ecc.h>
#endif
#include <tests/api/api.h>
#include <tests/utils.h>
#include <tests/api/test_lms_xmss.h>
@@ -230,11 +233,14 @@ int test_wc_LmsKey_reload_cache(void)
* 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
/* Only the LMS interop-anchor verification still loads a committed fixture
* (bc_lms_native_bc_root.der); everything else is generated in-process. Gate
* these file helpers on exactly that call site to avoid an unused-function
* warning in XMSS-only or truncated-hash builds. */
#if defined(WOLFSSL_HAVE_LMS) && !defined(NO_FILESYSTEM) && \
!defined(NO_CERTS) && !defined(WOLFSSL_NO_LMS_SHA256_256)
/* Sanity bound on a test fixture cert. 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))
@@ -644,219 +650,32 @@ static int rfc9802_xmss_import_negative(void)
}
#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)
/* Collect the byte offset of the final sub-identifier of every
* 1.3.6.1.5.5.7.6.<lastByte> OID in a DER cert (XMSS ends 0x22, XMSS^MT ends
* 0x23). RFC 9802 reuses the same OID for the SubjectPublicKeyInfo algorithm,
* the TBS signatureAlgorithm and the outer signatureAlgorithm, so a conformant
* XMSS/XMSS^MT cert contains exactly three, in TBS-signature / SPKI-key /
* outer-signature order. Returns the number of occurrences found. */
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_HAVE_XMSS) && \
!defined(WOLFSSL_XMSS_VERIFY_ONLY) && defined(WOLFSSL_CERT_GEN) && \
!defined(NO_FILESYSTEM) && !defined(NO_CERTS)
static int rfc9802_collect_hbs_oid_offsets(const byte* der, word32 derSz,
byte lastByte, word32* offsets, int maxOff)
{
word32 idx = sigIndex;
word32 oidContentLen = 0;
/* OID body for 1.3.6.1.5.5.7.6: 2B 06 01 05 05 07 06, then <lastByte>. */
static const byte pfx[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x06 };
int n = 0;
word32 i;
/* 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;
for (i = 0; (word32)(i + sizeof(pfx)) < derSz; i++) {
if (XMEMCMP(der + i, pfx, sizeof(pfx)) == 0 &&
der[i + sizeof(pfx)] == lastByte) {
if (n < maxOff)
offsets[n] = i + (word32)sizeof(pfx);
n++;
}
}
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);
#if !defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 10)
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
#endif
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();
return n;
}
#endif
@@ -866,38 +685,15 @@ int test_rfc9802_lms_x509_verify(void)
#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",
#if !defined(WOLFSSL_LMS_MAX_HEIGHT) || (WOLFSSL_LMS_MAX_HEIGHT >= 10)
"./certs/lms/bc_lms_sha256_h10_w8_root.der",
#endif
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || (WOLFSSL_LMS_MAX_LEVELS >= 2)
"./certs/lms/bc_hss_L2_H5_W8_root.der",
#endif
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || (WOLFSSL_LMS_MAX_LEVELS >= 3)
"./certs/lms/bc_hss_L3_H5_W4_root.der",
#endif
"./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);
/* Cross-implementation interop gate. bc_lms_native_bc_root.der is
* generated through Bouncy Castle's stock JcaContentSignerBuilder("LMS")
* + JcaX509v3CertificateBuilder with no overrides; BC's native LMS X.509
* path is RFC 9802-compliant for HSS/LMS, so wolfSSL must accept it
* end-to-end. This is the one fixture from an independent implementation
* that we keep; wolfSSL's own generation is exercised by
* test_rfc9802_lms_x509_gen instead of committed wolfSSL fixtures. */
ExpectIntEQ(rfc9802_verify_one_cert("./certs/lms/bc_lms_native_bc_root.der",
HSS_LMSk, CTC_HSS_LMS), 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. */
@@ -910,57 +706,551 @@ int test_rfc9802_xmss_x509_verify(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_XMSS)
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
#if (!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 16))
static const char* const xmssFiles[] = {
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 10)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 10))
"./certs/xmss/bc_xmss_sha2_10_256_root.der",
#endif
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 16)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 16))
"./certs/xmss/bc_xmss_sha2_16_256_root.der",
#endif
};
#endif
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 20)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 40))
static const char* const xmssmtFiles[] = {
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 20)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 20))
"./certs/xmss/bc_xmssmt_sha2_20_2_256_root.der",
"./certs/xmss/bc_xmssmt_sha2_20_4_256_root.der",
#endif
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 40)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 40))
"./certs/xmss/bc_xmssmt_sha2_40_8_256_root.der",
#endif
};
#endif
#if (!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 16)) || \
((!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 20)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 40)))
size_t i;
#endif
#if (!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 16))
for (i = 0; i < sizeof(xmssFiles) / sizeof(xmssFiles[0]); i++) {
ExpectIntEQ(rfc9802_verify_one_cert(xmssFiles[i],
XMSSk, CTC_XMSS), TEST_SUCCESS);
}
#endif
#if (!defined(WOLFSSL_XMSS_MAX_HEIGHT) || (WOLFSSL_XMSS_MAX_HEIGHT >= 20)) && \
(!defined(WOLFSSL_XMSS_MIN_HEIGHT) || (WOLFSSL_XMSS_MIN_HEIGHT <= 40))
for (i = 0; i < sizeof(xmssmtFiles) / sizeof(xmssmtFiles[0]); i++) {
ExpectIntEQ(rfc9802_verify_one_cert(xmssmtFiles[i],
XMSSMTk, CTC_XMSSMT), TEST_SUCCESS);
}
#endif
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. */
/* No independent (RFC 9802-aligned) third-party XMSS X.509 implementation
* exists to interop against - OpenSSL has no XMSS cert signing and Bouncy
* Castle's XMSS encoding is not yet aligned with the final RFC - so there
* is no committed interop fixture here. wolfSSL's own XMSS/XMSS^MT cert
* generation, chain signing and the X.509-level signatureAlgorithm/SPKI
* mismatch rejection are exercised in test_rfc9802_xmss_x509_gen.
*
* Pure wolfCrypt-level negative tests run for any XMSS-enabled build. */
ExpectIntEQ(rfc9802_xmss_import_negative(), TEST_SUCCESS);
#endif
return EXPECT_RESULT();
}
/* RFC 9802 certificate/CSR GENERATION tests.
*
* These exercise the cert-gen path (wc_MakeCert_ex / wc_SignCert_ex and
* wc_MakeCertReq_ex) with a freshly generated LMS or XMSS key, then feed
* the result back through the existing verification path to prove the
* generated SubjectPublicKeyInfo, signatureAlgorithm and signature are
* RFC 9802-compliant and self-consistent. */
/* RFC 9802 cert/CSR generation is only wired into the ASN.1 template
* implementation (the original/non-template path has no LMS/XMSS support),
* so all of these tests require WOLFSSL_ASN_TEMPLATE. */
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CERT_GEN) && \
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
((defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \
(defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)))
/* Populate a minimal self-consistent subject/issuer name. */
static void rfc9802_gen_set_names(Cert* cert)
{
XSTRNCPY(cert->subject.country, "US", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.state, "OR", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.locality, "Portland", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.org, "wolfSSL", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.unit, "Testing", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.commonName, "RFC9802 Gen Root CA", CTC_NAME_SIZE);
}
/* Verify a self-signed DER cert by loading it as its own CA. */
static int rfc9802_gen_verify_selfsigned(const byte* der, int derSz)
{
EXPECT_DECLS;
WOLFSSL_CERT_MANAGER* cm = NULL;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL)
wolfSSL_CertManagerFree(cm);
return EXPECT_RESULT();
}
#ifdef WOLFSSL_CERT_REQ
/* Parse a generated CSR and confirm its proof-of-possession signature. */
static int rfc9802_gen_verify_csr(const byte* der, int derSz)
{
EXPECT_DECLS;
DecodedCert dc;
wc_InitDecodedCert(&dc, der, (word32)derSz, NULL);
ExpectIntEQ(wc_ParseCert(&dc, CERTREQ_TYPE, VERIFY, NULL), 0);
wc_FreeDecodedCert(&dc);
return EXPECT_RESULT();
}
#endif /* WOLFSSL_CERT_REQ */
/* Generate a self-signed root CA (and, when CSRs are enabled, a PKCS#10
* request) for an already-made key, then feed each back through the
* verification path. keyType is the wc_MakeCert_ex/wc_SignCert_ex selector
* (LMS_TYPE / XMSS_TYPE / XMSSMT_TYPE) and sigType the matching CTC_ OID.
* key is void* to mirror the public wc_MakeCert_ex API; callers must pass a
* key object whose type matches keyType. */
static int rfc9802_gen_roundtrip(void* key, int keyType, int sigType,
WC_RNG* rng, word32 derCap)
{
EXPECT_DECLS;
byte* der = NULL;
int derSz = 0;
ExpectNotNull(der = (byte*)XMALLOC(derCap, NULL, DYNAMIC_TYPE_TMP_BUFFER));
/* Self-signed root CA: generate -> sign -> verify round trip. */
if (EXPECT_SUCCESS() && der != NULL) {
Cert cert;
ExpectIntEQ(wc_InitCert(&cert), 0);
rfc9802_gen_set_names(&cert);
cert.sigType = sigType;
cert.isCA = 1;
cert.selfSigned = 1;
cert.daysValid = 365;
ExpectIntGT(wc_MakeCert_ex(&cert, der, derCap, keyType, key, rng), 0);
ExpectIntGT(derSz = wc_SignCert_ex(cert.bodySz, cert.sigType, der,
derCap, keyType, key, rng), 0);
ExpectIntEQ(rfc9802_gen_verify_selfsigned(der, derSz), TEST_SUCCESS);
}
#ifdef WOLFSSL_CERT_REQ
/* PKCS#10 CSR: generate -> self-sign proof-of-possession -> parse. */
if (EXPECT_SUCCESS() && der != NULL) {
Cert cert;
ExpectIntEQ(wc_InitCert(&cert), 0);
rfc9802_gen_set_names(&cert);
cert.sigType = sigType;
ExpectIntGT(wc_MakeCertReq_ex(&cert, der, derCap, keyType, key), 0);
ExpectIntGT(derSz = wc_SignCert_ex(cert.bodySz, cert.sigType, der,
derCap, keyType, key, rng), 0);
ExpectIntEQ(rfc9802_gen_verify_csr(der, derSz), TEST_SUCCESS);
}
#endif /* WOLFSSL_CERT_REQ */
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
/* wc_ecc_make_key is available with HAVE_ECC; HAVE_ECC_KEY_EXPORT is needed
* for the leaf SPKI and !WC_NO_RNG for key generation. */
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG)
/* Subject name for the generated leaf (distinct from the CA subject). */
static void rfc9802_gen_set_leaf_names(Cert* cert)
{
XSTRNCPY(cert->subject.country, "US", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.state, "OR", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.locality, "Portland", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.org, "wolfSSL", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.unit, "Testing", CTC_NAME_SIZE);
XSTRNCPY(cert->subject.commonName, "RFC9802 Gen Leaf", CTC_NAME_SIZE);
}
/* Generate a self-signed LMS/XMSS CA, then an ECC leaf issued and signed by
* that CA, and confirm the leaf chains to the CA (and fails without it). This
* is the real RFC 9802 use case - a hash-based CA signing another cert - that
* self-signed roots and CSRs don't cover. caKey is the already-made CA key;
* caKeyType/caSigType select its algorithm. */
static int rfc9802_gen_chain(void* caKey, int caKeyType, int caSigType,
WC_RNG* rng, word32 derCap)
{
EXPECT_DECLS;
ecc_key leafKey;
int leafKeyInit = 0;
byte* caDer = NULL;
byte* leafDer = NULL;
int caSz = 0;
int leafSz = 0;
WOLFSSL_CERT_MANAGER* cm = NULL;
ExpectNotNull(caDer = (byte*)XMALLOC(derCap, NULL, DYNAMIC_TYPE_TMP_BUFFER));
ExpectNotNull(leafDer = (byte*)XMALLOC(derCap, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectIntEQ(wc_ecc_init(&leafKey), 0);
leafKeyInit = 1;
ExpectIntEQ(wc_ecc_make_key(rng, 32, &leafKey), 0);
/* Self-signed CA root. */
if (EXPECT_SUCCESS() && caDer != NULL) {
Cert ca;
ExpectIntEQ(wc_InitCert(&ca), 0);
rfc9802_gen_set_names(&ca);
ca.sigType = caSigType;
ca.isCA = 1;
ca.selfSigned = 1;
ca.daysValid = 365;
ExpectIntGT(wc_MakeCert_ex(&ca, caDer, derCap, caKeyType, caKey, rng),
0);
ExpectIntGT(caSz = wc_SignCert_ex(ca.bodySz, caSigType, caDer, derCap,
caKeyType, caKey, rng), 0);
}
/* ECC leaf, issued by the CA's subject and signed with the CA key. */
if (EXPECT_SUCCESS() && leafDer != NULL && caSz > 0) {
Cert leaf;
ExpectIntEQ(wc_InitCert(&leaf), 0);
rfc9802_gen_set_leaf_names(&leaf);
leaf.sigType = caSigType;
leaf.daysValid = 365;
ExpectIntEQ(wc_SetIssuerBuffer(&leaf, caDer, caSz), 0);
ExpectIntGT(wc_MakeCert_ex(&leaf, leafDer, derCap, ECC_TYPE, &leafKey,
rng), 0);
ExpectIntGT(leafSz = wc_SignCert_ex(leaf.bodySz, caSigType, leafDer,
derCap, caKeyType, caKey, rng), 0);
}
/* Leaf verifies only when the CA is the trust anchor. */
if (EXPECT_SUCCESS() && leafSz > 0) {
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caDer, (long)caSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafDer, (long)leafSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafDer, (long)leafSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
}
/* Negative: corrupt the leaf's signature (last byte of the DER, in the
* signatureValue) and confirm verification fails even with the CA loaded.
* This proves the CA's hash-based signature is cryptographically checked,
* not accepted on issuer-name chaining alone. */
if (EXPECT_SUCCESS() && leafSz > 0) {
byte saved = leafDer[leafSz - 1];
leafDer[leafSz - 1] ^= 0xFF;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caDer, (long)caSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafDer, (long)leafSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
leafDer[leafSz - 1] = saved;
}
if (leafKeyInit)
wc_ecc_free(&leafKey);
XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(caDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
#endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */
#endif /* gen test support */
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_HAVE_LMS) && \
!defined(WOLFSSL_LMS_VERIFY_ONLY) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_FILESYSTEM) && \
!defined(NO_CERTS) && !defined(WOLFSSL_NO_LMS_SHA256_256)
/* Init an LMS key with the shared persistence callbacks and given params. */
static int rfc9802_gen_lms_init(LmsKey* key, int levels, int height, int win)
{
int ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID);
if (ret == 0)
ret = wc_LmsKey_SetParameters(key, levels, height, win);
if (ret == 0)
ret = wc_LmsKey_SetWriteCb(key, test_lms_write_key);
if (ret == 0)
ret = wc_LmsKey_SetReadCb(key, test_lms_read_key);
if (ret == 0)
ret = wc_LmsKey_SetContext(key, (void*)LMS_TEST_PRIV_KEY_FILE);
return ret;
}
#endif
int test_rfc9802_lms_x509_gen(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_HAVE_LMS) && \
!defined(WOLFSSL_LMS_VERIFY_ONLY) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_FILESYSTEM) && \
!defined(NO_CERTS) && !defined(WOLFSSL_NO_LMS_SHA256_256)
LmsKey key;
WC_RNG rng;
ExpectIntEQ(wc_InitRng(&rng), 0);
/* Single-level LMS (L1-H5-W8). */
remove(LMS_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_lms_init(&key, 1, 5, 8), 0);
ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, LMS_TYPE, CTC_HSS_LMS, &rng, 8192),
TEST_SUCCESS);
/* Negative: signing an LMS key with a non-LMS signature OID must be
* rejected rather than emit a cert whose signatureAlgorithm contradicts
* its public key. The check fires before any signature is produced, so
* the key's one-time signatures are not consumed. */
if (EXPECT_SUCCESS()) {
Cert cert;
byte* tmp = NULL;
ExpectNotNull(tmp = (byte*)XMALLOC(8192, NULL, DYNAMIC_TYPE_TMP_BUFFER));
ExpectIntEQ(wc_InitCert(&cert), 0);
rfc9802_gen_set_names(&cert);
cert.sigType = CTC_HSS_LMS;
cert.isCA = 1;
cert.selfSigned = 1;
cert.daysValid = 365;
if (tmp != NULL) {
ExpectIntGT(wc_MakeCert_ex(&cert, tmp, 8192, LMS_TYPE, &key,
&rng), 0);
ExpectIntEQ(wc_SignCert_ex(cert.bodySz, CTC_XMSS, tmp, 8192,
LMS_TYPE, &key, &rng), WC_NO_ERR_TRACE(ALGO_ID_E));
}
XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG)
/* Real CA use case: the LMS CA signs an ECC leaf; the leaf must chain to
* the CA. Reuses the L1 key (plenty of one-time signatures remain). */
ExpectIntEQ(rfc9802_gen_chain(&key, LMS_TYPE, CTC_HSS_LMS, &rng, 8192),
TEST_SUCCESS);
#endif
wc_LmsKey_Free(&key);
remove(LMS_TEST_PRIV_KEY_FILE);
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || (WOLFSSL_LMS_MAX_LEVELS >= 2)
/* Multi-level HSS (L2-H5-W8): the signature embeds a lower-level LMS
* public key + signature, exercising the larger, multi-level encoding. */
remove(LMS_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_lms_init(&key, 2, 5, 8), 0);
ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, LMS_TYPE, CTC_HSS_LMS, &rng, 8192),
TEST_SUCCESS);
wc_LmsKey_Free(&key);
remove(LMS_TEST_PRIV_KEY_FILE);
#endif
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || (WOLFSSL_LMS_MAX_LEVELS >= 3)
/* Three-level HSS with Winternitz 4 (L3-H5-W4): exercises the deepest
* multi-level encoding and a different Winternitz parameter than the
* W8 cases above. */
remove(LMS_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_lms_init(&key, 3, 5, 4), 0);
ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, LMS_TYPE, CTC_HSS_LMS, &rng, 8192),
TEST_SUCCESS);
wc_LmsKey_Free(&key);
remove(LMS_TEST_PRIV_KEY_FILE);
#endif
wc_FreeRng(&rng);
#endif
return EXPECT_RESULT();
}
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_HAVE_XMSS) && \
!defined(WOLFSSL_XMSS_VERIFY_ONLY) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
#define XMSS_GEN_TEST_PRIV_KEY_FILE "/tmp/wolfssl_test_xmss_gen.key"
static enum wc_XmssRc xmss_gen_write_key(const byte* priv, word32 privSz,
void* context)
{
XFILE f = XFOPEN((const char*)context, "wb");
enum wc_XmssRc ret = WC_XMSS_RC_SAVED_TO_NV_MEMORY;
if (f == XBADFILE)
return WC_XMSS_RC_WRITE_FAIL;
if (XFWRITE(priv, 1, privSz, f) != privSz)
ret = WC_XMSS_RC_WRITE_FAIL;
XFCLOSE(f);
return ret;
}
static enum wc_XmssRc xmss_gen_read_key(byte* priv, word32 privSz,
void* context)
{
XFILE f = XFOPEN((const char*)context, "rb");
enum wc_XmssRc ret = WC_XMSS_RC_READ_TO_MEMORY;
if (f == XBADFILE)
return WC_XMSS_RC_READ_FAIL;
if (XFREAD(priv, 1, privSz, f) != privSz)
ret = WC_XMSS_RC_READ_FAIL;
XFCLOSE(f);
return ret;
}
/* Init an XMSS/XMSS^MT key with the shared persistence callbacks. */
static int rfc9802_gen_xmss_init(XmssKey* key, const char* paramStr)
{
int ret = wc_XmssKey_Init(key, NULL, INVALID_DEVID);
if (ret == 0)
ret = wc_XmssKey_SetParamStr(key, paramStr);
if (ret == 0)
ret = wc_XmssKey_SetWriteCb(key, xmss_gen_write_key);
if (ret == 0)
ret = wc_XmssKey_SetReadCb(key, xmss_gen_read_key);
if (ret == 0)
ret = wc_XmssKey_SetContext(key, (void*)XMSS_GEN_TEST_PRIV_KEY_FILE);
return ret;
}
/* X.509-level negative tests on a wolfSSL-generated XMSS/XMSS^MT cert, run
* against the already-made key (no extra keygen). oidLast is the cert's true
* final OID byte (XMSS 0x22, XMSS^MT 0x23) and oidSwap the other family's:
*
* (a) flip only the outer signatureAlgorithm OID -> it no longer equals the
* TBS signatureAlgorithm, which the generic X.509 algId-consistency check
* rejects (ASN_SIG_OID_E at parse);
* (b) flip both signatureAlgorithm copies (TBS + outer) but leave the SPKI
* key OID -> outer == TBS (that check passes), yet the signature
* algorithm now disagrees with the public-key algorithm, which RFC 9802
* requires verification to reject (SigOidMatchesKeyOid, before the - now
* also invalid - signature is even checked).
*
* Either way verification must fail. */
static int rfc9802_gen_xmss_oid_tamper(void* key, int keyType, int sigType,
WC_RNG* rng, byte oidLast, byte oidSwap)
{
EXPECT_DECLS;
byte* der = NULL;
int derSz = 0;
word32 off[8];
int n = 0;
WOLFSSL_CERT_MANAGER* cm = NULL;
ExpectNotNull(der = (byte*)XMALLOC(16384, NULL, DYNAMIC_TYPE_TMP_BUFFER));
if (EXPECT_SUCCESS() && der != NULL) {
Cert cert;
ExpectIntEQ(wc_InitCert(&cert), 0);
rfc9802_gen_set_names(&cert);
cert.sigType = sigType;
cert.isCA = 1;
cert.selfSigned = 1;
cert.daysValid = 365;
ExpectIntGT(wc_MakeCert_ex(&cert, der, 16384, keyType, key, rng), 0);
ExpectIntGT(derSz = wc_SignCert_ex(cert.bodySz, sigType, der, 16384,
keyType, key, rng), 0);
}
if (EXPECT_SUCCESS() && derSz > 0) {
n = rfc9802_collect_hbs_oid_offsets(der, (word32)derSz, oidLast, off, 8);
/* TBS-signature, SPKI-key, outer-signature - in that order. */
ExpectIntEQ(n, 3);
}
/* (a) Outer signatureAlgorithm != TBS signatureAlgorithm. */
if (EXPECT_SUCCESS() && n == 3) {
der[off[2]] = oidSwap;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
(void)wolfSSL_CertManagerLoadCABuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
der[off[2]] = oidLast; /* restore */
}
/* (b) signatureAlgorithm (both copies) disagrees with the SPKI key OID. */
if (EXPECT_SUCCESS() && n == 3) {
der[off[0]] = oidSwap;
der[off[2]] = oidSwap;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
(void)wolfSSL_CertManagerLoadCABuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1);
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, der, (long)derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
}
}
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return EXPECT_RESULT();
}
#endif /* XMSS gen support */
int test_rfc9802_xmss_x509_gen(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_HAVE_XMSS) && \
!defined(WOLFSSL_XMSS_VERIFY_ONLY) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
XmssKey key;
WC_RNG rng;
ExpectIntEQ(wc_InitRng(&rng), 0);
/* Single-tree XMSS. */
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_xmss_init(&key, "XMSS-SHA2_10_256"), 0);
ExpectIntEQ(wc_XmssKey_MakeKey(&key, &rng), 0);
ExpectIntEQ((int)key.is_xmssmt, 0);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, XMSS_TYPE, CTC_XMSS, &rng, 16384),
TEST_SUCCESS);
/* Negative: the XMSSMT_TYPE selector must not be accepted for a
* single-tree XMSS key, and signing a single-tree key as XMSS^MT must be
* rejected. Both checks fire before signing, so no signature is used. */
if (EXPECT_SUCCESS()) {
Cert cert;
byte* tmp = NULL;
ExpectNotNull(tmp = (byte*)XMALLOC(16384, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectIntEQ(wc_InitCert(&cert), 0);
rfc9802_gen_set_names(&cert);
cert.sigType = CTC_XMSS;
cert.isCA = 1;
cert.selfSigned = 1;
cert.daysValid = 365;
/* Wrong selector for the key's tree variant. */
if (tmp != NULL) {
ExpectIntEQ(wc_MakeCert_ex(&cert, tmp, 16384, XMSSMT_TYPE, &key,
&rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* Correct selector, but signed with the XMSS^MT OID. */
ExpectIntGT(wc_MakeCert_ex(&cert, tmp, 16384, XMSS_TYPE, &key,
&rng), 0);
ExpectIntEQ(wc_SignCert_ex(cert.bodySz, CTC_XMSSMT, tmp, 16384,
XMSS_TYPE, &key, &rng), WC_NO_ERR_TRACE(ALGO_ID_E));
}
XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG)
/* Real CA use case: the XMSS CA signs an ECC leaf; the leaf must chain. */
ExpectIntEQ(rfc9802_gen_chain(&key, XMSS_TYPE, CTC_XMSS, &rng, 16384),
TEST_SUCCESS);
#endif
/* X.509-level signatureAlgorithm/SPKI OID consistency, reusing this key. */
ExpectIntEQ(rfc9802_gen_xmss_oid_tamper(&key, XMSS_TYPE, CTC_XMSS, &rng,
/* XMSS */ 0x22, /* swap */ 0x23), TEST_SUCCESS);
wc_XmssKey_Free(&key);
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
/* Multi-tree XMSS^MT: exercises the XMSSMT_TYPE selector, the
* XMSSMTk public-key OID branch and the CTC_XMSSMT signature OID. */
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_xmss_init(&key, "XMSSMT-SHA2_20/2_256"), 0);
ExpectIntEQ(wc_XmssKey_MakeKey(&key, &rng), 0);
ExpectIntEQ((int)key.is_xmssmt, 1);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, XMSSMT_TYPE, CTC_XMSSMT, &rng,
16384), TEST_SUCCESS);
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG)
ExpectIntEQ(rfc9802_gen_chain(&key, XMSSMT_TYPE, CTC_XMSSMT, &rng, 16384),
TEST_SUCCESS);
#endif
ExpectIntEQ(rfc9802_gen_xmss_oid_tamper(&key, XMSSMT_TYPE, CTC_XMSSMT, &rng,
/* XMSS^MT */ 0x23, /* swap */ 0x22), TEST_SUCCESS);
wc_XmssKey_Free(&key);
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
/* A second XMSS^MT parameter set (different embedded param-set OID and a
* larger signature) to keep the encoder/auto-derive decoder exercised
* across sizes now that the committed multi-size fixtures are gone. */
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
ExpectIntEQ(rfc9802_gen_xmss_init(&key, "XMSSMT-SHA2_20/4_256"), 0);
ExpectIntEQ(wc_XmssKey_MakeKey(&key, &rng), 0);
ExpectIntEQ((int)key.is_xmssmt, 1);
ExpectIntEQ(rfc9802_gen_roundtrip(&key, XMSSMT_TYPE, CTC_XMSSMT, &rng,
16384), TEST_SUCCESS);
wc_XmssKey_Free(&key);
remove(XMSS_GEN_TEST_PRIV_KEY_FILE);
wc_FreeRng(&rng);
#endif
return EXPECT_RESULT();
}
+5 -1
View File
@@ -28,12 +28,16 @@ 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);
int test_rfc9802_lms_x509_gen(void);
int test_rfc9802_xmss_x509_gen(void);
/* LMS, and RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509). */
#define TEST_LMS_XMSS_DECLS \
TEST_DECL_GROUP("lms", test_wc_LmsKey_sign_verify), \
TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_cache), \
TEST_DECL_GROUP("lms", test_rfc9802_lms_x509_verify), \
TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_verify)
TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_verify), \
TEST_DECL_GROUP("lms", test_rfc9802_lms_x509_gen), \
TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_gen)
#endif /* WOLFCRYPT_TEST_LMS_XMSS_H */
+280 -24
View File
@@ -4418,9 +4418,9 @@ static int EncodeName(EncodedName* name, const char* nameStr, byte nameTag, byte
#endif
#ifdef WOLFSSL_CERT_GEN
static int SetValidity(byte* output, int daysValid);
static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng, DsaKey* dsaKey, ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey);
static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng, DsaKey* dsaKey, ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey);
#ifdef WOLFSSL_CERT_REQ
static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey, DsaKey* dsaKey, ecc_key* eccKey, ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey);
static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey, DsaKey* dsaKey, ecc_key* eccKey, ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey, wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey);
#endif
#endif
#endif
@@ -13065,6 +13065,82 @@ int wc_Ed448PublicKeyToDer(const ed448_key* key, byte* output, word32 inLen,
return ret;
}
#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
/* Encode the public part of an LMS/HSS key in DER.
*
* Per RFC 9802, the SubjectPublicKeyInfo for HSS/LMS uses the
* id-alg-hss-lms-hashsig OID and carries the raw HSS public key in the
* BIT STRING with no additional wrapping.
*
* Pass NULL for output to get the size of the encoding.
*
* @param [in] key LMS key object.
* @param [out] output Buffer to put encoded data in.
* @param [in] inLen Size of buffer in bytes.
* @param [in] withAlg Whether to use SubjectPublicKeyInfo format.
* @return Size of encoded data in bytes on success.
* @return BAD_FUNC_ARG when key is NULL.
*/
int wc_LmsKey_PublicKeyToDer(const LmsKey* key, byte* output, word32 inLen,
int withAlg)
{
int ret;
byte pubKey[HSS_MAX_PUBLIC_KEY_LEN];
word32 pubKeyLen = (word32)sizeof(pubKey);
if (key == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_LmsKey_ExportPubRaw(key, pubKey, &pubKeyLen);
if (ret == 0) {
ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen,
HSS_LMSk, withAlg);
}
return ret;
}
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
/* Encode the public part of an XMSS/XMSS^MT key in DER.
*
* Per RFC 9802, the SubjectPublicKeyInfo for XMSS/XMSS^MT uses the
* id-alg-xmss-hashsig / id-alg-xmssmt-hashsig OID and carries the raw
* public key in the BIT STRING with no additional wrapping.
*
* Pass NULL for output to get the size of the encoding.
*
* @param [in] key XMSS key object.
* @param [out] output Buffer to put encoded data in.
* @param [in] inLen Size of buffer in bytes.
* @param [in] withAlg Whether to use SubjectPublicKeyInfo format.
* @return Size of encoded data in bytes on success.
* @return BAD_FUNC_ARG when key is NULL.
*/
int wc_XmssKey_PublicKeyToDer(const XmssKey* key, byte* output, word32 inLen,
int withAlg)
{
int ret;
byte pubKey[2 * WC_XMSS_MAX_N + XMSS_OID_LEN];
word32 pubKeyLen = (word32)sizeof(pubKey);
int keyType;
if (key == NULL) {
return BAD_FUNC_ARG;
}
keyType = key->is_xmssmt ? XMSSMTk : XMSSk;
ret = wc_XmssKey_ExportPubRaw(key, pubKey, &pubKeyLen);
if (ret == 0) {
ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen,
keyType, withAlg);
}
return ret;
}
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
#if !defined(NO_RSA) && !defined(NO_CERTS)
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for header before RSA key in certificate. */
@@ -27393,7 +27469,8 @@ static int EncodePublicKey(int keyType, byte* output, int outLen,
RsaKey* rsaKey, ecc_key* eccKey,
ed25519_key* ed25519Key, ed448_key* ed448Key,
DsaKey* dsaKey, falcon_key* falconKey,
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey)
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
LmsKey* lmsKey, XmssKey* xmssKey)
{
int ret = 0;
@@ -27406,6 +27483,8 @@ static int EncodePublicKey(int keyType, byte* output, int outLen,
(void)falconKey;
(void)mldsaKey;
(void)slhDsaKey;
(void)lmsKey;
(void)xmssKey;
switch (keyType) {
#ifndef NO_RSA
@@ -27489,6 +27568,23 @@ static int EncodePublicKey(int keyType, byte* output, int outLen,
}
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
case LMS_KEY:
ret = wc_LmsKey_PublicKeyToDer(lmsKey, output, (word32)outLen, 1);
if (ret <= 0) {
ret = PUBLIC_KEY_E;
}
break;
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
case XMSS_KEY:
case XMSSMT_KEY:
ret = wc_XmssKey_PublicKeyToDer(xmssKey, output, (word32)outLen, 1);
if (ret <= 0) {
ret = PUBLIC_KEY_E;
}
break;
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
default:
ret = PUBLIC_KEY_E;
break;
@@ -28291,8 +28387,8 @@ static int InternalSignCb(const byte* in, word32 inLen,
static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
byte* sig, word32 sigSz, RsaKey* rsaKey, ecc_key* eccKey,
ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey,
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, WC_RNG* rng,
word32 sigAlgoType, void* heap)
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey,
XmssKey* xmssKey, WC_RNG* rng, word32 sigAlgoType, void* heap)
{
int ret = 0;
@@ -28305,6 +28401,8 @@ static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
(void)falconKey;
(void)mldsaKey;
(void)slhDsaKey;
(void)lmsKey;
(void)xmssKey;
(void)rng;
(void)heap;
@@ -28398,6 +28496,26 @@ static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
}
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
if (lmsKey) {
word32 outSz = sigSz;
/* RFC 9802: the TBS is signed directly with no pre-hash. */
ret = wc_LmsKey_Sign(lmsKey, sig, &outSz, buf, (int)sz);
if (ret == 0)
ret = (int)outSz;
}
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
if (xmssKey) {
word32 outSz = sigSz;
/* RFC 9802: the TBS is signed directly with no pre-hash. */
ret = wc_XmssKey_Sign(xmssKey, sig, &outSz, buf, (int)sz);
if (ret == 0)
ret = (int)outSz;
}
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
if (ret == -1)
ret = ALGO_ID_E;
@@ -28545,7 +28663,8 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng,
DsaKey* dsaKey, ed25519_key* ed25519Key,
ed448_key* ed448Key, falcon_key* falconKey,
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey)
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
LmsKey* lmsKey, XmssKey* xmssKey)
{
/* TODO: issRaw and sbjRaw should be NUL terminated. */
DECL_ASNSETDATA(dataASN, x509CertASN_Length);
@@ -28562,6 +28681,8 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
(void)falconKey;
(void)mldsaKey;
(void)slhDsaKey;
(void)lmsKey;
(void)xmssKey;
CALLOC_ASNSETDATA(dataASN, x509CertASN_Length, ret, cert->heap);
@@ -28629,6 +28750,16 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
}
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (lmsKey != NULL) {
cert->keyType = LMS_KEY;
}
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
else if (xmssKey != NULL) {
cert->keyType = xmssKey->is_xmssmt ? XMSSMT_KEY : XMSS_KEY;
}
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
else {
ret = BAD_FUNC_ARG;
}
@@ -28677,7 +28808,7 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
/* Calculate public key encoding size. */
ret = EncodePublicKey(cert->keyType, NULL, 0, rsaKey,
eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
mldsaKey, slhDsaKey);
mldsaKey, slhDsaKey, lmsKey, xmssKey);
publicKeySz = (word32)ret;
}
if (ret >= 0) {
@@ -28864,7 +28995,7 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
(int)dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ]
.data.buffer.length,
rsaKey, eccKey, ed25519Key, ed448Key, dsaKey,
falconKey, mldsaKey, slhDsaKey);
falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey);
}
if ((ret >= 0) && (!dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].noOut)) {
/* Encode extensions into buffer. */
@@ -28909,6 +29040,8 @@ int wc_MakeCert_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
falcon_key* falconKey = NULL;
wc_MlDsaKey* mldsaKey = NULL;
SlhDsaKey* slhDsaKey = NULL;
LmsKey* lmsKey = NULL;
XmssKey* xmssKey = NULL;
if (keyType == RSA_TYPE)
rsaKey = (RsaKey*)key;
@@ -28942,10 +29075,28 @@ int wc_MakeCert_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
else if (IsSlhDsaKeyType(keyType))
slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (keyType == LMS_TYPE)
lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
/* The selector must match the key's actual tree variant so XMSS_TYPE and
* XMSSMT_TYPE are not silently interchangeable. */
else if (keyType == XMSS_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
else if (keyType == XMSSMT_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && !xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
#endif
return MakeAnyCert(cert, derBuffer, derSz, rsaKey, eccKey, rng, dsaKey,
ed25519Key, ed448Key, falconKey, mldsaKey,
slhDsaKey);
slhDsaKey, lmsKey, xmssKey);
}
/* Make an x509 Certificate v3 RSA or ECC from cert input, write to buffer */
@@ -28954,7 +29105,7 @@ int wc_MakeCert(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey,
ecc_key* eccKey, WC_RNG* rng)
{
return MakeAnyCert(cert, derBuffer, derSz, rsaKey, eccKey, rng, NULL, NULL,
NULL, NULL, NULL, NULL);
NULL, NULL, NULL, NULL, NULL, NULL);
}
@@ -29022,7 +29173,7 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
RsaKey* rsaKey, DsaKey* dsaKey, ecc_key* eccKey,
ed25519_key* ed25519Key, ed448_key* ed448Key,
falcon_key* falconKey, wc_MlDsaKey* mldsaKey,
SlhDsaKey* slhDsaKey)
SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey)
{
DECL_ASNSETDATA(dataASN, certReqBodyASN_Length);
word32 publicKeySz = 0;
@@ -29038,6 +29189,8 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
(void)falconKey;
(void)mldsaKey;
(void)slhDsaKey;
(void)lmsKey;
(void)xmssKey;
CALLOC_ASNSETDATA(dataASN, certReqBodyASN_Length, ret, cert->heap);
@@ -29105,6 +29258,16 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
}
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (lmsKey != NULL) {
cert->keyType = LMS_KEY;
}
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
else if (xmssKey != NULL) {
cert->keyType = xmssKey->is_xmssmt ? XMSSMT_KEY : XMSS_KEY;
}
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
else {
ret = BAD_FUNC_ARG;
}
@@ -29127,7 +29290,7 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
/* Determine encode public key size. */
ret = EncodePublicKey(cert->keyType, NULL, 0, rsaKey,
eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
mldsaKey, slhDsaKey);
mldsaKey, slhDsaKey, lmsKey, xmssKey);
publicKeySz = (word32)ret;
}
if (ret >= 0) {
@@ -29247,7 +29410,7 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
dataASN[CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ].data.buffer.data,
(int)dataASN[CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ].data.buffer.length,
rsaKey, eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
mldsaKey, slhDsaKey);
mldsaKey, slhDsaKey, lmsKey, xmssKey);
}
if ((ret >= 0 && derBuffer != NULL) &&
(!dataASN[CERTREQBODYASN_IDX_EXT_BODY].noOut)) {
@@ -29281,6 +29444,8 @@ int wc_MakeCertReq_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
falcon_key* falconKey = NULL;
wc_MlDsaKey* mldsaKey = NULL;
SlhDsaKey* slhDsaKey = NULL;
LmsKey* lmsKey = NULL;
XmssKey* xmssKey = NULL;
if (keyType == RSA_TYPE)
rsaKey = (RsaKey*)key;
@@ -29314,10 +29479,28 @@ int wc_MakeCertReq_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
else if (IsSlhDsaKeyType(keyType))
slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (keyType == LMS_TYPE)
lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
/* The selector must match the key's actual tree variant so XMSS_TYPE and
* XMSSMT_TYPE are not silently interchangeable. */
else if (keyType == XMSS_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
else if (keyType == XMSSMT_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && !xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
#endif
return MakeCertReq(cert, derBuffer, derSz, rsaKey, dsaKey, eccKey,
ed25519Key, ed448Key, falconKey, mldsaKey,
slhDsaKey);
slhDsaKey, lmsKey, xmssKey);
}
WOLFSSL_ABI
@@ -29325,7 +29508,7 @@ int wc_MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
RsaKey* rsaKey, ecc_key* eccKey)
{
return MakeCertReq(cert, derBuffer, derSz, rsaKey, NULL, eccKey, NULL,
NULL, NULL, NULL, NULL);
NULL, NULL, NULL, NULL, NULL, NULL);
}
#endif /* WOLFSSL_CERT_REQ */
@@ -29472,13 +29655,20 @@ static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
RsaKey* rsaKey, ecc_key* eccKey, ed25519_key* ed25519Key,
ed448_key* ed448Key, falcon_key* falconKey,
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
WC_RNG* rng)
LmsKey* lmsKey, XmssKey* xmssKey, WC_RNG* rng)
{
int sigSz = 0;
void* heap = NULL;
/* The signature buffer must hold the largest signature any supported key
* type can produce. LMS/XMSS signatures are parameter-dependent and can
* exceed MAX_ENCODED_SIG_SZ, so size them from the key at runtime. */
word32 maxSigSz = MAX_ENCODED_SIG_SZ;
CertSignCtx certSignCtx_lcl;
CertSignCtx* certSignCtx = &certSignCtx_lcl;
(void)lmsKey;
(void)xmssKey;
XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));
if (requestSz < 0)
@@ -29505,19 +29695,64 @@ static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
return NOT_COMPILED_IN;
#endif /* HAVE_ECC */
}
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (lmsKey) {
word32 lmsSigSz = 0;
/* The signature algorithm OID is written from sType. Reject a
* mismatch so we never emit a cert whose signatureAlgorithm
* contradicts its HSS/LMS public key. */
if (sType != CTC_HSS_LMS) {
WOLFSSL_MSG("LMS key requires CTC_HSS_LMS signature type");
return ALGO_ID_E;
}
heap = lmsKey->heap;
if (wc_LmsKey_GetSigLen(lmsKey, &lmsSigSz) != 0)
return BAD_FUNC_ARG;
if (lmsSigSz > maxSigSz)
maxSigSz = lmsSigSz;
}
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
else if (xmssKey) {
word32 xmssSigSz = 0;
/* sType must match the tree variant (XMSS vs XMSS^MT) so the
* signatureAlgorithm OID agrees with the XMSS public key OID that
* MakeAnyCert derived from key->is_xmssmt. */
if (xmssKey->is_xmssmt ? (sType != CTC_XMSSMT)
: (sType != CTC_XMSS)) {
WOLFSSL_MSG("XMSS signature type does not match key variant");
return ALGO_ID_E;
}
heap = xmssKey->heap;
if (wc_XmssKey_GetSigLen(xmssKey, &xmssSigSz) != 0)
return BAD_FUNC_ARG;
if (xmssSigSz > maxSigSz)
maxSigSz = xmssSigSz;
}
#endif
#ifndef WOLFSSL_NO_MALLOC
if (certSignCtx->sig == NULL) {
certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap,
certSignCtx->sig = (byte*)XMALLOC(maxSigSz, heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (certSignCtx->sig == NULL)
return MEMORY_E;
}
#else
/* Without dynamic memory the signature buffer is a fixed
* MAX_ENCODED_SIG_SZ array in CertSignCtx. LMS/XMSS signatures are
* parameter-dependent and can be larger, so reject rather than overflow
* the fixed buffer. */
if (maxSigSz > MAX_ENCODED_SIG_SZ) {
WOLFSSL_MSG("LMS/XMSS signature larger than fixed CertSignCtx buffer");
return BUFFER_E;
}
#endif
sigSz = MakeSignature(certSignCtx, buf, (word32)requestSz, certSignCtx->sig,
MAX_ENCODED_SIG_SZ, rsaKey, eccKey, ed25519Key, ed448Key,
falconKey, mldsaKey, slhDsaKey, rng, (word32)sType, heap);
maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key,
falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey, rng, (word32)sType,
heap);
#ifdef WOLFSSL_ASYNC_CRYPT
if (sigSz == WC_NO_ERR_TRACE(WC_PENDING_E)) {
/* Not free'ing certSignCtx->sig here because it could still be in use
@@ -29663,7 +29898,7 @@ int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf,
ret = MakeSignature(certSignCtx, buf, (word32)bufSz, certSignCtx->sig,
MAX_ENCODED_SIG_SZ, rsaKey, eccKey, ed25519Key, ed448Key,
falconKey, mldsaKey, slhDsaKey, rng, (word32)sType, heap);
falconKey, mldsaKey, slhDsaKey, NULL, NULL, rng, (word32)sType, heap);
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
/* Not free'ing certSignCtx->sig here because it could still be in use
@@ -29723,6 +29958,8 @@ int wc_SignCert_ex(int requestSz, int sType, byte* buf, word32 buffSz,
falcon_key* falconKey = NULL;
wc_MlDsaKey* mldsaKey = NULL;
SlhDsaKey* slhDsaKey = NULL;
LmsKey* lmsKey = NULL;
XmssKey* xmssKey = NULL;
if (keyType == RSA_TYPE)
rsaKey = (RsaKey*)key;
@@ -29754,16 +29991,35 @@ int wc_SignCert_ex(int requestSz, int sType, byte* buf, word32 buffSz,
else if (IsSlhDsaKeyType(keyType))
slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
else if (keyType == LMS_TYPE)
lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
/* The selector must match the key's actual tree variant so XMSS_TYPE and
* XMSSMT_TYPE are not silently interchangeable. */
else if (keyType == XMSS_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
else if (keyType == XMSSMT_TYPE) {
xmssKey = (XmssKey*)key;
if (xmssKey != NULL && !xmssKey->is_xmssmt)
return BAD_FUNC_ARG;
}
#endif
return SignCert(requestSz, sType, buf, buffSz, rsaKey, eccKey, ed25519Key,
ed448Key, falconKey, mldsaKey, slhDsaKey, rng);
ed448Key, falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey,
rng);
}
int wc_SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
return SignCert(requestSz, sType, buf, buffSz, rsaKey, eccKey, NULL, NULL,
NULL, NULL, NULL, rng);
NULL, NULL, NULL, NULL, NULL, rng);
}
/* Sign certificate/CSR using a callback function
@@ -34179,7 +34435,7 @@ WC_MAYBE_UNUSED static int EncodeBasicOcspResponse(OcspResponse* resp,
XMEMSET(&certSignCtx, 0, sizeof(CertSignCtx));
ret = MakeSignature(&certSignCtx, respData, respDataSz,
sigData, sigSz, rsaKey, eccKey, NULL, NULL, NULL, NULL,
NULL, rng, resp->sigOID, resp->heap);
NULL, NULL, NULL, rng, resp->sigOID, resp->heap);
if (ret > 0) {
sigSz = (word32)ret;
ret = 0;
@@ -36397,7 +36653,7 @@ int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType,
/* Create signature */
sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig,
MAX_ENCODED_SIG_SZ, rsaKey, eccKey, NULL, NULL, NULL,
NULL, NULL, rng, (word32)sType, heap);
NULL, NULL, NULL, NULL, rng, (word32)sType, heap);
if (sigSz < 0) {
#ifndef WOLFSSL_NO_MALLOC
XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
+22 -2
View File
@@ -6594,11 +6594,23 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng,
DsaKey* dsaKey, ed25519_key* ed25519Key,
ed448_key* ed448Key, falcon_key* falconKey,
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey)
wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
LmsKey* lmsKey, XmssKey* xmssKey)
{
int ret;
WC_DECLARE_VAR(der, DerCert, 1, 0);
/* RFC 9802 LMS/XMSS support (both verification and generation) lives only
* in the ASN.1 template encoder; this original/non-template path has no
* LMS/XMSS code at all. Rather than duplicate the encoding here for a
* legacy path that could not verify such certs anyway, generation is
* template-only and rejected here with a clear diagnostic. */
if ((lmsKey != NULL) || (xmssKey != NULL)) {
WOLFSSL_MSG("LMS/XMSS certificate generation requires "
"WOLFSSL_ASN_TEMPLATE");
return ALGO_ID_E;
}
if (derBuffer == NULL)
return BAD_FUNC_ARG;
@@ -7227,11 +7239,19 @@ static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
RsaKey* rsaKey, DsaKey* dsaKey, ecc_key* eccKey,
ed25519_key* ed25519Key, ed448_key* ed448Key,
falcon_key* falconKey, wc_MlDsaKey* mldsaKey,
SlhDsaKey* slhDsaKey)
SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey)
{
int ret;
WC_DECLARE_VAR(der, DerCert, 1, 0);
/* LMS/XMSS certificate request generation is only supported with
* WOLFSSL_ASN_TEMPLATE. */
if ((lmsKey != NULL) || (xmssKey != NULL)) {
WOLFSSL_MSG("LMS/XMSS certificate request generation requires "
"WOLFSSL_ASN_TEMPLATE");
return ALGO_ID_E;
}
if (eccKey)
cert->keyType = ECC_KEY;
else if (rsaKey)
+4 -1
View File
@@ -2773,7 +2773,10 @@ enum cert_enums {
SLH_DSA_SHAKE_192S_KEY = 32,
SLH_DSA_SHAKE_192F_KEY = 33,
SLH_DSA_SHAKE_256S_KEY = 34,
SLH_DSA_SHAKE_256F_KEY = 35
SLH_DSA_SHAKE_256F_KEY = 35,
LMS_KEY = 36,
XMSS_KEY = 37,
XMSSMT_KEY = 38
};
#ifndef WOLFSSL_NO_DILITHIUM_LEGACY_NAMES
+12 -1
View File
@@ -99,6 +99,14 @@ This library defines the interface APIs for X509 certificates.
typedef struct SlhDsaKey SlhDsaKey;
#define WC_SLHDSAKEY_TYPE_DEFINED
#endif
#ifndef WC_LMSKEY_TYPE_DEFINED
typedef struct LmsKey LmsKey;
#define WC_LMSKEY_TYPE_DEFINED
#endif
#ifndef WC_XMSSKEY_TYPE_DEFINED
typedef struct XmssKey XmssKey;
#define WC_XMSSKEY_TYPE_DEFINED
#endif
enum EncPkcs8Types {
ENC_PKCS8_VER_PKCS12 = 1,
@@ -172,7 +180,10 @@ enum CertType {
ECC_PARAM_TYPE,
CHAIN_CERT_TYPE,
PKCS7_TYPE,
TRUSTED_CERT_TYPE
TRUSTED_CERT_TYPE,
LMS_TYPE,
XMSS_TYPE,
XMSSMT_TYPE
};
#ifndef WOLFSSL_NO_DILITHIUM_LEGACY_NAMES
+6 -2
View File
@@ -3379,7 +3379,9 @@
(defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)) || \
(defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT)) || \
defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || \
defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_LIBOQS))
defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_LIBOQS) || \
(defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \
(defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)))
#define WC_ENABLE_ASYM_KEY_EXPORT
#endif
@@ -3389,7 +3391,9 @@
(defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) || \
(defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT)) || \
defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || \
defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_LIBOQS))
defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_LIBOQS) || \
(defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \
(defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)))
#define WC_ENABLE_ASYM_KEY_IMPORT
#endif
+9 -2
View File
@@ -749,7 +749,7 @@ typedef struct HssPrivKey {
#define LMS_MAX_LABEL_LEN 32
#endif
typedef struct LmsKey {
struct LmsKey {
/* Public key. */
ALIGN16 byte pub[HSS_PUBLIC_KEY_LEN(LMS_MAX_NODE_LEN)];
#ifndef WOLFSSL_LMS_VERIFY_ONLY
@@ -788,7 +788,12 @@ typedef struct LmsKey {
char label[LMS_MAX_LABEL_LEN];
int labelLen;
#endif
} LmsKey;
};
#ifndef WC_LMSKEY_TYPE_DEFINED
typedef struct LmsKey LmsKey;
#define WC_LMSKEY_TYPE_DEFINED
#endif
#ifdef __cplusplus
extern "C" {
@@ -822,6 +827,8 @@ WOLFSSL_API int wc_LmsKey_GetPrivLen(const LmsKey* key, word32* len);
WOLFSSL_API int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz,
const byte* msg, int msgSz);
WOLFSSL_API int wc_LmsKey_SigsLeft(LmsKey* key);
WOLFSSL_API int wc_LmsKey_PublicKeyToDer(const LmsKey* key, byte* output,
word32 inLen, int withAlg);
#endif /* ifndef WOLFSSL_LMS_VERIFY_ONLY */
WOLFSSL_API void wc_LmsKey_Free(LmsKey* key);
WOLFSSL_API int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len);
+9 -2
View File
@@ -346,7 +346,7 @@ typedef struct XmssParams {
#define XMSS_MAX_LABEL_LEN 32
#endif
typedef struct XmssKey {
struct XmssKey {
/* Public key. */
unsigned char pk[2 * WC_XMSS_MAX_N];
/* OID that identifies parameters. */
@@ -385,7 +385,12 @@ typedef struct XmssKey {
char label[XMSS_MAX_LABEL_LEN];
int labelLen;
#endif
} XmssKey;
};
#ifndef WC_XMSSKEY_TYPE_DEFINED
typedef struct XmssKey XmssKey;
#define WC_XMSSKEY_TYPE_DEFINED
#endif
typedef struct XmssState {
const XmssParams* params;
@@ -446,6 +451,8 @@ WOLFSSL_API int wc_XmssKey_GetPrivLen(const XmssKey* key, word32* len);
WOLFSSL_API int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigSz,
const byte* msg, int msgSz);
WOLFSSL_API int wc_XmssKey_SigsLeft(XmssKey* key);
WOLFSSL_API int wc_XmssKey_PublicKeyToDer(const XmssKey* key, byte* output,
word32 inLen, int withAlg);
#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */
WOLFSSL_API void wc_XmssKey_Free(XmssKey* key);
WOLFSSL_API int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len);