Skip to content

Commit 2ada881

Browse files
committed
RFC 9802: tighten ImportPubRaw_ex, doc fixes, more negative tests
Tightening * wc_XmssKey_ImportPubRaw_ex now rejects an is_xmssmt hint that contradicts a pre-set key->is_xmssmt with BAD_FUNC_ARG, instead of silently ignoring it. The OID-only check that already exists catches a wrong-numeric-OID case but cannot distinguish XMSS oid 1 from XMSS^MT oid 1. * The MIN/MAX_HEIGHT compile-time guards in the auto-derive branch now have explicit #else arms that document the dead-code path (and silence -Wunused on the local oid). Docs * Update doxygen on wc_LmsKey_ImportPubRaw to describe the auto-derive behaviour and its return codes. * Update doxygen on wc_LmsKey_GetSigLen to mention the key->params == NULL precondition. * Cross-reference wc_XmssKey_ImportPubRaw_ex from the plain wc_XmssKey_ImportPubRaw doxygen. Tests * New PARMSET-mismatch test for wc_XmssKey_ImportPubRaw_ex covers both an OID-prefix mismatch and an is_xmssmt-hint mismatch (locks in the new tightening). * New partial-write invariant test for wc_LmsKey_ImportPubRaw builds a buffer with valid type bytes but truncated length, and asserts key->params is still NULL after the BUFFER_E return. * rfc9802_load_file gains a named RFC9802_TEST_MAX_CERT_SIZE constant, uses size_t for the XFREAD return compare, and the junk[] buffer in rfc9802_xmss_import_negative is XMEMSET-zeroed (project convention) instead of using {0}. https://claude.ai/code/session_01SnSQMb145Hkyyf7hQQQ8cq
1 parent 17b374c commit 2ada881

3 files changed

Lines changed: 86 additions & 14 deletions

File tree

tests/api.c

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35807,13 +35807,19 @@ int test_wc_LmsKey_reload_cache(void)
3580735807
* STRING, so the fixtures were produced with a small generator that
3580835808
* overrides the AlgorithmIdentifier and SPKI to match RFC 9802. */
3580935809
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
35810+
/* Sanity bound on a test fixture cert. The largest BC-generated
35811+
* fixture we ship (XMSS^MT 40/8) is ~19 KiB; 1 MiB is well above
35812+
* any realistic RFC 9802 cert and catches a wild XFTELL. */
35813+
#define RFC9802_TEST_MAX_CERT_SIZE (1 << 20)
35814+
3581035815
/* Load a whole file into a freshly-allocated buffer. Caller frees. */
3581135816
static int rfc9802_load_file(const char* path, byte** out, int* outLen)
3581235817
{
3581335818
EXPECT_DECLS;
35814-
XFILE f = XBADFILE;
35815-
long sz = 0;
35816-
byte* buf = NULL;
35819+
XFILE f = XBADFILE;
35820+
long sz = 0;
35821+
size_t got = 0;
35822+
byte* buf = NULL;
3581735823

3581835824
*out = NULL;
3581935825
*outLen = 0;
@@ -35824,11 +35830,12 @@ static int rfc9802_load_file(const char* path, byte** out, int* outLen)
3582435830
sz = XFTELL(f);
3582535831
(void)XFSEEK(f, 0, XSEEK_SET);
3582635832
ExpectIntGT(sz, 0);
35827-
ExpectIntLT(sz, 1 << 20); /* sanity: certs are smaller than 1 MiB */
35833+
ExpectIntLT(sz, RFC9802_TEST_MAX_CERT_SIZE);
3582835834
ExpectNotNull(buf = (byte*)XMALLOC((size_t)sz, NULL,
3582935835
DYNAMIC_TYPE_TMP_BUFFER));
3583035836
if (buf != NULL) {
35831-
ExpectIntEQ((long)XFREAD(buf, 1, (size_t)sz, f), sz);
35837+
got = XFREAD(buf, 1, (size_t)sz, f);
35838+
ExpectIntEQ(got, (size_t)sz);
3583235839
}
3583335840
XFCLOSE(f);
3583435841
*out = buf;
@@ -35984,6 +35991,25 @@ static int rfc9802_lms_import_negative(void)
3598435991
wc_LmsKey_Free(&key);
3598535992
}
3598635993

35994+
/* Partial-write invariant: a length mismatch after a successful
35995+
* auto-derive must leave key->params NULL. Build a buffer whose
35996+
* leading u32str(L) || lmsType || lmOtsType identifies a known
35997+
* parameter set, but truncate to one byte less than the real pub
35998+
* key length so the post-derive length check fails. */
35999+
{
36000+
byte truncated[59]; /* HSS_PUBLIC_KEY_LEN(32) is 60 */
36001+
XMEMSET(truncated, 0, sizeof(truncated));
36002+
truncated[3] = 1; /* L = 1 */
36003+
truncated[7] = 5; /* lmsType = LMS_SHA256_M32_H5 */
36004+
truncated[11] = 4; /* lmOtsType = LMOTS_SHA256_N32_W4 */
36005+
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0);
36006+
ExpectNull(key.params);
36007+
ExpectIntEQ(wc_LmsKey_ImportPubRaw(&key, truncated,
36008+
sizeof(truncated)), WC_NO_ERR_TRACE(BUFFER_E));
36009+
ExpectNull(key.params);
36010+
wc_LmsKey_Free(&key);
36011+
}
36012+
3598736013
return EXPECT_RESULT();
3598836014
}
3598936015
#endif
@@ -35993,7 +36019,9 @@ static int rfc9802_xmss_import_negative(void)
3599336019
{
3599436020
EXPECT_DECLS;
3599536021
XmssKey key;
35996-
byte junk[8] = {0};
36022+
byte junk[8];
36023+
36024+
XMEMSET(junk, 0, sizeof(junk));
3599736025

3599836026
/* Too-short buffer. */
3599936027
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
@@ -36029,6 +36057,26 @@ static int rfc9802_xmss_import_negative(void)
3602936057
wc_XmssKey_Free(&key);
3603036058
}
3603136059

36060+
/* Once params have been configured (state != INITED), the OID
36061+
* prefix in the raw key MUST match key->oid and is_xmssmt MUST
36062+
* match key->is_xmssmt. Set XMSS-SHA2_10_256 and feed a valid-
36063+
* sized buffer whose 4-byte OID prefix is bogus -> BAD_FUNC_ARG. */
36064+
{
36065+
byte mismatch[XMSS_SHA256_PUBLEN];
36066+
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, INVALID_DEVID), 0);
36067+
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
36068+
XMEMSET(mismatch, 0, sizeof(mismatch));
36069+
mismatch[3] = 0x77; /* nonsense OID */
36070+
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
36071+
sizeof(mismatch), 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
36072+
/* Same buffer with the correct OID, but is_xmssmt hint
36073+
* contradicts the configured family -> BAD_FUNC_ARG. */
36074+
mismatch[3] = 0x01; /* WC_XMSS_OID_SHA2_10_256 */
36075+
ExpectIntEQ(wc_XmssKey_ImportPubRaw_ex(&key, mismatch,
36076+
sizeof(mismatch), 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
36077+
wc_XmssKey_Free(&key);
36078+
}
36079+
3603236080
return EXPECT_RESULT();
3603336081
}
3603436082
#endif

wolfcrypt/src/wc_lms.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,18 +1418,28 @@ int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen)
14181418

14191419
/* Imports a raw public key buffer from in array to LmsKey key.
14201420
*
1421-
* The LMS parameters must be set first with wc_LmsKey_SetLmsParm or
1422-
* wc_LmsKey_SetParameters, and inLen must match the length returned
1423-
* by wc_LmsKey_GetPubLen.
1421+
* If the LMS parameters have already been configured (via
1422+
* wc_LmsKey_SetLmsParm or wc_LmsKey_SetParameters), the levels /
1423+
* lms_algorithm_type / lmots_algorithm_type encoded in the raw key are
1424+
* checked for consistency and inLen must match wc_LmsKey_GetPubLen.
14241425
*
1425-
* Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
1426+
* If the parameters have not yet been set (key->params == NULL), they
1427+
* are derived from the raw public key prefix (RFC 8554 sec 3.3 / sec
1428+
* 6.1: u32str(L) || lms_algorithm_type || lmots_algorithm_type) and
1429+
* matched against the static parameter map. The candidate is held in
1430+
* a local until the length check passes, so a length mismatch leaves
1431+
* key->params NULL.
14261432
*
14271433
* @param [in, out] key LMS key to put public key in.
14281434
* @param [in] in Buffer holding encoded public key.
14291435
* @param [in] inLen Length of encoded public key in bytes.
14301436
* @return 0 on success.
1431-
* @return BAD_FUNC_ARG when key or in is NULL.
1432-
* @return BUFFER_E when inLen does not match public key length by parameters.
1437+
* @return BAD_FUNC_ARG when key or in is NULL, or when the raw key's
1438+
* levels / lmsType / lmOtsType disagree with pre-set params.
1439+
* @return BUFFER_E when inLen is too small to contain the LMS type
1440+
* fields, or doesn't match the public key length determined
1441+
* by parameters.
1442+
* @return NOT_COMPILED_IN when the derived parameter set isn't built in.
14331443
*/
14341444
int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
14351445
{
@@ -1515,7 +1525,8 @@ int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
15151525
* @param [in] key LMS key.
15161526
* @param [out] len Length of a signature in bytes.
15171527
* @return 0 on success.
1518-
* @return BAD_FUNC_ARG when key or len is NULL.
1528+
* @return BAD_FUNC_ARG when key or len is NULL, or when the LMS
1529+
* parameters have not been configured on the key.
15191530
*/
15201531
int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len)
15211532
{

wolfcrypt/src/wc_xmss.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,9 @@ int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
15561556
break;
15571557
}
15581558
}
1559+
#else
1560+
/* XMSS^MT compiled out; ret stays at NOT_COMPILED_IN. */
1561+
(void)oid;
15591562
#endif
15601563
}
15611564
else {
@@ -1568,6 +1571,9 @@ int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
15681571
break;
15691572
}
15701573
}
1574+
#else
1575+
/* XMSS compiled out; ret stays at NOT_COMPILED_IN. */
1576+
(void)oid;
15711577
#endif
15721578
}
15731579

@@ -1581,11 +1587,15 @@ int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
15811587
}
15821588
}
15831589
else {
1584-
/* Params already set; OID prefix must match. */
1590+
/* Params already set; OID prefix and family must match. */
15851591
if (oid != key->oid) {
15861592
WOLFSSL_MSG("error: XMSS pub OID doesn't match set params");
15871593
ret = BAD_FUNC_ARG;
15881594
}
1595+
else if ((is_xmssmt ? 1 : 0) != (int)key->is_xmssmt) {
1596+
WOLFSSL_MSG("error: XMSS is_xmssmt hint contradicts set params");
1597+
ret = BAD_FUNC_ARG;
1598+
}
15891599
}
15901600
}
15911601

@@ -1612,6 +1622,9 @@ int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, word32 inLen,
16121622
*
16131623
* The XMSS parameters must be set first with wc_XmssKey_SetParamStr,
16141624
* and inLen must match the length returned by wc_XmssKey_GetPubLen.
1625+
* If the caller only has the raw public-key bytes and has not yet
1626+
* configured the parameter set, use wc_XmssKey_ImportPubRaw_ex which
1627+
* derives parameters from the OID prefix at the start of the buffer.
16151628
*
16161629
* @param [in, out] key XMSS key.
16171630
* @param [in] in Array holding public key.

0 commit comments

Comments
 (0)