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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user