1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

Let an ML-KEM key named by id decapsulate on a device

wc_MlKemKey_Init_Id names a key held by a crypto callback device. It sets
no key flags, because the private key never enters software. But
wc_MlKemKey_Decapsulate returned BAD_STATE_E for a key without
MLKEM_FLAG_PRIV_SET before calling the device, so a key named by id
could never decapsulate, on any device.

Check MLKEM_FLAG_PRIV_SET after the device call instead, next to the
MLKEM_FLAG_PUB_SET check that already guards the software path. A
device can then decapsulate with a key it holds. When it declines, or
there is no device, a key without a private key still gets BAD_STATE_E
before software reads any key material. wc_MlKemKey_SignCtx for ML-DSA
already dispatches in this order for the same reason.

test_wc_mlkem_decapsulate_id_key decapsulates with a key from
wc_MlKemKey_Init_Id through a callback device. It checks that the
device is called, and that BAD_STATE_E comes back when the device
declines.
This commit is contained in:
Tobias Frauenschläger
2026-10-16 09:45:25 +02:00
parent 68d83d2a7f
commit 55084c9a50
3 changed files with 55 additions and 4 deletions

View File

@@ -5102,6 +5102,55 @@ int test_wc_mlkem_cb_pending_rejected(void)
return EXPECT_RESULT();
}
/* A key named by id holds no private key in software, so decapsulating it
* must reach the device. Only a declined call falls to the local key check. */
int test_wc_mlkem_decapsulate_id_key(void)
{
EXPECT_DECLS;
#if defined(TEST_MLKEM_CB_PENDING) && defined(WOLF_PRIVATE_KEY_ID)
MlKemKey* key = NULL;
MlKemCbPendingCtx seen;
byte* ct = NULL;
byte ss[WC_ML_KEM_SS_SZ];
word32 ctLen = 0;
const byte id[] = { 0x01, 0x02, 0x03, 0x04 };
int keyInit = 0;
XMEMSET(&seen, 0, sizeof(seen));
ExpectNotNull(key = (MlKemKey*)XMALLOC(sizeof(MlKemKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_MLKEM_CB_PENDING_DEVID,
mlkem_cb_pending_cb, &seen), 0);
ExpectIntEQ(wc_MlKemKey_Init_Id(key, TEST_MLKEM_CB_PENDING_TYPE, id,
(int)sizeof(id), NULL, TEST_MLKEM_CB_PENDING_DEVID), 0);
if (EXPECT_SUCCESS()) {
keyInit = 1;
}
ExpectIntEQ(wc_MlKemKey_CipherTextSize(key, &ctLen), 0);
ExpectNotNull(ct = (byte*)XMALLOC(ctLen, NULL, DYNAMIC_TYPE_TMP_BUFFER));
if (ct != NULL) {
XMEMSET(ct, 0, ctLen);
}
seen.ret = 0;
ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ct, ctLen), 0);
ExpectIntEQ(seen.calls, 1);
seen.ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ct, ctLen),
WC_NO_ERR_TRACE(BAD_STATE_E));
ExpectIntEQ(seen.calls, 2);
if (keyInit) {
wc_MlKemKey_Free(key);
}
wc_CryptoCb_UnRegisterDevice(TEST_MLKEM_CB_PENDING_DEVID);
XFREE(ct, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}
/* A refused save while re-decoding a public key must leave the key unusable,
* not holding one key's polynomials with another key's seed. */
int test_wc_mlkem_decode_pubkey_refused_save(void)

View File

@@ -39,6 +39,7 @@ int test_wc_mlkem_encapsulate_pubkey_unset_decision(void);
int test_wc_mlkem_encode_key_len_decision(void);
int test_wc_mlkem_cb_free(void);
int test_wc_mlkem_cb_pending_rejected(void);
int test_wc_mlkem_decapsulate_id_key(void);
int test_wc_mlkem_decode_pubkey_refused_save(void);
int test_wc_mlkem_decode_reused_key(void);
int test_wc_mlkem_decode_alloc_fail(void);
@@ -59,6 +60,7 @@ int test_wc_mlkem_decode_alloc_fail(void);
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encode_key_len_decision), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_cb_free), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_cb_pending_rejected), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_id_key), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_pubkey_refused_save), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_reused_key), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decode_alloc_fail)

View File

@@ -2125,10 +2125,6 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss,
if ((key == NULL) || (ss == NULL) || (ct == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && ((key->flags & MLKEM_FLAG_PRIV_SET) == 0)) {
ret = BAD_STATE_E;
}
if (ret == 0) {
/* Establish cipher text size based on key type. */
switch (key->type) {
@@ -2194,6 +2190,10 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss,
ret = 0;
}
#endif
/* Only software needs a local private key; a key named by id has none */
if ((ret == 0) && ((key->flags & MLKEM_FLAG_PRIV_SET) == 0)) {
ret = BAD_STATE_E;
}
/* Software decapsulation re-encrypts with the public key (FIPS 203,
* Algorithm 18, steps 2 and 8), so refuse before decrypting without it. */
if ((ret == 0) && ((key->flags & MLKEM_FLAG_PUB_SET) == 0)) {