mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 21:40:52 +02:00
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:
@@ -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.
@@ -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.
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user