mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 10:20:50 +02:00
Support RFC 9802 LMS and XMSS in X.509 verification
Wire the stateful hash-based signature schemes HSS/LMS (RFC 8554) and XMSS / XMSS^MT (RFC 8391) into the X.509 cert-verification path per RFC 9802. asn: - Register id-alg-hss-lms-hashsig (1.2.840.113549.1.9.16.3.17), id-alg-xmss-hashsig (1.3.6.1.5.5.7.6.34) and id-alg-xmssmt-hashsig (1.3.6.1.5.5.7.6.35) in oid_sum.h, asn.c and asn1_oid_sum.pl. - Plumb the new keyOIDs through GetCertKey, SigOidMatchesKeyOid, HashForSignature, FreeSignatureCtx and ConfirmSignature so leaf and CA certificates parse, load and verify end-to-end. - Rename IsSigAlgoECC -> IsSigAlgoNoParams; the function has tested "AlgorithmIdentifier omits NULL parameters" since PQC algos were added, and HSS/LMS + XMSS only made the original name more misleading. wc_lms / wc_xmss: - Add wc_XmssKey_ImportPubRaw_ex which derives parameters from the 4-byte OID prefix at the start of the raw public key, taking an is_xmssmt hint to disambiguate the overlapping XMSS / XMSS^MT OID spaces. - Extend wc_LmsKey_ImportPubRaw with the same auto-derive from u32str(L) || lmsType || lmOtsType when key->params is NULL; this also fixes a latent NULL-deref when the legacy precondition was violated. - Reject WC_*_STATE_OK in both ImportPubRaw paths so re-importing on a private-key-loaded handle can't desync priv/pub. - Tighten wc_XmssKey_Verify's length check to strict equality, matching wc_LmsKey_Verify and the documented contract of using wc_XmssKey_GetSigLen for the buffer size. tests / fixtures: - Bouncy Castle 1.81 fixtures in certs/lms and certs/xmss covering every supported parameter set, plus CA->leaf chains per family and one BC-native LMS fixture as a cross-impl interop gate. - New api tests verify each fixture end-to-end, tamper TBS and signature bytes, exercise the wolfCrypt-level negative paths (NOT_COMPILED_IN, BUFFER_E, BAD_FUNC_ARG, BAD_STATE_E, OID/family mismatch, partial-write invariants, lenient VERIFYONLY re-import, strict sigLen check) and confirm the outer signatureAlgorithm OID is rejected when it disagrees with the SPKI in both XMSS<->XMSS^MT directions.
This commit is contained in:
+9
-1
@@ -699,6 +699,10 @@ add_option(WOLFSSL_LMSSHA256192
|
||||
"Enable the LMS SHA_256_192 truncated variant (default: disabled)"
|
||||
"no" "yes;no")
|
||||
|
||||
add_option(WOLFSSL_LMSNOSHA256256
|
||||
"Disable the LMS SHA_256_256 standard variant (default: disabled)"
|
||||
"no" "yes;no")
|
||||
|
||||
if (WOLFSSL_LMS)
|
||||
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_HAVE_LMS")
|
||||
|
||||
@@ -706,9 +710,13 @@ if (WOLFSSL_LMS)
|
||||
|
||||
if (WOLFSSL_LMSSHA256192)
|
||||
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_LMS_SHA256_192")
|
||||
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_LMS_SHA256_256")
|
||||
|
||||
set_wolfssl_definitions("WOLFSSL_LMS_SHA256_192" RESULT)
|
||||
endif()
|
||||
|
||||
if (WOLFSSL_LMSNOSHA256256)
|
||||
list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_LMS_SHA256_256")
|
||||
|
||||
set_wolfssl_definitions("WOLFSSL_NO_LMS_SHA256_256" RESULT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -161,6 +161,8 @@ include certs/falcon/include.am
|
||||
include certs/rsapss/include.am
|
||||
include certs/dilithium/include.am
|
||||
include certs/slhdsa/include.am
|
||||
include certs/lms/include.am
|
||||
include certs/xmss/include.am
|
||||
include certs/rpk/include.am
|
||||
include certs/acert/include.am
|
||||
include certs/mldsa/include.am
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
# vim:ft=automake
|
||||
# All paths should be given relative to the root
|
||||
#
|
||||
|
||||
EXTRA_DIST += \
|
||||
certs/lms/bc_lms_sha256_h5_w4_root.der \
|
||||
certs/lms/bc_lms_sha256_h10_w8_root.der \
|
||||
certs/lms/bc_hss_L2_H5_W8_root.der \
|
||||
certs/lms/bc_hss_L3_H5_W4_root.der \
|
||||
certs/lms/bc_lms_chain_ca.der \
|
||||
certs/lms/bc_lms_chain_leaf.der \
|
||||
certs/lms/bc_lms_native_bc_root.der
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
# vim:ft=automake
|
||||
# All paths should be given relative to the root
|
||||
#
|
||||
|
||||
EXTRA_DIST += \
|
||||
certs/xmss/bc_xmss_sha2_10_256_root.der \
|
||||
certs/xmss/bc_xmss_sha2_16_256_root.der \
|
||||
certs/xmss/bc_xmssmt_sha2_20_2_256_root.der \
|
||||
certs/xmss/bc_xmssmt_sha2_20_4_256_root.der \
|
||||
certs/xmss/bc_xmssmt_sha2_40_8_256_root.der \
|
||||
certs/xmss/bc_xmss_chain_ca.der \
|
||||
certs/xmss/bc_xmss_chain_leaf.der
|
||||
@@ -314,6 +314,9 @@ my @slhdsa_shake_192s = (2, 16, 840, 1, 101, 3, 4, 3, 28);
|
||||
my @slhdsa_shake_192f = (2, 16, 840, 1, 101, 3, 4, 3, 29);
|
||||
my @slhdsa_shake_256s = (2, 16, 840, 1, 101, 3, 4, 3, 30);
|
||||
my @slhdsa_shake_256f = (2, 16, 840, 1, 101, 3, 4, 3, 31);
|
||||
my @hss_lms = ( 1, 2, 840, 113549, 1, 9, 16, 3, 17 );
|
||||
my @xmss = ( 1, 3, 6, 1, 5, 5, 7, 6, 34 );
|
||||
my @xmssmt = ( 1, 3, 6, 1, 5, 5, 7, 6, 35 );
|
||||
|
||||
my @keys = (
|
||||
{ name => "ANON", oid => \@anon },
|
||||
@@ -348,6 +351,9 @@ my @keys = (
|
||||
{ name => "SLH_DSA_SHAKE_192F", oid => \@slhdsa_shake_192f },
|
||||
{ name => "SLH_DSA_SHAKE_256S", oid => \@slhdsa_shake_256s },
|
||||
{ name => "SLH_DSA_SHAKE_256F", oid => \@slhdsa_shake_256f },
|
||||
{ name => "HSS_LMS", oid => \@hss_lms },
|
||||
{ name => "XMSS", oid => \@xmss },
|
||||
{ name => "XMSSMT", oid => \@xmssmt },
|
||||
);
|
||||
|
||||
print_sum_enum("Key", "k", \@keys);
|
||||
@@ -1161,6 +1167,12 @@ my @sig_types = (
|
||||
same => 1 },
|
||||
{ name => "CTC_SLH_DSA_SHAKE_256F", oid => \@slhdsa_shake_256f,
|
||||
same => 1 },
|
||||
{ name => "CTC_HSS_LMS", oid => \@hss_lms,
|
||||
same => 1 },
|
||||
{ name => "CTC_XMSS", oid => \@xmss,
|
||||
same => 1 },
|
||||
{ name => "CTC_XMSSMT", oid => \@xmssmt,
|
||||
same => 1 },
|
||||
);
|
||||
|
||||
print_enum("Ctc_SigType", "", \@sig_types, 32, 48);
|
||||
|
||||
+722
@@ -37714,6 +37714,8 @@ int stopOnFail = 0;
|
||||
/*----------------------------------------------------------------------------*/
|
||||
int test_wc_LmsKey_sign_verify(void);
|
||||
int test_wc_LmsKey_reload_cache(void);
|
||||
int test_rfc9802_lms_x509_verify(void);
|
||||
int test_rfc9802_xmss_x509_verify(void);
|
||||
|
||||
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
||||
|
||||
@@ -37879,6 +37881,722 @@ int test_wc_LmsKey_reload_cache(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509) tests */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
/* For every committed self-signed test certificate confirm:
|
||||
* - wc_ParseCert succeeds on the RFC 9802 AlgorithmIdentifier encoding
|
||||
* (OID-only SEQUENCE, no NULL parameters)
|
||||
* - keyOID and signatureOID are set to the expected values
|
||||
* - loading as a trust anchor and verifying the same bytes through
|
||||
* wolfSSL_CertManagerVerifyBuffer exercises the ConfirmSignature
|
||||
* path and succeeds on a valid cert
|
||||
* - flipping a byte in the signature AND flipping a byte in the
|
||||
* TBSCertificate both cause verification to fail.
|
||||
*
|
||||
* Test vectors are in certs/lms/ and certs/xmss/, generated with Bouncy
|
||||
* Castle 1.81. BC's default XMSS / XMSS^MT X.509 encoding uses pre-
|
||||
* standard ISARA OIDs and wraps the raw RFC 8391 pub key in an OCTET
|
||||
* STRING, so the fixtures were produced with a small generator that
|
||||
* overrides the AlgorithmIdentifier and SPKI to match RFC 9802. */
|
||||
#if (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) && \
|
||||
!defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
||||
/* Sanity bound on a test fixture cert. The largest BC-generated
|
||||
* fixture we ship (XMSS^MT 40/8) is ~19 KiB; 1 MiB is well above
|
||||
* any realistic RFC 9802 cert and catches a wild XFTELL. Typed as
|
||||
* long to match XFTELL's return so the size comparison below isn't
|
||||
* a mixed long-vs-int compare. */
|
||||
#define RFC9802_TEST_MAX_CERT_SIZE ((long)(1L << 20))
|
||||
|
||||
/* Load a whole file into a freshly-allocated buffer. Caller frees. */
|
||||
static int rfc9802_load_file(const char* path, byte** out, int* outLen)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
XFILE f = XBADFILE;
|
||||
long sz = 0;
|
||||
size_t got = 0;
|
||||
byte* buf = NULL;
|
||||
|
||||
*out = NULL;
|
||||
*outLen = 0;
|
||||
ExpectTrue((f = XFOPEN(path, "rb")) != XBADFILE);
|
||||
if (f == XBADFILE)
|
||||
return TEST_FAIL;
|
||||
if (XFSEEK(f, 0, XSEEK_END) == 0)
|
||||
sz = XFTELL(f);
|
||||
(void)XFSEEK(f, 0, XSEEK_SET);
|
||||
ExpectIntGT(sz, 0);
|
||||
ExpectIntLT(sz, RFC9802_TEST_MAX_CERT_SIZE);
|
||||
/* Hard-fail before XMALLOC if XFSEEK / XFTELL produced an unusable
|
||||
* size: ExpectInt* records the failure but doesn't short-circuit,
|
||||
* so without this guard a -1 from XFTELL would cast to a multi-GiB
|
||||
* (size_t) allocation, and a 0 would request a zero-byte malloc. */
|
||||
if (sz <= 0 || sz >= RFC9802_TEST_MAX_CERT_SIZE) {
|
||||
XFCLOSE(f);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
ExpectNotNull(buf = (byte*)XMALLOC((size_t)sz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER));
|
||||
if (buf != NULL) {
|
||||
got = XFREAD(buf, 1, (size_t)sz, f);
|
||||
ExpectIntEQ(got, (size_t)sz);
|
||||
/* On a short read the caller would otherwise proceed with a
|
||||
* partially-initialized buffer and produce cascading parse
|
||||
* failures driven by the uninitialized tail. Free here so the
|
||||
* caller's `if (buf == NULL) return TEST_FAIL;` short-circuits
|
||||
* cleanly with a single recorded failure. */
|
||||
if (got != (size_t)sz) {
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
buf = NULL;
|
||||
sz = 0;
|
||||
}
|
||||
}
|
||||
XFCLOSE(f);
|
||||
*out = buf;
|
||||
*outLen = (int)sz;
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
static int rfc9802_verify_one_cert(const char* path, word32 expectedKeyOID,
|
||||
word32 expectedSigOID)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
byte* buf = NULL;
|
||||
byte* tampered = NULL;
|
||||
int bytes = 0;
|
||||
DecodedCert cert;
|
||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||
word32 certBegin = 0;
|
||||
word32 sigIndex = 0;
|
||||
|
||||
ExpectIntEQ(rfc9802_load_file(path, &buf, &bytes), TEST_SUCCESS);
|
||||
if (buf == NULL)
|
||||
return TEST_FAIL;
|
||||
|
||||
/* Parse + check OIDs, capture certBegin and sigIndex for later tamper. */
|
||||
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
|
||||
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
|
||||
ExpectIntEQ((int)cert.keyOID, (int)expectedKeyOID);
|
||||
ExpectIntEQ((int)cert.signatureOID, (int)expectedSigOID);
|
||||
certBegin = cert.certBegin;
|
||||
sigIndex = cert.sigIndex;
|
||||
wc_FreeDecodedCert(&cert);
|
||||
|
||||
/* Full verify against a self-installed trust anchor. */
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, buf, (long)bytes,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
|
||||
ExpectNotNull(tampered = (byte*)XMALLOC((size_t)bytes, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER));
|
||||
|
||||
/* Negative 1: flip a byte inside the signatureValue BIT STRING.
|
||||
* Everything after sigIndex is the signatureAlgorithm + the BIT
|
||||
* STRING payload, so flipping the last byte is always inside the
|
||||
* signature content. */
|
||||
if (tampered != NULL) {
|
||||
XMEMCPY(tampered, buf, (size_t)bytes);
|
||||
tampered[bytes - 1] ^= 0x01;
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, tampered,
|
||||
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Negative 2: flip a byte at the midpoint of the TBSCertificate. The
|
||||
* TBS is the first element of the outer Certificate SEQUENCE and
|
||||
* its bytes lie between (certBegin + outerSeqHeader) and sigIndex.
|
||||
* Picking the midpoint ensures we're inside TBS regardless of the
|
||||
* fixture's DN / extensions layout. */
|
||||
if (tampered != NULL && sigIndex > certBegin + 8u) {
|
||||
word32 midTbs = certBegin + 8 + ((sigIndex - (certBegin + 8)) / 2);
|
||||
XMEMCPY(tampered, buf, (size_t)bytes);
|
||||
tampered[midTbs] ^= 0x01;
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, tampered,
|
||||
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* The fixtures MUST carry a KeyUsage extension with at least one of
|
||||
* digitalSignature / nonRepudiation / keyCertSign / cRLSign set per
|
||||
* RFC 9802 sec 3. Re-parse and assert that wolfSSL recorded a non-
|
||||
* empty set of KeyUsage bits from one of those values. */
|
||||
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
|
||||
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
|
||||
ExpectIntEQ(cert.extKeyUsageSet, 1);
|
||||
ExpectIntNE(cert.extKeyUsage & (KEYUSE_DIGITAL_SIG | KEYUSE_CONTENT_COMMIT |
|
||||
KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN), 0);
|
||||
wc_FreeDecodedCert(&cert);
|
||||
|
||||
XFREE(tampered, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Direct wolfCrypt-level negative tests for the parameter-derivation
|
||||
* helpers used by the RFC 9802 parse path. These exercise failure modes
|
||||
* (unknown algorithm bytes, truncated inputs, mismatches) that a real
|
||||
* cert body wouldn't easily reach. */
|
||||
#if defined(WOLFSSL_HAVE_LMS)
|
||||
static int rfc9802_lms_import_negative(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
LmsKey key;
|
||||
/* 60-byte buffer matches HSS_PUBLIC_KEY_LEN(32), just like a valid
|
||||
* SHA-256/M32/H5 key; the algorithm-type bytes are junk so param
|
||||
* derivation must fail cleanly. */
|
||||
byte junk[60];
|
||||
|
||||
XMEMSET(junk, 0, sizeof(junk));
|
||||
/* levels=1, lmsType=0xFFFFFFFF, lmOtsType=0xFFFFFFFF. */
|
||||
junk[3] = 1;
|
||||
XMEMSET(junk + 4, 0xFF, 4);
|
||||
XMEMSET(junk + 8, 0xFF, 4);
|
||||
|
||||
/* Unknown algorithm types must be rejected. */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, sizeof(junk)),
|
||||
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
|
||||
wc_LmsKey_Free(&key);
|
||||
|
||||
/* Too-short buffer: only L + lmsType, no lmOtsType. */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, 8),
|
||||
WC_NO_ERR_TRACE(BUFFER_E));
|
||||
wc_LmsKey_Free(&key);
|
||||
|
||||
#if !defined(WOLFSSL_NO_LMS_SHA256_256)
|
||||
/* The two cases below pin specific SHA-256/M32 parameter codes
|
||||
* (L1_H5_W8, L1_H5_W4, L1_H10_W2). Skip them in builds where the
|
||||
* SHA-256/M32 family is disabled -- the family-agnostic checks
|
||||
* above (junk algorithm types, too-short buffer, GetSigLen on
|
||||
* unconfigured key) still cover the universal invariants. */
|
||||
|
||||
/* Pre-set params that disagree with the raw key's algorithm bytes:
|
||||
* configure H=5/W=8 but feed buffer that claims H=10 / W=2. */
|
||||
XMEMSET(junk, 0, sizeof(junk));
|
||||
junk[3] = 1; /* levels=1 */
|
||||
junk[7] = 6; /* lmsType = LMS_SHA256_M32_H10 = 6 */
|
||||
junk[11] = 2; /* lmOtsType = LMOTS_SHA256_N32_W2 = 2 */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_LmsKey_SetParameters(&key, 1, 5, 8), 0);
|
||||
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, junk, sizeof(junk)),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
wc_LmsKey_Free(&key);
|
||||
#endif /* !WOLFSSL_NO_LMS_SHA256_256 */
|
||||
|
||||
/* GetSigLen on a key with no params set must not NULL-deref the
|
||||
* params pointer; it must return BAD_FUNC_ARG instead. */
|
||||
{
|
||||
word32 sigLen = 0;
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_LmsKey_GetSigLen(&key, &sigLen),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
wc_LmsKey_Free(&key);
|
||||
}
|
||||
|
||||
#if !defined(WOLFSSL_NO_LMS_SHA256_256)
|
||||
/* Partial-write invariant: a length mismatch after a successful
|
||||
* auto-derive must leave key->params NULL. Build a buffer whose
|
||||
* leading u32str(L) || lmsType || lmOtsType identifies a known
|
||||
* parameter set, but truncate to one byte less than the real pub
|
||||
* key length so the post-derive length check fails. */
|
||||
{
|
||||
byte truncated[59]; /* HSS_PUBLIC_KEY_LEN(32) is 60 */
|
||||
XMEMSET(truncated, 0, sizeof(truncated));
|
||||
truncated[3] = 1; /* L = 1 */
|
||||
truncated[7] = 5; /* lmsType = LMS_SHA256_M32_H5 */
|
||||
truncated[11] = 4; /* lmOtsType = LMOTS_SHA256_N32_W4 */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectNull(key.params);
|
||||
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, truncated,
|
||||
sizeof(truncated)), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
ExpectNull(key.params);
|
||||
wc_LmsKey_Free(&key);
|
||||
}
|
||||
#endif /* !WOLFSSL_NO_LMS_SHA256_256 */
|
||||
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSL_HAVE_XMSS)
|
||||
static int rfc9802_xmss_import_negative(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
XmssKey key;
|
||||
byte junk[8];
|
||||
|
||||
XMEMSET(junk, 0, sizeof(junk));
|
||||
|
||||
/* Too-short buffer. */
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, 2, 0),
|
||||
WC_NO_ERR_TRACE(BUFFER_E));
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
/* Unknown OID (all-zero) for both XMSS and XMSS^MT. */
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, sizeof(junk), 0),
|
||||
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
|
||||
wc_XmssKey_Free(&key);
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, junk, sizeof(junk), 1),
|
||||
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
/* NULL key / input. */
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(NULL, junk, sizeof(junk), 0),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, NULL, 8, 0),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
/* GetSigLen on a key with no params set must not NULL-deref the
|
||||
* params pointer; it must return BAD_FUNC_ARG instead. */
|
||||
{
|
||||
word32 sigLen = 0;
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_GetSigLen(&key, &sigLen),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* Once params have been configured (state != INITED), the OID
|
||||
* prefix in the raw key MUST match key->oid and is_xmssmt MUST
|
||||
* match key->is_xmssmt. Set XMSS-SHA2_10_256 and feed a valid-
|
||||
* sized buffer whose 4-byte OID prefix is bogus -> BAD_FUNC_ARG. */
|
||||
{
|
||||
byte mismatch[XMSS_SHA256_PUBLEN];
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
|
||||
XMEMSET(mismatch, 0, sizeof(mismatch));
|
||||
mismatch[3] = 0x77; /* nonsense OID */
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
|
||||
sizeof(mismatch), 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
/* Same buffer with the correct OID, but is_xmssmt hint
|
||||
* contradicts the configured family -> BAD_FUNC_ARG. */
|
||||
mismatch[3] = 0x01; /* WC_XMSS_OID_SHA2_10_256 */
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
|
||||
sizeof(mismatch), 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* Partial-write invariant: a length mismatch after a successful
|
||||
* auto-derive must leave the key in its INITED state, with
|
||||
* key->params NULL. */
|
||||
{
|
||||
byte truncated[XMSS_SHA256_PUBLEN - 1];
|
||||
XMEMSET(truncated, 0, sizeof(truncated));
|
||||
truncated[3] = 0x01;
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectNull(key.params);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, truncated,
|
||||
sizeof(truncated), 0), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
ExpectNull(key.params);
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* is_xmssmt disambiguation: XMSS oid=1 and XMSS^MT oid=1 share
|
||||
* the wire-numeric value but resolve to different parameter sets.
|
||||
* Importing the same 68-byte buffer with hint=0 vs hint=1 must
|
||||
* land in different tables and produce distinct is_xmssmt. */
|
||||
{
|
||||
byte buf[XMSS_SHA256_PUBLEN];
|
||||
XMEMSET(buf, 0, sizeof(buf));
|
||||
buf[3] = 0x01;
|
||||
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
|
||||
ExpectIntEQ((int)key.is_xmssmt, 0);
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 1), 0);
|
||||
ExpectIntEQ((int)key.is_xmssmt, 1);
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* Lenient state: re-importing the same pub key into a VERIFYONLY
|
||||
* key (params set, no private material) succeeds. The second
|
||||
* call exercises the lenient-state branch. */
|
||||
{
|
||||
byte buf[XMSS_SHA256_PUBLEN];
|
||||
XMEMSET(buf, 0, sizeof(buf));
|
||||
buf[3] = 0x01;
|
||||
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
|
||||
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, buf, sizeof(buf), 0), 0);
|
||||
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* Strict signature-length check: wc_XmssKey_Verify rejects any
|
||||
* sigLen != key->params->sig_len. This guards every consumer
|
||||
* (RFC 9802 X.509, PKCS#7, CMS, ...) against a longer wrapper that
|
||||
* happens to start with a valid signature. Construct a key in
|
||||
* VERIFYONLY state, then verify with sig_len + 1 and sig_len - 1
|
||||
* byte buffers; both must fail with BUFFER_E before any crypto
|
||||
* runs. The buffer contents are irrelevant since the length check
|
||||
* fires first. */
|
||||
{
|
||||
byte pub[XMSS_SHA256_PUBLEN];
|
||||
byte* sigBuf = NULL;
|
||||
word32 sigLen = 0;
|
||||
const byte msg[1] = { 0 };
|
||||
|
||||
XMEMSET(pub, 0, sizeof(pub));
|
||||
pub[3] = 0x01;
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, pub, sizeof(pub), 0), 0);
|
||||
ExpectIntEQ((int)key.state, (int)WC_XMSS_STATE_VERIFYONLY);
|
||||
ExpectIntEQ(wc_XmssKey_GetSigLen(&key, &sigLen), 0);
|
||||
ExpectIntGT(sigLen, 0);
|
||||
ExpectNotNull(sigBuf = (byte*)XMALLOC((size_t)sigLen + 1, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER));
|
||||
if (sigBuf != NULL) {
|
||||
XMEMSET(sigBuf, 0, (size_t)sigLen + 1);
|
||||
ExpectIntEQ(wc_XmssKey_Verify(&key, sigBuf, sigLen + 1,
|
||||
msg, (int)sizeof(msg)), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
ExpectIntEQ(wc_XmssKey_Verify(&key, sigBuf, sigLen - 1,
|
||||
msg, (int)sizeof(msg)), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
XFREE(sigBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
/* BAD_STATE_E branch: WC_XMSS_STATE_OK must be rejected. Reaching
|
||||
* OK normally requires a successful private-key Reload / sign,
|
||||
* which is unavailable in WOLFSSL_XMSS_VERIFY_ONLY builds. Force
|
||||
* the state directly to exercise the rejection without coupling
|
||||
* this helper to the signing test fixture; sk stays NULL so Free
|
||||
* is still safe. */
|
||||
{
|
||||
byte pub[XMSS_SHA256_PUBLEN];
|
||||
|
||||
XMEMSET(pub, 0, sizeof(pub));
|
||||
pub[3] = 0x01;
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
|
||||
key.state = WC_XMSS_STATE_OK;
|
||||
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, pub, sizeof(pub), 0),
|
||||
WC_NO_ERR_TRACE(BAD_STATE_E));
|
||||
wc_XmssKey_Free(&key);
|
||||
}
|
||||
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Walk the AlgorithmIdentifier SEQUENCE that begins at sigIndex and
|
||||
* locate the byte offset of the last byte of its OID content. Handles
|
||||
* both short-form (length < 128) and long-form DER length encodings,
|
||||
* so a future fixture-regenerator that emits longer OIDs / SEQUENCEs
|
||||
* still drives this test rather than tripping the loud-fail branch.
|
||||
*
|
||||
* Returns 0 on success with *oidLastByte set; returns -1 on any DER
|
||||
* shape mismatch. */
|
||||
#if defined(WOLFSSL_HAVE_XMSS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
||||
static int rfc9802_find_sig_alg_oid_last_byte(const byte* buf, word32 bufLen,
|
||||
word32 sigIndex, word32* oidLastByte)
|
||||
{
|
||||
word32 idx = sigIndex;
|
||||
word32 oidContentLen = 0;
|
||||
|
||||
/* AlgorithmIdentifier ::= SEQUENCE { algorithm OID, ... } */
|
||||
if (idx >= bufLen || buf[idx] != 0x30)
|
||||
return -1;
|
||||
idx++;
|
||||
/* Skip SEQUENCE length (short or long form). */
|
||||
if (idx >= bufLen)
|
||||
return -1;
|
||||
if (buf[idx] < 0x80) {
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
word32 nbytes = (word32)(buf[idx] & 0x7F);
|
||||
if (nbytes == 0 || nbytes > 4 || idx + 1 + nbytes > bufLen)
|
||||
return -1;
|
||||
idx += 1 + nbytes;
|
||||
}
|
||||
/* algorithm OID tag. */
|
||||
if (idx >= bufLen || buf[idx] != 0x06)
|
||||
return -1;
|
||||
idx++;
|
||||
/* OID length (short or long form). */
|
||||
if (idx >= bufLen)
|
||||
return -1;
|
||||
if (buf[idx] < 0x80) {
|
||||
oidContentLen = buf[idx];
|
||||
idx++;
|
||||
}
|
||||
else {
|
||||
word32 nbytes = (word32)(buf[idx] & 0x7F);
|
||||
word32 i;
|
||||
if (nbytes == 0 || nbytes > 4 || idx + 1 + nbytes > bufLen)
|
||||
return -1;
|
||||
for (i = 0; i < nbytes; i++)
|
||||
oidContentLen = (oidContentLen << 8) | buf[idx + 1 + i];
|
||||
idx += 1 + nbytes;
|
||||
}
|
||||
if (oidContentLen == 0 || idx + oidContentLen > bufLen)
|
||||
return -1;
|
||||
*oidLastByte = idx + oidContentLen - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper: load fixture, locate last byte of outer signatureAlgorithm
|
||||
* OID, patch it from `expected` to `swap`, and assert that verifying
|
||||
* the patched cert against itself as a trust anchor fails. */
|
||||
static int rfc9802_assert_oid_patch_breaks_verify(const char* path,
|
||||
byte expectedLastByte, byte patchedLastByte)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
byte* buf = NULL;
|
||||
int bytes = 0;
|
||||
DecodedCert cert;
|
||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||
word32 sigIndex = 0;
|
||||
word32 lastOidByte = 0;
|
||||
|
||||
ExpectIntEQ(rfc9802_load_file(path, &buf, &bytes), TEST_SUCCESS);
|
||||
if (buf == NULL)
|
||||
return TEST_FAIL;
|
||||
|
||||
wc_InitDecodedCert(&cert, buf, (word32)bytes, NULL);
|
||||
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
|
||||
sigIndex = cert.sigIndex;
|
||||
wc_FreeDecodedCert(&cert);
|
||||
|
||||
ExpectIntEQ(rfc9802_find_sig_alg_oid_last_byte(buf, (word32)bytes,
|
||||
sigIndex, &lastOidByte), 0);
|
||||
/* Sanity-check the fixture matches the family the caller asserted,
|
||||
* so a future regenerator swapping fixtures fails loudly here
|
||||
* rather than silently testing the wrong direction. */
|
||||
ExpectIntEQ((int)buf[lastOidByte], (int)expectedLastByte);
|
||||
|
||||
if (lastOidByte < (word32)bytes &&
|
||||
buf[lastOidByte] == expectedLastByte) {
|
||||
buf[lastOidByte] = patchedLastByte;
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
/* After the patch the cert's outer signatureAlgorithm and SPKI
|
||||
* disagree. Verification must fail somewhere (at parse, at
|
||||
* load, or at ConfirmSignature). The load is best-effort -
|
||||
* some shape changes get caught there, others only at verify. */
|
||||
(void)wolfSSL_CertManagerLoadCABuffer(cm, buf, (long)bytes,
|
||||
WOLFSSL_FILETYPE_ASN1);
|
||||
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, buf,
|
||||
(long)bytes, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* X.509-level negative: swap the outer signatureAlgorithm OID byte so
|
||||
* the cert declares XMSS where the SPKI is XMSS^MT, and vice versa.
|
||||
* SigOidMatchesKeyOid must reject both directions before any crypto. */
|
||||
static int rfc9802_xmss_sig_oid_mismatch(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* XMSS sigOID ends 0x22; XMSS^MT sigOID ends 0x23. Patch each
|
||||
* direction so the asymmetric-key path is exercised both ways -
|
||||
* a regression that only stripped the check from one branch of
|
||||
* SigOidMatchesKeyOid would otherwise be missed. */
|
||||
ExpectIntEQ(rfc9802_assert_oid_patch_breaks_verify(
|
||||
"./certs/xmss/bc_xmss_sha2_10_256_root.der",
|
||||
/* expected XMSS */ 0x22, /* patched to XMSS^MT */ 0x23),
|
||||
TEST_SUCCESS);
|
||||
ExpectIntEQ(rfc9802_assert_oid_patch_breaks_verify(
|
||||
"./certs/xmss/bc_xmssmt_sha2_20_2_256_root.der",
|
||||
/* expected XMSS^MT */ 0x23, /* patched to XMSS */ 0x22),
|
||||
TEST_SUCCESS);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Exercise a real CA -> leaf certificate chain, not just self-signed.
|
||||
* Loads the CA as a trust anchor and verifies the leaf against it. */
|
||||
#if defined(WOLFSSL_HAVE_LMS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
||||
static int rfc9802_lms_chain_verify(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
byte* caBuf = NULL;
|
||||
byte* leafBuf = NULL;
|
||||
int caLen = 0;
|
||||
int leafLen = 0;
|
||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||
|
||||
ExpectIntEQ(rfc9802_load_file("./certs/lms/bc_lms_chain_ca.der",
|
||||
&caBuf, &caLen), TEST_SUCCESS);
|
||||
ExpectIntEQ(rfc9802_load_file("./certs/lms/bc_lms_chain_leaf.der",
|
||||
&leafBuf, &leafLen), TEST_SUCCESS);
|
||||
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
/* Only the CA is a trust anchor; the leaf is verified against it. */
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caBuf, (long)caLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
|
||||
/* Without loading the CA the leaf must NOT verify. */
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
|
||||
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(caBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Mirror of rfc9802_lms_chain_verify but for an XMSS CA -> leaf pair. */
|
||||
#if defined(WOLFSSL_HAVE_XMSS) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
||||
static int rfc9802_xmss_chain_verify(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
byte* caBuf = NULL;
|
||||
byte* leafBuf = NULL;
|
||||
int caLen = 0;
|
||||
int leafLen = 0;
|
||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||
|
||||
ExpectIntEQ(rfc9802_load_file("./certs/xmss/bc_xmss_chain_ca.der",
|
||||
&caBuf, &caLen), TEST_SUCCESS);
|
||||
ExpectIntEQ(rfc9802_load_file("./certs/xmss/bc_xmss_chain_leaf.der",
|
||||
&leafBuf, &leafLen), TEST_SUCCESS);
|
||||
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caBuf, (long)caLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntNE(wolfSSL_CertManagerVerifyBuffer(cm, leafBuf, (long)leafLen,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
if (cm != NULL) {
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
cm = NULL;
|
||||
}
|
||||
|
||||
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(caBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
int test_rfc9802_lms_x509_verify(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_HAVE_LMS)
|
||||
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
||||
!defined(WOLFSSL_NO_LMS_SHA256_256)
|
||||
/* Mixed single-level LMS and multi-level HSS fixtures. The HSS
|
||||
* public key carries only the top-level LMS/LM-OTS types, so
|
||||
* wc_LmsKey_ImportPubRaw's auto-derive path searches the map
|
||||
* by (levels, lmsType, lmOtsType). The bc_lms_native_bc_root
|
||||
* fixture is generated through Bouncy Castle's stock
|
||||
* JcaContentSignerBuilder("LMS") + JcaX509v3CertificateBuilder
|
||||
* with no overrides; including it here is the cross-impl interop
|
||||
* gate (BC's native LMS X.509 path is RFC 9802-compliant for HSS/
|
||||
* LMS, so wolfSSL must accept it end-to-end).
|
||||
*
|
||||
* All fixtures use the SHA-256/M32 family, so the whole block
|
||||
* is gated on that family being compiled in. Truncated SHA-256/192
|
||||
* or SHAKE-only builds skip this block. */
|
||||
static const char* const lmsFiles[] = {
|
||||
"./certs/lms/bc_lms_sha256_h5_w4_root.der",
|
||||
"./certs/lms/bc_lms_sha256_h10_w8_root.der",
|
||||
"./certs/lms/bc_hss_L2_H5_W8_root.der",
|
||||
"./certs/lms/bc_hss_L3_H5_W4_root.der",
|
||||
"./certs/lms/bc_lms_native_bc_root.der",
|
||||
};
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(lmsFiles) / sizeof(lmsFiles[0]); i++) {
|
||||
ExpectIntEQ(rfc9802_verify_one_cert(lmsFiles[i],
|
||||
HSS_LMSk, CTC_HSS_LMS), TEST_SUCCESS);
|
||||
}
|
||||
ExpectIntEQ(rfc9802_lms_chain_verify(), TEST_SUCCESS);
|
||||
#endif /* !NO_FILESYSTEM && !NO_CERTS && !WOLFSSL_NO_LMS_SHA256_256 */
|
||||
/* Pure wolfCrypt-level negative tests don't need filesystem or cert
|
||||
* support, so they run for any LMS-enabled build. */
|
||||
ExpectIntEQ(rfc9802_lms_import_negative(), TEST_SUCCESS);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_rfc9802_xmss_x509_verify(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_HAVE_XMSS)
|
||||
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
||||
static const char* const xmssFiles[] = {
|
||||
"./certs/xmss/bc_xmss_sha2_10_256_root.der",
|
||||
"./certs/xmss/bc_xmss_sha2_16_256_root.der",
|
||||
};
|
||||
static const char* const xmssmtFiles[] = {
|
||||
"./certs/xmss/bc_xmssmt_sha2_20_2_256_root.der",
|
||||
"./certs/xmss/bc_xmssmt_sha2_20_4_256_root.der",
|
||||
"./certs/xmss/bc_xmssmt_sha2_40_8_256_root.der",
|
||||
};
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(xmssFiles) / sizeof(xmssFiles[0]); i++) {
|
||||
ExpectIntEQ(rfc9802_verify_one_cert(xmssFiles[i],
|
||||
XMSSk, CTC_XMSS), TEST_SUCCESS);
|
||||
}
|
||||
for (i = 0; i < sizeof(xmssmtFiles) / sizeof(xmssmtFiles[0]); i++) {
|
||||
ExpectIntEQ(rfc9802_verify_one_cert(xmssmtFiles[i],
|
||||
XMSSMTk, CTC_XMSSMT), TEST_SUCCESS);
|
||||
}
|
||||
ExpectIntEQ(rfc9802_xmss_sig_oid_mismatch(), TEST_SUCCESS);
|
||||
ExpectIntEQ(rfc9802_xmss_chain_verify(), TEST_SUCCESS);
|
||||
#endif /* !NO_FILESYSTEM && !NO_CERTS */
|
||||
/* Pure wolfCrypt-level negative tests don't need filesystem or cert
|
||||
* support, so they run for any XMSS-enabled build. */
|
||||
ExpectIntEQ(rfc9802_xmss_import_negative(), TEST_SUCCESS);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
|
||||
static int test_sniffer_chain_input_overflow(void)
|
||||
{
|
||||
@@ -39153,6 +39871,10 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL_GROUP("lms", test_wc_LmsKey_sign_verify),
|
||||
TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_cache),
|
||||
|
||||
/* RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509) */
|
||||
TEST_DECL_GROUP("lms", test_rfc9802_lms_x509_verify),
|
||||
TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_verify),
|
||||
|
||||
/* PEM and DER APIs. */
|
||||
TEST_DECL(test_wc_PemToDer),
|
||||
TEST_DECL(test_wc_AllocDer),
|
||||
|
||||
+224
-10
@@ -4647,6 +4647,17 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32* inOutI
|
||||
/* SLH-DSA-SHAKE-256f: 2.16.840.1.101.3.4.3.31 */
|
||||
static const byte sigSlhDsa_Shake_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 31};
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
/* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
|
||||
static const byte sigHssLmsOid[] =
|
||||
{42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
/* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
|
||||
static const byte sigXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
|
||||
/* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
|
||||
static const byte sigXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
|
||||
/* keyType */
|
||||
#ifndef NO_DSA
|
||||
@@ -4978,6 +4989,17 @@ static int SlhDsaParamToKeyType(enum SlhDsaParam param)
|
||||
}
|
||||
#endif /* WOLFSSL_CERT_GEN */
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
/* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
|
||||
static const byte keyHssLmsOid[] =
|
||||
{42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
/* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
|
||||
static const byte keyXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
|
||||
/* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
|
||||
static const byte keyXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
|
||||
/* curveType */
|
||||
#ifdef HAVE_ECC
|
||||
@@ -5870,6 +5892,22 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz)
|
||||
*oidSz = sizeof(sigSlhDsa_Shake_256fOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case CTC_HSS_LMS:
|
||||
oid = sigHssLmsOid;
|
||||
*oidSz = sizeof(sigHssLmsOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case CTC_XMSS:
|
||||
oid = sigXmssOid;
|
||||
*oidSz = sizeof(sigXmssOid);
|
||||
break;
|
||||
case CTC_XMSSMT:
|
||||
oid = sigXmssMtOid;
|
||||
*oidSz = sizeof(sigXmssMtOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -6019,6 +6057,22 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz)
|
||||
*oidSz = sizeof(keySlhDsa_Shake_256fOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
oid = keyHssLmsOid;
|
||||
*oidSz = sizeof(keyHssLmsOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
oid = keyXmssOid;
|
||||
*oidSz = sizeof(keyXmssOid);
|
||||
break;
|
||||
case XMSSMTk:
|
||||
oid = keyXmssMtOid;
|
||||
*oidSz = sizeof(keyXmssMtOid);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -12465,7 +12519,8 @@ void wc_FreeDecodedCert(DecodedCert* cert)
|
||||
}
|
||||
|
||||
#if defined(HAVE_ED25519) || defined(HAVE_ED448) || defined(HAVE_FALCON) || \
|
||||
defined(HAVE_DILITHIUM) || defined(WOLFSSL_HAVE_SLHDSA)
|
||||
defined(HAVE_DILITHIUM) || defined(WOLFSSL_HAVE_SLHDSA) || \
|
||||
defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
||||
/* Store the key data under the BIT_STRING in dynamically allocated data.
|
||||
*
|
||||
* @param [in, out] cert Certificate object.
|
||||
@@ -13386,6 +13441,22 @@ static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx,
|
||||
ret = StoreKey(cert, source, &srcIdx, maxIdx);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
cert->pkCurveOID = HSS_LMSk;
|
||||
ret = StoreKey(cert, source, &srcIdx, maxIdx);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
cert->pkCurveOID = XMSSk;
|
||||
ret = StoreKey(cert, source, &srcIdx, maxIdx);
|
||||
break;
|
||||
case XMSSMTk:
|
||||
cert->pkCurveOID = XMSSMTk;
|
||||
ret = StoreKey(cert, source, &srcIdx, maxIdx);
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
#ifndef NO_DSA
|
||||
case DSAk:
|
||||
cert->publicKey = source + pubIdx;
|
||||
@@ -15805,14 +15876,16 @@ static WC_INLINE int IsSigAlgoECDSA(word32 algoOID)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Determines if OID is for an EC signing algorithm including ECDSA and EdDSA
|
||||
* and post-quantum algorithms.
|
||||
/* Determines whether the signature algorithm's AlgorithmIdentifier omits
|
||||
* the trailing NULL parameters element. True for ECC / EdDSA / SM2 and
|
||||
* for the post-quantum families (Falcon, ML-DSA , SLH-DSA, LMS, XMSS).
|
||||
*
|
||||
* @param [in] algoOID Algorithm OID.
|
||||
* @return 1 when is EC signing algorithm.
|
||||
* @return 1 when the algorithm encodes its AlgorithmIdentifier without
|
||||
* a NULL parameters element.
|
||||
* @return 0 otherwise.
|
||||
*/
|
||||
static WC_INLINE int IsSigAlgoECC(word32 algoOID)
|
||||
static WC_INLINE int IsSigAlgoNoParams(word32 algoOID)
|
||||
{
|
||||
(void)algoOID;
|
||||
|
||||
@@ -15863,6 +15936,13 @@ static WC_INLINE int IsSigAlgoECC(word32 algoOID)
|
||||
|| (algoOID == SLH_DSA_SHA2_192Sk)
|
||||
|| (algoOID == SLH_DSA_SHA2_256Sk)
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
|| (algoOID == HSS_LMSk)
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
|| (algoOID == XMSSk)
|
||||
|| (algoOID == XMSSMTk)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15907,7 +15987,7 @@ static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
|
||||
SetASN_OID(&dataASN[ALGOIDASN_IDX_OID], (word32)algoOID, (word32)type);
|
||||
/* Hashes, signatures not ECC and keys not RSA output NULL tag. */
|
||||
if (!(type == oidHashType ||
|
||||
(type == oidSigType && !IsSigAlgoECC((word32)algoOID)) ||
|
||||
(type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
|
||||
(type == oidKeyType && algoOID == RSAk))) {
|
||||
/* Don't put out NULL DER item. */
|
||||
dataASN[ALGOIDASN_IDX_NULL].noOut = 1;
|
||||
@@ -16218,6 +16298,25 @@ void FreeSignatureCtx(SignatureCtx* sigCtx)
|
||||
#endif
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
wc_LmsKey_Free(sigCtx->key.lms);
|
||||
#ifndef WOLFSSL_NO_MALLOC
|
||||
XFREE(sigCtx->key.lms, sigCtx->heap, DYNAMIC_TYPE_LMS);
|
||||
sigCtx->key.lms = NULL;
|
||||
#endif
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
case XMSSMTk:
|
||||
wc_XmssKey_Free(sigCtx->key.xmss);
|
||||
#ifndef WOLFSSL_NO_MALLOC
|
||||
XFREE(sigCtx->key.xmss, sigCtx->heap, DYNAMIC_TYPE_XMSS);
|
||||
sigCtx->key.xmss = NULL;
|
||||
#endif
|
||||
break;
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
break;
|
||||
} /* switch (keyOID) */
|
||||
@@ -16416,6 +16515,17 @@ static int HashForSignature(const byte* buf, word32 bufSz, word32 sigOID,
|
||||
/* Hashes done in signing operation. */
|
||||
break;
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case CTC_HSS_LMS:
|
||||
/* RFC 9802 sec 2: no digest is applied before signing. */
|
||||
break;
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case CTC_XMSS:
|
||||
case CTC_XMSSMT:
|
||||
/* RFC 9802 sec 2: no digest is applied before signing. */
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = HASH_TYPE_E;
|
||||
@@ -16613,6 +16723,16 @@ static int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID)
|
||||
case SLH_DSA_SHA2_256Sk:
|
||||
return (sigOID == CTC_SLH_DSA_SHA2_256S);
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
return (sigOID == CTC_HSS_LMS);
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
return (sigOID == CTC_XMSS);
|
||||
case XMSSMTk:
|
||||
return (sigOID == CTC_XMSSMT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Default to reject unknown key types */
|
||||
@@ -17141,6 +17261,54 @@ int ConfirmSignature(SignatureCtx* sigCtx,
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
{
|
||||
sigCtx->verify = 0;
|
||||
#ifndef WOLFSSL_NO_MALLOC
|
||||
sigCtx->key.lms = (LmsKey*)XMALLOC(sizeof(LmsKey),
|
||||
sigCtx->heap, DYNAMIC_TYPE_LMS);
|
||||
if (sigCtx->key.lms == NULL) {
|
||||
ERROR_OUT(MEMORY_E, exit_cs);
|
||||
}
|
||||
#endif
|
||||
if ((ret = wc_LmsKey_Init(sigCtx->key.lms,
|
||||
sigCtx->heap, sigCtx->devId)) < 0) {
|
||||
goto exit_cs;
|
||||
}
|
||||
if ((ret = wc_LmsKey_ImportPubRaw(sigCtx->key.lms,
|
||||
key, keySz)) < 0) {
|
||||
WOLFSSL_MSG("ASN Key import error HSS/LMS");
|
||||
goto exit_cs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
case XMSSMTk:
|
||||
{
|
||||
int is_xmssmt = (keyOID == XMSSMTk);
|
||||
sigCtx->verify = 0;
|
||||
#ifndef WOLFSSL_NO_MALLOC
|
||||
sigCtx->key.xmss = (XmssKey*)XMALLOC(sizeof(XmssKey),
|
||||
sigCtx->heap, DYNAMIC_TYPE_XMSS);
|
||||
if (sigCtx->key.xmss == NULL) {
|
||||
ERROR_OUT(MEMORY_E, exit_cs);
|
||||
}
|
||||
#endif
|
||||
if ((ret = wc_XmssKey_Init(sigCtx->key.xmss,
|
||||
sigCtx->heap, sigCtx->devId)) < 0) {
|
||||
goto exit_cs;
|
||||
}
|
||||
if ((ret = wc_XmssKey_ImportPubRaw_ex(sigCtx->key.xmss,
|
||||
key, keySz, is_xmssmt)) < 0) {
|
||||
WOLFSSL_MSG("ASN Key import error XMSS/XMSS^MT");
|
||||
goto exit_cs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
WOLFSSL_MSG("Verify Key type unknown");
|
||||
ret = ASN_UNKNOWN_OID_E;
|
||||
@@ -17353,6 +17521,25 @@ int ConfirmSignature(SignatureCtx* sigCtx,
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
{
|
||||
ret = wc_LmsKey_Verify(sigCtx->key.lms, sig, sigSz,
|
||||
buf, (int)bufSz);
|
||||
sigCtx->verify = (ret == 0);
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
case XMSSMTk:
|
||||
{
|
||||
ret = wc_XmssKey_Verify(sigCtx->key.xmss, sig, sigSz,
|
||||
buf, (int)bufSz);
|
||||
sigCtx->verify = (ret == 0);
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
break;
|
||||
} /* switch (keyOID) */
|
||||
@@ -17575,6 +17762,33 @@ int ConfirmSignature(SignatureCtx* sigCtx,
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_SLHDSA */
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
case HSS_LMSk:
|
||||
{
|
||||
if (sigCtx->verify == 1) {
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
WOLFSSL_MSG("HSS/LMS Verify didn't match");
|
||||
ret = ASN_SIG_CONFIRM_E;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
case XMSSk:
|
||||
case XMSSMTk:
|
||||
{
|
||||
if (sigCtx->verify == 1) {
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
WOLFSSL_MSG("XMSS/XMSS^MT Verify didn't match");
|
||||
ret = ASN_SIG_CONFIRM_E;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_XMSS */
|
||||
default:
|
||||
break;
|
||||
} /* switch (keyOID) */
|
||||
@@ -21237,7 +21451,7 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
|
||||
ret = ASN_SIG_OID_E;
|
||||
}
|
||||
/* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
|
||||
else if (IsSigAlgoECC(cert->signatureOID)) {
|
||||
else if (IsSigAlgoNoParams(cert->signatureOID)) {
|
||||
#ifndef WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED
|
||||
if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS_NULL].tag != 0) {
|
||||
WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
|
||||
@@ -27917,7 +28131,7 @@ int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz,
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (IsSigAlgoECC((word32)sigAlgoType)) {
|
||||
if (IsSigAlgoNoParams((word32)sigAlgoType)) {
|
||||
/* ECDSA and EdDSA doesn't have NULL tagged item. */
|
||||
dataASN[SIGASN_IDX_SIGALGO_NULL].noOut = 1;
|
||||
}
|
||||
@@ -28128,7 +28342,7 @@ static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
|
||||
(word32)cert->sigType, oidSigType);
|
||||
}
|
||||
|
||||
if (IsSigAlgoECC((word32)cert->sigType)) {
|
||||
if (IsSigAlgoNoParams((word32)cert->sigType)) {
|
||||
/* No NULL tagged item with ECDSA and EdDSA signature OIDs. */
|
||||
dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
|
||||
}
|
||||
@@ -37972,7 +38186,7 @@ int ParseX509Acert(DecodedAcert* acert, int verify)
|
||||
}
|
||||
|
||||
/* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
|
||||
if (IsSigAlgoECC(acert->signatureOID)) {
|
||||
if (IsSigAlgoNoParams(acert->signatureOID)) {
|
||||
if ((dataASN[ACERT_IDX_SIGALGO_PARAMS_NULL].tag != 0)
|
||||
#ifdef WC_RSA_PSS
|
||||
|| (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0)
|
||||
|
||||
@@ -2947,7 +2947,7 @@ static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
|
||||
word32 length = 0;
|
||||
|
||||
tagSz = ((type == oidHashType ||
|
||||
(type == oidSigType && !IsSigAlgoECC((word32)algoOID)) ||
|
||||
(type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
|
||||
(type == oidKeyType && algoOID == RSAk)) &&
|
||||
(absentParams == FALSE)) ? 2U : 0U;
|
||||
algoName = OidFromId((word32)algoOID, (word32)type, &algoSz);
|
||||
|
||||
+105
-16
@@ -1418,39 +1418,127 @@ int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen)
|
||||
|
||||
/* Imports a raw public key buffer from in array to LmsKey key.
|
||||
*
|
||||
* The LMS parameters must be set first with wc_LmsKey_SetLmsParm or
|
||||
* wc_LmsKey_SetParameters, and inLen must match the length returned
|
||||
* by wc_LmsKey_GetPubLen.
|
||||
* If the LMS parameters have already been configured (via
|
||||
* wc_LmsKey_SetLmsParm or wc_LmsKey_SetParameters), the levels /
|
||||
* lms_algorithm_type / lmots_algorithm_type encoded in the raw key are
|
||||
* checked for consistency and inLen must match wc_LmsKey_GetPubLen.
|
||||
*
|
||||
* Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
|
||||
* If the parameters have not yet been set (key->params == NULL), they
|
||||
* are derived from the raw public key prefix (RFC 8554 sec 3.3 / sec
|
||||
* 6.1: u32str(L) || lms_algorithm_type || lmots_algorithm_type) and
|
||||
* matched against the static parameter map. The candidate is held in
|
||||
* a local until the length check passes, so a length mismatch leaves
|
||||
* key->params NULL.
|
||||
*
|
||||
* Accepts a key in INITED, PARMSET or VERIFYONLY state. WC_LMS_STATE_OK
|
||||
* is rejected because the key already has private material loaded and
|
||||
* silently overwriting key->pub would create an inconsistent priv/pub
|
||||
* pair.
|
||||
*
|
||||
* @param [in, out] key LMS key to put public key in.
|
||||
* @param [in] in Buffer holding encoded public key.
|
||||
* @param [in] inLen Length of encoded public key in bytes.
|
||||
* @return 0 on success.
|
||||
* @return BAD_FUNC_ARG when key or in is NULL.
|
||||
* @return BUFFER_E when inLen does not match public key length by parameters.
|
||||
* @return BAD_FUNC_ARG when key or in is NULL, or when the raw key's
|
||||
* levels / lmsType / lmOtsType disagree with pre-set params.
|
||||
* @return BAD_STATE_E when wrong state for operation.
|
||||
* @return BUFFER_E when inLen is too small to contain the LMS type
|
||||
* fields, or doesn't match the public key length determined
|
||||
* by parameters.
|
||||
* @return NOT_COMPILED_IN when the derived parameter set isn't built in.
|
||||
*/
|
||||
int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret = 0;
|
||||
const LmsParams* matched = NULL;
|
||||
|
||||
/* Validate parameters. */
|
||||
if ((key == NULL) || (in == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
/* Reject states where re-importing the public bytes would desync
|
||||
* the key. INITED (params unset, will derive), PARMSET (params set
|
||||
* but no private material) and VERIFYONLY (already a pub-only key)
|
||||
* are all safe. OK means a private key is loaded; silently
|
||||
* overwriting key->pub would create a priv/pub mismatch. Mirrors
|
||||
* the wc_XmssKey_ImportPubRaw_ex post-condition added in the same
|
||||
* RFC 9802 series. */
|
||||
if ((ret == 0) &&
|
||||
(inLen != (word32)HSS_PUBLIC_KEY_LEN(key->params->hash_len))) {
|
||||
/* Something inconsistent. Parameters weren't set, or input
|
||||
* pub key is wrong.*/
|
||||
return BUFFER_E;
|
||||
(key->state != WC_LMS_STATE_INITED) &&
|
||||
(key->state != WC_LMS_STATE_PARMSET) &&
|
||||
(key->state != WC_LMS_STATE_VERIFYONLY)) {
|
||||
WOLFSSL_MSG("error: LMS key not ready for import");
|
||||
ret = BAD_STATE_E;
|
||||
}
|
||||
/* Need at least L || lmsType || lmOtsType to derive or validate. */
|
||||
if ((ret == 0) && (inLen < (word32)(LMS_L_LEN + 2 * LMS_TYPE_LEN))) {
|
||||
ret = BUFFER_E;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
XMEMCPY(key->pub, in, inLen);
|
||||
word32 levels = 0;
|
||||
word32 lmsType = 0;
|
||||
word32 lmOtsType = 0;
|
||||
|
||||
if (key->state != WC_LMS_STATE_OK)
|
||||
key->state = WC_LMS_STATE_VERIFYONLY;
|
||||
/* RFC 8554 sec 3.3 / sec 6.1: HSS public key = u32str(L) || pub[0],
|
||||
* where pub[0] starts with lms_algorithm_type || lmots_algorithm_type.
|
||||
*/
|
||||
ato32(in + 0, &levels);
|
||||
ato32(in + LMS_L_LEN, &lmsType);
|
||||
ato32(in + LMS_L_LEN + LMS_TYPE_LEN, &lmOtsType);
|
||||
|
||||
/* The wire format carries only the RFC type code (low 12 bits);
|
||||
* params->lmsType / lmOtsType also pack a wolfSSL-internal hash
|
||||
* family flag in the high 4 bits (LMS_HASH_MASK). Compare on the
|
||||
* RFC code only -- safe as long as low-12-bit codes stay globally
|
||||
* distinct across hash families (see wc_lms_impl.c step 3.d-e
|
||||
* note). */
|
||||
if (key->params == NULL) {
|
||||
/* Auto-derive: find matching entry in the static map. Hold
|
||||
* the candidate in a local until the length check passes to
|
||||
* avoid leaving key->params half-set on failure. */
|
||||
int i;
|
||||
ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
|
||||
for (i = 0; i < WC_LMS_MAP_LEN; i++) {
|
||||
if (((word32)wc_lms_map[i].params.levels == levels) &&
|
||||
((word32)(wc_lms_map[i].params.lmsType & LMS_H_W_MASK) ==
|
||||
lmsType) &&
|
||||
((word32)(wc_lms_map[i].params.lmOtsType& LMS_H_W_MASK) ==
|
||||
lmOtsType)) {
|
||||
matched = &wc_lms_map[i].params;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret != 0) {
|
||||
WOLFSSL_MSG("error: LMS params from pub key not supported");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Validate against pre-set params. */
|
||||
if (((word32)key->params->levels != levels) ||
|
||||
((word32)(key->params->lmsType & LMS_H_W_MASK) != lmsType) ||
|
||||
((word32)(key->params->lmOtsType & LMS_H_W_MASK) != lmOtsType)){
|
||||
WOLFSSL_MSG("error: LMS pub key doesn't match set params");
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
matched = key->params;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((ret == 0) &&
|
||||
(inLen != (word32)HSS_PUBLIC_KEY_LEN(matched->hash_len))) {
|
||||
ret = BUFFER_E;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* Commit params (no-op when already set) and copy the key.
|
||||
* State is INITED/PARMSET/VERIFYONLY here (OK is rejected
|
||||
* above), so promoting to VERIFYONLY is always correct. */
|
||||
key->params = matched;
|
||||
XMEMCPY(key->pub, in, inLen);
|
||||
key->state = WC_LMS_STATE_VERIFYONLY;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1465,14 +1553,15 @@ int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
|
||||
* @param [in] key LMS key.
|
||||
* @param [out] len Length of a signature in bytes.
|
||||
* @return 0 on success.
|
||||
* @return BAD_FUNC_ARG when key or len is NULL.
|
||||
* @return BAD_FUNC_ARG when key or len is NULL, or when the LMS
|
||||
* parameters have not been configured on the key.
|
||||
*/
|
||||
int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Validate parameters. */
|
||||
if ((key == NULL) || (len == NULL)) {
|
||||
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
|
||||
+155
-6
@@ -1493,10 +1493,156 @@ int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out, word32* outLen)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Imports a raw public key buffer from in array to XmssKey key, taking
|
||||
* an is_xmssmt hint to disambiguate the XMSS / XMSS^MT OID namespaces
|
||||
* when params have not yet been configured on the key.
|
||||
*
|
||||
* Accepts a key in INITED, PARMSET or VERIFYONLY state. WC_XMSS_STATE_OK
|
||||
* is rejected because the key already has private material loaded and
|
||||
* silently overwriting key->pk would create an inconsistent priv/pub
|
||||
* pair. When state is INITED, params are derived from the 4-byte OID
|
||||
* prefix at the start of the raw key (RFC 8391 Appendix B.1 / C.1)
|
||||
* using is_xmssmt to pick the XMSS or XMSS^MT table; key->oid,
|
||||
* key->is_xmssmt and key->params are populated only after the public
|
||||
* key length check passes, so a length mismatch leaves the key in its
|
||||
* original state. When params have already been set, the 4-byte OID
|
||||
* prefix and the is_xmssmt hint are checked for consistency.
|
||||
*
|
||||
* @param [in, out] key XMSS key.
|
||||
* @param [in] in Array holding public key.
|
||||
* @param [in] inLen Length of array in bytes.
|
||||
* @param [in] is_xmssmt 0 to search the XMSS table, non-zero to
|
||||
* search the XMSS^MT table.
|
||||
*
|
||||
* @return 0 on success.
|
||||
* @return BAD_FUNC_ARG when a parameter is NULL or the OID prefix /
|
||||
* is_xmssmt hint contradicts pre-set params.
|
||||
* @return BUFFER_E if array is incorrect size.
|
||||
* @return BAD_STATE_E when wrong state for operation.
|
||||
* @return NOT_COMPILED_IN when the derived parameter set isn't built in.
|
||||
*/
|
||||
int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
|
||||
int is_xmssmt)
|
||||
{
|
||||
int ret = 0;
|
||||
word32 oid = 0;
|
||||
const XmssParams* matched = NULL;
|
||||
|
||||
/* Validate parameters. */
|
||||
if ((key == NULL) || (in == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
if ((ret == 0) && (inLen < XMSS_OID_LEN)) {
|
||||
ret = BUFFER_E;
|
||||
}
|
||||
|
||||
/* Reject states where the key is unusable for re-import. INITED
|
||||
* means params are unset (we'll derive them); PARMSET / VERIFYONLY
|
||||
* means params are set without a working private key (we just
|
||||
* overwrite the pub bytes). OK means a private key is already
|
||||
* loaded; overwriting key->pk silently would desync priv/pub. */
|
||||
if ((ret == 0) &&
|
||||
(key->state != WC_XMSS_STATE_INITED) &&
|
||||
(key->state != WC_XMSS_STATE_PARMSET) &&
|
||||
(key->state != WC_XMSS_STATE_VERIFYONLY)) {
|
||||
WOLFSSL_MSG("error: XMSS key not ready for import");
|
||||
ret = BAD_STATE_E;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* OID is encoded big-endian in the first 4 bytes. */
|
||||
ato32(in, &oid);
|
||||
|
||||
if (key->state == WC_XMSS_STATE_INITED) {
|
||||
/* Auto-derive params from OID prefix, using is_xmssmt hint.
|
||||
* Hold the candidate in a local; commit to the key only
|
||||
* after the length check below succeeds. The compile-time
|
||||
* gates here mirror the wc_xmss_alg / wc_xmssmt_alg table
|
||||
* definitions exactly, so a build with one family disabled
|
||||
* still rejects pubkeys for that family with NOT_COMPILED_IN
|
||||
* rather than referring to undefined WC_*_ALG_LEN. */
|
||||
ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
|
||||
if (is_xmssmt) {
|
||||
#if WOLFSSL_XMSS_MAX_HEIGHT >= 20
|
||||
unsigned int i;
|
||||
for (i = 0; i < WC_XMSSMT_ALG_LEN; i++) {
|
||||
if (wc_xmssmt_alg[i].oid == oid) {
|
||||
matched = &wc_xmssmt_alg[i].params;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* XMSS^MT disabled at compile time; ret stays at
|
||||
* NOT_COMPILED_IN. */
|
||||
(void)oid;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if WOLFSSL_XMSS_MIN_HEIGHT <= 20
|
||||
unsigned int i;
|
||||
for (i = 0; i < WC_XMSS_ALG_LEN; i++) {
|
||||
if (wc_xmss_alg[i].oid == oid) {
|
||||
matched = &wc_xmss_alg[i].params;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* XMSS disabled at compile time; ret stays at
|
||||
* NOT_COMPILED_IN. */
|
||||
(void)oid;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
WOLFSSL_MSG("error: XMSS OID from pub key not supported");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Params already set; OID prefix and family must match. */
|
||||
if (oid != key->oid) {
|
||||
WOLFSSL_MSG("error: XMSS pub OID doesn't match set params");
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else if ((is_xmssmt ? 1 : 0) != key->is_xmssmt) {
|
||||
WOLFSSL_MSG("error: XMSS is_xmssmt hint contradicts set params");
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
matched = key->params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Length check using the candidate (auto-derived) or pre-set
|
||||
* params, without committing yet. */
|
||||
if ((ret == 0) && (inLen != (word32)(XMSS_OID_LEN + matched->pk_len))) {
|
||||
ret = BUFFER_E;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* Commit (no-op when params were already set) and copy pub
|
||||
* bytes (skipping the OID prefix). */
|
||||
if (key->state == WC_XMSS_STATE_INITED) {
|
||||
key->params = matched;
|
||||
key->oid = oid;
|
||||
key->is_xmssmt = is_xmssmt ? 1 : 0;
|
||||
}
|
||||
XMEMCPY(key->pk, in + XMSS_OID_LEN, matched->pk_len);
|
||||
key->state = WC_XMSS_STATE_VERIFYONLY;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Imports a raw public key buffer from in array to XmssKey key.
|
||||
*
|
||||
* The XMSS parameters must be set first with wc_XmssKey_SetParamStr,
|
||||
* and inLen must match the length returned by wc_XmssKey_GetPubLen.
|
||||
* If the caller only has the raw public-key bytes and has not yet
|
||||
* configured the parameter set, use wc_XmssKey_ImportPubRaw_ex which
|
||||
* derives parameters from the OID prefix at the start of the buffer.
|
||||
*
|
||||
* @param [in, out] key XMSS key.
|
||||
* @param [in] in Array holding public key.
|
||||
@@ -1565,12 +1711,13 @@ int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len)
|
||||
int ret = 0;
|
||||
|
||||
/* Validate parameters. */
|
||||
if ((key == NULL) || (len == NULL)) {
|
||||
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
/* Validate state. */
|
||||
if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) &&
|
||||
(key->state != WC_XMSS_STATE_PARMSET)) {
|
||||
(key->state != WC_XMSS_STATE_PARMSET) &&
|
||||
(key->state != WC_XMSS_STATE_VERIFYONLY)) {
|
||||
ret = BAD_STATE_E;
|
||||
}
|
||||
|
||||
@@ -1601,7 +1748,8 @@ int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len)
|
||||
* @return SIG_VERIFY_E when signature did not verify message.
|
||||
* @return BAD_FUNC_ARG when a parameter is NULL.
|
||||
* @return BAD_STATE_E when wrong state for operation.
|
||||
* @return BUFFER_E when sigLen is too small.
|
||||
* @return BUFFER_E when sigLen does not exactly match the parameter-set
|
||||
* signature length (use wc_XmssKey_GetSigLen).
|
||||
*/
|
||||
int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
|
||||
const byte* m, int mLen)
|
||||
@@ -1620,9 +1768,10 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
|
||||
WOLFSSL_MSG("error: XMSS key not ready for verification");
|
||||
ret = BAD_STATE_E;
|
||||
}
|
||||
/* Check the signature is the big enough. */
|
||||
if ((ret == 0) && (sigLen < key->params->sig_len)) {
|
||||
/* Signature buffer too small. */
|
||||
/* Check the signature length is exactly the parameter-set size.
|
||||
* XMSS / XMSS^MT signatures are fixed-length per parameter set, so
|
||||
* any buffer that's longer or shorter than sig_len is malformed. */
|
||||
if ((ret == 0) && (sigLen != key->params->sig_len)) {
|
||||
ret = BUFFER_E;
|
||||
}
|
||||
|
||||
|
||||
+26
-3
@@ -70,6 +70,12 @@ that can be serialized and deserialized in a cross-platform way.
|
||||
#ifdef WOLFSSL_HAVE_SLHDSA
|
||||
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#include <wolfssl/wolfcrypt/wc_lms.h>
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
#include <wolfssl/wolfcrypt/wc_xmss.h>
|
||||
#endif
|
||||
#ifdef HAVE_FALCON
|
||||
#include <wolfssl/wolfcrypt/falcon.h>
|
||||
#endif
|
||||
@@ -1542,7 +1548,8 @@ struct SignatureCtx {
|
||||
#endif
|
||||
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
|
||||
!defined(NO_DSA) || defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
|
||||
defined(WOLFSSL_HAVE_SLHDSA)
|
||||
defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_LMS) || \
|
||||
defined(WOLFSSL_HAVE_XMSS)
|
||||
int verify;
|
||||
#endif
|
||||
union {
|
||||
@@ -1602,6 +1609,20 @@ struct SignatureCtx {
|
||||
SlhDsaKey* slhdsa;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#ifdef WOLFSSL_NO_MALLOC
|
||||
LmsKey lms[1];
|
||||
#else
|
||||
LmsKey* lms;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_XMSS
|
||||
#ifdef WOLFSSL_NO_MALLOC
|
||||
XmssKey xmss[1];
|
||||
#else
|
||||
XmssKey* xmss;
|
||||
#endif
|
||||
#endif
|
||||
#ifndef WOLFSSL_NO_MALLOC
|
||||
void* ptr;
|
||||
#endif
|
||||
@@ -1864,13 +1885,15 @@ struct DecodedCert {
|
||||
|
||||
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
|
||||
defined(HAVE_DILITHIUM) || defined(HAVE_FALCON) || \
|
||||
defined(WOLFSSL_HAVE_SLHDSA)
|
||||
defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_LMS) || \
|
||||
defined(WOLFSSL_HAVE_XMSS)
|
||||
word32 pkCurveOID; /* Public Key's curve OID */
|
||||
#ifdef WOLFSSL_CUSTOM_CURVES
|
||||
int pkCurveSize; /* Public Key's curve size */
|
||||
#endif
|
||||
#endif /* HAVE_ECC || HAVE_ED25519 || HAVE_ED448 || HAVE_DILITHIUM ||
|
||||
* HAVE_FALCON || WOLFSSL_HAVE_SLHDSA */
|
||||
* HAVE_FALCON || WOLFSSL_HAVE_SLHDSA || WOLFSSL_HAVE_LMS ||
|
||||
* WOLFSSL_HAVE_XMSS */
|
||||
const byte* beforeDate;
|
||||
int beforeDateLen;
|
||||
const byte* afterDate;
|
||||
|
||||
@@ -219,7 +219,13 @@ enum Key_Sum {
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
|
||||
SLH_DSA_SHAKE_256Sk = 444, /* 2.16.840.1.101.3.4.3.30 */
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
|
||||
SLH_DSA_SHAKE_256Fk = 445 /* 2.16.840.1.101.3.4.3.31 */
|
||||
SLH_DSA_SHAKE_256Fk = 445, /* 2.16.840.1.101.3.4.3.31 */
|
||||
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
|
||||
HSS_LMSk = 688, /* 1.2.840.113549.1.9.16.3.17 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
|
||||
XMSSk = 107, /* 1.3.6.1.5.5.7.6.34 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
|
||||
XMSSMTk = 108 /* 1.3.6.1.5.5.7.6.35 */
|
||||
#else
|
||||
/* 0x00 */
|
||||
ANONk = 0x7fffffff, /* 0.0 */
|
||||
@@ -284,7 +290,13 @@ enum Key_Sum {
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
|
||||
SLH_DSA_SHAKE_256Sk = 0x7db37ae4, /* 2.16.840.1.101.3.4.3.30 */
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
|
||||
SLH_DSA_SHAKE_256Fk = 0x7db37ae5 /* 2.16.840.1.101.3.4.3.31 */
|
||||
SLH_DSA_SHAKE_256Fk = 0x7db37ae5, /* 2.16.840.1.101.3.4.3.31 */
|
||||
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
|
||||
HSS_LMSk = 0x70a78832, /* 1.2.840.113549.1.9.16.3.17 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
|
||||
XMSSk = 0x2707012e, /* 1.3.6.1.5.5.7.6.34 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
|
||||
XMSSMTk = 0x2607012e /* 1.3.6.1.5.5.7.6.35 */
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1627,7 +1639,13 @@ enum Ctc_SigType {
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
|
||||
CTC_SLH_DSA_SHAKE_256S = 444, /* 2.16.840.1.101.3.4.3.30 */
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
|
||||
CTC_SLH_DSA_SHAKE_256F = 445 /* 2.16.840.1.101.3.4.3.31 */
|
||||
CTC_SLH_DSA_SHAKE_256F = 445, /* 2.16.840.1.101.3.4.3.31 */
|
||||
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
|
||||
CTC_HSS_LMS = 688, /* 1.2.840.113549.1.9.16.3.17 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
|
||||
CTC_XMSS = 107, /* 1.3.6.1.5.5.7.6.34 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
|
||||
CTC_XMSSMT = 108 /* 1.3.6.1.5.5.7.6.35 */
|
||||
#else
|
||||
/* 0x2a,0x86,0x48,0xce,0x38,0x04,0x03 */
|
||||
CTC_SHAwDSA = 0x314b8212, /* 1.2.840.10040.4.3 */
|
||||
@@ -1720,7 +1738,13 @@ enum Ctc_SigType {
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1e */
|
||||
CTC_SLH_DSA_SHAKE_256S = 0x7db37ae4, /* 2.16.840.1.101.3.4.3.30 */
|
||||
/* 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,0x1f */
|
||||
CTC_SLH_DSA_SHAKE_256F = 0x7db37ae5 /* 2.16.840.1.101.3.4.3.31 */
|
||||
CTC_SLH_DSA_SHAKE_256F = 0x7db37ae5, /* 2.16.840.1.101.3.4.3.31 */
|
||||
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x03,0x11 */
|
||||
CTC_HSS_LMS = 0x70a78832, /* 1.2.840.113549.1.9.16.3.17 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x22 */
|
||||
CTC_XMSS = 0x2707012e, /* 1.3.6.1.5.5.7.6.34 */
|
||||
/* 0x2b,0x06,0x01,0x05,0x05,0x07,0x06,0x23 */
|
||||
CTC_XMSSMT = 0x2607012e /* 1.3.6.1.5.5.7.6.35 */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -1381,6 +1381,7 @@ enum {
|
||||
DYNAMIC_TYPE_SHA = 106,
|
||||
DYNAMIC_TYPE_SLHDSA = 107,
|
||||
DYNAMIC_TYPE_OCSP_RESPONSE = 108,
|
||||
DYNAMIC_TYPE_XMSS = 109,
|
||||
DYNAMIC_TYPE_SNIFFER_SERVER = 1000,
|
||||
DYNAMIC_TYPE_SNIFFER_SESSION = 1001,
|
||||
DYNAMIC_TYPE_SNIFFER_PB = 1002,
|
||||
|
||||
@@ -427,6 +427,8 @@ WOLFSSL_API int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out,
|
||||
word32* outLen);
|
||||
WOLFSSL_API int wc_XmssKey_ImportPubRaw(XmssKey* key, const byte* in,
|
||||
word32 inLen);
|
||||
WOLFSSL_API int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in,
|
||||
word32 inLen, int is_xmssmt);
|
||||
WOLFSSL_API int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigSz,
|
||||
const byte* msg, int msgSz);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user