mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
PR feedback: block GetKid when privKey is not set, add test case and entry for CI
This commit is contained in:
6
.github/workflows/cryptocb-only.yml
vendored
6
.github/workflows/cryptocb-only.yml
vendored
@@ -178,6 +178,12 @@ jobs:
|
||||
{"name": "lms-xmss-onlycb-no-swdev", "minutes": 4.0,
|
||||
"comment": "Both stripped without swdev, so nothing serves a key that has no device or whose device declines: test_wc_LmsKey_cb_only_no_device and test_wc_XmssKey_cb_only_no_device check that those operations, and Reload, fail with NO_VALID_DEVID. The tests that need a software LMS/XMSS are skipped. cryptocbutils=free builds the device check before the free dispatch, which swdev's WOLF_CRYPTO_CB_FIND compiles out.",
|
||||
"configure": ["--disable-swdev", "--enable-lms", "--enable-xmss", "--enable-cryptocbutils=free", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]},
|
||||
{"name": "lms-xmss-swdev", "minutes": 4.0,
|
||||
"comment": "LMS and XMSS with the software cores kept. swdev's WOLF_CRYPTO_CB_FIND still sends a key bound to no device to swdev, which keeps the private key, so lms_test checks that wc_LmsKey_GetKid fails rather than return a key ID that is not in memory.",
|
||||
"configure": ["--enable-lms", "--enable-xmss"]},
|
||||
{"name": "lms-xmss-only-pk", "minutes": 2.0,
|
||||
"comment": "LMS and XMSS stripped as the only public key algorithms, so they alone turn on swdev's public key dispatch. Every other entry also has RSA or ECC, which would hide a missing LMS/XMSS term in that guard: the build only warns and LMS/XMSS operations fail with NO_VALID_DEVID. cryptonly because TLS needs another public key algorithm; the base's ASN, PKCS#7, OCSP and CRL options need RSA or ECC, so they are off too.",
|
||||
"configure": ["--enable-cryptonly", "--disable-rsa", "--disable-ecc", "--disable-dh", "--disable-curve25519", "--disable-ed25519", "--disable-curve448", "--disable-ed448", "--disable-mlkem", "--disable-dilithium", "--disable-dtls", "--disable-dtls13", "--disable-asn", "--disable-pkcs7", "--disable-ocsp", "--disable-ocspstapling", "--disable-ocspstapling2", "--disable-crl", "--enable-lms", "--enable-xmss", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]},
|
||||
{"name": "shake-xof", "minutes": 4.0,
|
||||
"comment": "WOLF_CRYPTO_CB_SHAKE_XOF: swdev handles SHAKE absorb and squeeze. No ONLY_* strip exists for SHAKE, so software SHAKE stays in. ML-KEM and ML-DSA reach swdev_shake through WOLF_CRYPTO_CB_FIND, and cryptocb_test runs shake_cb_xof_test.",
|
||||
"configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_SHAKE_XOF"]},
|
||||
|
||||
@@ -363,6 +363,10 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng);
|
||||
taken to be device-backed and Reload is a no-op. With a read
|
||||
callback the software reload runs as usual.
|
||||
|
||||
WOLF_CRYPTO_CB_ONLY_LMS removes the software reload, so the read
|
||||
callback is never called and only the device-backed no-op
|
||||
succeeds.
|
||||
|
||||
A reloaded key holds no public key: neither arm populates it.
|
||||
Call wc_LmsKey_ImportPubRaw() on a separate key to verify, or keep
|
||||
the public key exported at generation time.
|
||||
@@ -371,6 +375,8 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng);
|
||||
\return BAD_FUNC_ARG if any required pointer is NULL.
|
||||
\return WC_LMS_RC_* mapped error if the read callback fails.
|
||||
\return IO_FAILED_E if the private key could not be read.
|
||||
\return NO_VALID_DEVID with WOLF_CRYPTO_CB_ONLY_LMS when the key is
|
||||
not device-backed.
|
||||
|
||||
\param [in,out] key Pointer to an LmsKey with parameters and read
|
||||
callback set.
|
||||
@@ -627,6 +633,8 @@ const char* wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm);
|
||||
|
||||
\return 0 on success.
|
||||
\return BAD_FUNC_ARG if any required pointer is NULL.
|
||||
\return BAD_STATE_E if the private key is not in memory, such as
|
||||
when a device keeps it.
|
||||
|
||||
\param [in,out] key Pointer to an LmsKey with a private key.
|
||||
\param [out] kid Receives a pointer to the I bytes.
|
||||
|
||||
@@ -275,6 +275,10 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng);
|
||||
callback the software reload runs as usual, which needs the write
|
||||
callback set too.
|
||||
|
||||
WOLF_CRYPTO_CB_ONLY_XMSS removes the software reload, so the read
|
||||
callback is never called and only the device-backed no-op
|
||||
succeeds.
|
||||
|
||||
A reloaded key holds no public key: neither arm populates it.
|
||||
Call wc_XmssKey_ImportPubRaw() on a separate key to verify, or keep
|
||||
the public key exported at generation time.
|
||||
@@ -283,6 +287,8 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng);
|
||||
\return BAD_FUNC_ARG if any required pointer is NULL.
|
||||
\return WC_XMSS_RC_* mapped error if the read callback fails.
|
||||
\return IO_FAILED_E if the private key could not be read.
|
||||
\return NO_VALID_DEVID with WOLF_CRYPTO_CB_ONLY_XMSS when the key is
|
||||
not device-backed.
|
||||
|
||||
\param [in,out] key Pointer to an XmssKey with parameters and
|
||||
read callback set.
|
||||
|
||||
@@ -444,6 +444,8 @@ int test_wc_LmsKey_reload_devid(void)
|
||||
word32 sigSz;
|
||||
byte pub[64];
|
||||
word32 pubSz = sizeof(pub);
|
||||
const byte* kid = NULL;
|
||||
word32 kidSz = 0;
|
||||
|
||||
/* Zero so cleanup is safe if an early alloc failure skips init. */
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
@@ -468,6 +470,8 @@ int test_wc_LmsKey_reload_devid(void)
|
||||
ExpectIntEQ(wc_LmsKey_Reload(&key), 0);
|
||||
/* The reload must have expanded the private key, not been skipped. */
|
||||
ExpectNotNull(key.priv_data);
|
||||
ExpectIntEQ(wc_LmsKey_GetKid(&key, &kid, &kidSz), 0);
|
||||
ExpectIntEQ(kidSz, WC_LMS_I_LEN);
|
||||
|
||||
/* Sign with the reloaded key and verify with a software-only key. */
|
||||
sigSz = sizeof(sig);
|
||||
@@ -488,6 +492,8 @@ int test_wc_LmsKey_reload_devid(void)
|
||||
ExpectIntEQ(test_lms_set_params(&hsmKey), 0);
|
||||
ExpectIntEQ(wc_LmsKey_Reload(&hsmKey), 0);
|
||||
ExpectNull(hsmKey.priv_data);
|
||||
ExpectIntEQ(wc_LmsKey_GetKid(&hsmKey, &kid, &kidSz),
|
||||
WC_NO_ERR_TRACE(BAD_STATE_E));
|
||||
|
||||
wc_LmsKey_Free(&hsmKey);
|
||||
wc_LmsKey_Free(&vkey);
|
||||
@@ -642,6 +648,8 @@ int test_wc_LmsKey_keygen_no_sigsleft(void)
|
||||
defined(WOLF_CRYPTO_CB)
|
||||
LmsKey key;
|
||||
WC_RNG rng;
|
||||
const byte* kid = NULL;
|
||||
word32 kidSz = 0;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
@@ -654,6 +662,9 @@ int test_wc_LmsKey_keygen_no_sigsleft(void)
|
||||
ExpectIntEQ(test_lms_set_params(&key), 0);
|
||||
ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
|
||||
ExpectIntEQ(key.state, WC_LMS_STATE_OK);
|
||||
/* The device keeps the private key, which contains the key ID. */
|
||||
ExpectIntEQ(wc_LmsKey_GetKid(&key, &kid, &kidSz),
|
||||
WC_NO_ERR_TRACE(BAD_STATE_E));
|
||||
|
||||
wc_LmsKey_Free(&key);
|
||||
wc_FreeRng(&rng);
|
||||
@@ -662,6 +673,17 @@ int test_wc_LmsKey_keygen_no_sigsleft(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLF_CRYPTO_CB_ONLY_LMS) && !defined(WOLFSSL_SWDEV) && \
|
||||
!defined(WOLFSSL_LMS_VERIFY_ONLY)
|
||||
static int test_lms_read_fail(byte* priv, word32 privSz, void* context)
|
||||
{
|
||||
(void)priv;
|
||||
(void)privSz;
|
||||
(void)context;
|
||||
return WC_LMS_RC_READ_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Callback-only LMS returns NO_VALID_DEVID when no device does the work. */
|
||||
int test_wc_LmsKey_cb_only_no_device(void)
|
||||
{
|
||||
@@ -692,6 +714,14 @@ int test_wc_LmsKey_cb_only_no_device(void)
|
||||
ExpectIntEQ(wc_LmsKey_Reload(&key), WC_NO_ERR_TRACE(NO_VALID_DEVID));
|
||||
wc_LmsKey_Free(&key);
|
||||
|
||||
/* A read callback skips the device no-op, and no software reload
|
||||
* exists to call it. */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0);
|
||||
ExpectIntEQ(test_lms_set_params(&key), 0);
|
||||
ExpectIntEQ(wc_LmsKey_SetReadCb(&key, test_lms_read_fail), 0);
|
||||
ExpectIntEQ(wc_LmsKey_Reload(&key), WC_NO_ERR_TRACE(NO_VALID_DEVID));
|
||||
wc_LmsKey_Free(&key);
|
||||
|
||||
/* The device makes the key, then declines sign, verify and sigs left. */
|
||||
ExpectIntEQ(wc_LmsKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0);
|
||||
ExpectIntEQ(test_lms_set_params(&key), 0);
|
||||
@@ -957,6 +987,18 @@ int test_wc_XmssKey_reload_devid_verify(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLF_CRYPTO_CB_ONLY_XMSS) && !defined(WOLFSSL_SWDEV) && \
|
||||
!defined(WOLFSSL_XMSS_VERIFY_ONLY) && defined(TEST_XMSS_H10_AVAILABLE)
|
||||
static enum wc_XmssRc test_xmss_read_fail(byte* priv, word32 privSz,
|
||||
void* context)
|
||||
{
|
||||
(void)priv;
|
||||
(void)privSz;
|
||||
(void)context;
|
||||
return WC_XMSS_RC_READ_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Same as test_wc_LmsKey_cb_only_no_device, for XMSS. */
|
||||
int test_wc_XmssKey_cb_only_no_device(void)
|
||||
{
|
||||
@@ -987,6 +1029,14 @@ int test_wc_XmssKey_cb_only_no_device(void)
|
||||
ExpectIntEQ(wc_XmssKey_Reload(&key), WC_NO_ERR_TRACE(NO_VALID_DEVID));
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
/* A read callback skips the device no-op, and no software reload
|
||||
* exists to call it. */
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
|
||||
ExpectIntEQ(wc_XmssKey_SetReadCb(&key, test_xmss_read_fail), 0);
|
||||
ExpectIntEQ(wc_XmssKey_Reload(&key), WC_NO_ERR_TRACE(NO_VALID_DEVID));
|
||||
wc_XmssKey_Free(&key);
|
||||
|
||||
/* The device makes the key, then declines sign, verify and sigs left. */
|
||||
ExpectIntEQ(wc_XmssKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0);
|
||||
ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0);
|
||||
|
||||
@@ -941,8 +941,8 @@ static void wb_getkid_from_privraw(void)
|
||||
* its independence pair still needs the all-valid baseline row alongside it
|
||||
* (masking MC/DC on an OR chain: the operand's pair is baseline-all-false
|
||||
* vs only-that-operand-true, not two different-failure rows). No real
|
||||
* keygen needed -- GetKid does not check state, only key/params/kid/kidSz,
|
||||
* and reads a zeroed priv_raw harmlessly.
|
||||
* keygen needed -- forcing privSet stands in for it, and GetKid reads a
|
||||
* zeroed priv_raw harmlessly.
|
||||
******************************************************************/
|
||||
static void wb_getkid(void)
|
||||
{
|
||||
@@ -953,7 +953,13 @@ static void wb_getkid(void)
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
wc_LmsKey_Init(&key, NULL, INVALID_DEVID);
|
||||
wc_LmsKey_SetParameters(&key, 1, 5, 8); /* baseline: all operands false */
|
||||
wc_LmsKey_SetParameters(&key, 1, 5, 8);
|
||||
ret = wc_LmsKey_GetKid(&key, &kid, &kidSz);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) {
|
||||
WB_NOTE("GetKid(privSet==0) did not fail");
|
||||
wb_fail = 1;
|
||||
}
|
||||
key.privSet = 1; /* baseline: all operands false */
|
||||
ret = wc_LmsKey_GetKid(&key, &kid, &kidSz);
|
||||
if (ret != 0) {
|
||||
WB_NOTE("GetKid baseline failed");
|
||||
|
||||
@@ -1043,6 +1043,7 @@ void wc_LmsKey_Free(LmsKey* key)
|
||||
}
|
||||
|
||||
ForceZero(key->priv_raw, HSS_MAX_PRIVATE_KEY_LEN);
|
||||
key->privSet = 0;
|
||||
ForceZero(&key->priv, sizeof(HssPrivKey));
|
||||
|
||||
key->write_private_key = NULL;
|
||||
@@ -1316,6 +1317,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng)
|
||||
/* Update state. */
|
||||
key->state = WC_LMS_STATE_OK;
|
||||
key->pubSet = 1;
|
||||
key->privSet = 1;
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB_ONLY_LMS */
|
||||
|
||||
@@ -1332,13 +1334,17 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng)
|
||||
*
|
||||
* With a crypto callback device, the read callback and not the devId decides
|
||||
* whether the software reload runs. See wc_LmsKey_Reload below.
|
||||
* WOLF_CRYPTO_CB_ONLY_LMS removes the software reload, so the read callback
|
||||
* is never called.
|
||||
*
|
||||
* Neither arm populates key->pub, so the reloaded key can sign but cannot
|
||||
* export a public key or verify.
|
||||
*
|
||||
* @param [in, out] key LMS key.
|
||||
*
|
||||
* Returns 0 on success. */
|
||||
* Returns 0 on success.
|
||||
* Returns NO_VALID_DEVID with WOLF_CRYPTO_CB_ONLY_LMS unless the key is
|
||||
* device-backed. */
|
||||
int wc_LmsKey_Reload(LmsKey* key)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -1370,7 +1376,7 @@ int wc_LmsKey_Reload(LmsKey* key)
|
||||
#endif
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB_ONLY_LMS
|
||||
/* The device owns the state, so there is nothing to reload. */
|
||||
/* No software reload exists to use the read callback. */
|
||||
if (ret == 0) {
|
||||
ret = NO_VALID_DEVID;
|
||||
}
|
||||
@@ -1458,6 +1464,7 @@ int wc_LmsKey_Reload(LmsKey* key)
|
||||
if (ret == 0) {
|
||||
/* Update state. */
|
||||
key->state = WC_LMS_STATE_OK;
|
||||
key->privSet = 1;
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB_ONLY_LMS */
|
||||
|
||||
@@ -2119,7 +2126,8 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
|
||||
* @param [out] kidSz Size of key ID.
|
||||
* @return 0 on success.
|
||||
* @return BAD_FUNC_ARG when a key, kid or kidSz is NULL.
|
||||
* @return NOT_COMPILED_IN when key is on a device.
|
||||
* @return BAD_STATE_E when the private key is not in memory, such as when a
|
||||
* device keeps it.
|
||||
*/
|
||||
int wc_LmsKey_GetKid(LmsKey* key, const byte** kid, word32* kidSz)
|
||||
{
|
||||
@@ -2130,31 +2138,17 @@ int wc_LmsKey_GetKid(LmsKey* key, const byte** kid, word32* kidSz)
|
||||
(kidSz == NULL)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
if ((ret == 0) && (!key->privSet)) {
|
||||
WOLFSSL_MSG("error: LMS key contains no private key");
|
||||
ret = BAD_STATE_E;
|
||||
}
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB_ONLY_LMS
|
||||
/* The device owns the private key, which contains the key ID. */
|
||||
if (ret == 0) {
|
||||
ret = NOT_COMPILED_IN;
|
||||
}
|
||||
(void)offset;
|
||||
#else
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
/* priv_raw is not populated for HSM-backed keys where the device owns
|
||||
* the private state. Extend the CryptoCb surface if device-side KID
|
||||
* retrieval becomes a requirement.
|
||||
*/
|
||||
if ((ret == 0) && (key->devId != INVALID_DEVID)) {
|
||||
WOLFSSL_MSG(
|
||||
"wc_LmsKey_GetKid: priv_raw may be uninitialised for HSM keys");
|
||||
}
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
/* SEED length is hash length. */
|
||||
offset = HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN + key->params->hash_len;
|
||||
*kid = key->priv_raw + offset;
|
||||
*kidSz = HSS_PRIVATE_KEY_LEN(key->params->hash_len) - offset;
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB_ONLY_LMS */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1382,6 +1382,8 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng)
|
||||
*
|
||||
* With a crypto callback device, the read callback and not the devId decides
|
||||
* whether the software reload runs. See wc_XmssKey_Reload below.
|
||||
* WOLF_CRYPTO_CB_ONLY_XMSS removes the software reload, so the read callback
|
||||
* is never called.
|
||||
*
|
||||
* Neither arm populates key->pk, so the reloaded key can sign but cannot
|
||||
* export a public key or verify.
|
||||
@@ -1396,6 +1398,8 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng)
|
||||
* @return MEMORY_E when allocating dynamic memory fails.
|
||||
* @return BAD_STATE_E when wrong state for operation.
|
||||
* @return IO_FAILED_E when reading private key failed.
|
||||
* @return NO_VALID_DEVID with WOLF_CRYPTO_CB_ONLY_XMSS unless the key is
|
||||
* device-backed.
|
||||
*/
|
||||
int wc_XmssKey_Reload(XmssKey* key)
|
||||
{
|
||||
@@ -1428,7 +1432,7 @@ int wc_XmssKey_Reload(XmssKey* key)
|
||||
#endif
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB_ONLY_XMSS
|
||||
/* The device owns the state, so there is nothing to reload. */
|
||||
/* No software reload exists to use the read callback. */
|
||||
if (ret == 0) {
|
||||
ret = NO_VALID_DEVID;
|
||||
}
|
||||
|
||||
@@ -71998,15 +71998,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void)
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
|
||||
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
|
||||
ret = wc_LmsKey_GetKid(&signingKey, &kid, &kidSz);
|
||||
#ifdef WOLF_CRYPTO_CB_ONLY_LMS
|
||||
/* The key ID stays on the device. */
|
||||
if (ret != WC_NO_ERR_TRACE(NOT_COMPILED_IN))
|
||||
#if defined(WOLFSSL_SWDEV) && defined(WOLF_CRYPTO_CB_FIND)
|
||||
/* swdev keeps the private key, which contains the key ID. */
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_STATE_E))
|
||||
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
|
||||
#else
|
||||
if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); }
|
||||
if (kidSz != WC_LMS_I_LEN) {
|
||||
ERROR_OUT(WC_TEST_RET_ENC_I(kidSz), out);
|
||||
}
|
||||
/* The key ID follows the L, LMS and LM-OTS types in the public key. */
|
||||
if (XMEMCMP(kid, signingKey.pub + LMS_L_LEN + 2 * LMS_TYPE_LEN,
|
||||
kidSz) != 0) {
|
||||
ERROR_OUT(WC_TEST_RET_ENC_NC, out);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = wc_LmsKey_ExportPub_ex(&verifyKey, &signingKey, NULL, devId);
|
||||
|
||||
@@ -881,6 +881,9 @@ struct LmsKey {
|
||||
int labelLen;
|
||||
#endif
|
||||
WC_BITFIELD pubSet:1; /* pub holds a public key */
|
||||
#ifndef WOLFSSL_LMS_VERIFY_ONLY
|
||||
WC_BITFIELD privSet:1; /* priv_raw contains the private key */
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef WC_LMSKEY_TYPE_DEFINED
|
||||
|
||||
Reference in New Issue
Block a user