Add privKeySet checks to Ed25519, Ed448, ML-DSA, and ML-KEM

This commit is contained in:
Anthony Hu
2026-03-26 14:56:00 -04:00
parent b2f1c5864d
commit 30b196471b
12 changed files with 260 additions and 1 deletions
+44
View File
@@ -169,6 +169,50 @@ int test_wc_ed25519_sign_msg(void)
} /* END test_wc_ed25519_sign_msg */
/*
* Test that wc_ed25519_sign_msg() rejects a public-key-only key object.
* A key with pubKeySet=1 but privKeySet=0 must not silently sign.
*/
int test_wc_ed25519_sign_msg_pubonly_fails(void)
{
EXPECT_DECLS;
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) && \
defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_KEY_EXPORT)
ed25519_key fullKey;
ed25519_key pubOnlyKey;
WC_RNG rng;
byte pubBuf[ED25519_PUB_KEY_SIZE];
word32 pubSz = sizeof(pubBuf);
byte msg[] = "test message for pubonly check";
byte sig[ED25519_SIG_SIZE];
word32 sigLen = sizeof(sig);
XMEMSET(&fullKey, 0, sizeof(fullKey));
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
XMEMSET(&rng, 0, sizeof(rng));
ExpectIntEQ(wc_ed25519_init(&fullKey), 0);
ExpectIntEQ(wc_ed25519_init(&pubOnlyKey), 0);
ExpectIntEQ(wc_InitRng(&rng), 0);
/* Generate a real key pair and export its public key. */
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &fullKey), 0);
ExpectIntEQ(wc_ed25519_export_public(&fullKey, pubBuf, &pubSz), 0);
/* Import only the public key into a fresh key object. */
ExpectIntEQ(wc_ed25519_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
/* Signing with a public-key-only object must fail. */
ExpectIntEQ(wc_ed25519_sign_msg(msg, sizeof(msg), sig, &sigLen,
&pubOnlyKey), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
DoExpectIntEQ(wc_FreeRng(&rng), 0);
wc_ed25519_free(&pubOnlyKey);
wc_ed25519_free(&fullKey);
#endif
return EXPECT_RESULT();
} /* END test_wc_ed25519_sign_msg_pubonly_fails */
/*
* Testing wc_ed25519_import_public()
*/
+2
View File
@@ -27,6 +27,7 @@
int test_wc_ed25519_make_key(void);
int test_wc_ed25519_init(void);
int test_wc_ed25519_sign_msg(void);
int test_wc_ed25519_sign_msg_pubonly_fails(void);
int test_wc_ed25519_import_public(void);
int test_wc_ed25519_import_private_key(void);
int test_wc_ed25519_export(void);
@@ -40,6 +41,7 @@ int test_wc_Ed25519PrivateKeyToDer(void);
TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_init), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg_pubonly_fails), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_public), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_private_key), \
TEST_DECL_GROUP("ed25519", test_wc_ed25519_export), \
+44
View File
@@ -162,6 +162,50 @@ int test_wc_ed448_sign_msg(void)
return EXPECT_RESULT();
} /* END test_wc_ed448_sign_msg */
/*
* Test that wc_ed448_sign_msg() rejects a public-key-only key object.
* A key with pubKeySet=1 but privKeySet=0 must not silently sign.
*/
int test_wc_ed448_sign_msg_pubonly_fails(void)
{
EXPECT_DECLS;
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
defined(HAVE_ED448_KEY_IMPORT) && defined(HAVE_ED448_KEY_EXPORT)
ed448_key fullKey;
ed448_key pubOnlyKey;
WC_RNG rng;
byte pubBuf[ED448_PUB_KEY_SIZE];
word32 pubSz = sizeof(pubBuf);
byte msg[] = "test message for pubonly check";
byte sig[ED448_SIG_SIZE];
word32 sigLen = sizeof(sig);
XMEMSET(&fullKey, 0, sizeof(fullKey));
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
XMEMSET(&rng, 0, sizeof(rng));
ExpectIntEQ(wc_ed448_init(&fullKey), 0);
ExpectIntEQ(wc_ed448_init(&pubOnlyKey), 0);
ExpectIntEQ(wc_InitRng(&rng), 0);
/* Generate a real key pair and export its public key. */
ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &fullKey), 0);
ExpectIntEQ(wc_ed448_export_public(&fullKey, pubBuf, &pubSz), 0);
/* Import only the public key into a fresh key object. */
ExpectIntEQ(wc_ed448_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
/* Signing with a public-key-only object must fail. */
ExpectIntEQ(wc_ed448_sign_msg(msg, sizeof(msg), sig, &sigLen,
&pubOnlyKey, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
DoExpectIntEQ(wc_FreeRng(&rng), 0);
wc_ed448_free(&pubOnlyKey);
wc_ed448_free(&fullKey);
#endif
return EXPECT_RESULT();
} /* END test_wc_ed448_sign_msg_pubonly_fails */
/*
* Testing wc_ed448_import_public()
*/
+2
View File
@@ -27,6 +27,7 @@
int test_wc_ed448_make_key(void);
int test_wc_ed448_init(void);
int test_wc_ed448_sign_msg(void);
int test_wc_ed448_sign_msg_pubonly_fails(void);
int test_wc_ed448_import_public(void);
int test_wc_ed448_import_private_key(void);
int test_wc_ed448_export(void);
@@ -40,6 +41,7 @@ int test_wc_Ed448PrivateKeyToDer(void);
TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \
TEST_DECL_GROUP("ed448", test_wc_ed448_init), \
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg), \
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg_pubonly_fails), \
TEST_DECL_GROUP("ed448", test_wc_ed448_import_public), \
TEST_DECL_GROUP("ed448", test_wc_ed448_import_private_key), \
TEST_DECL_GROUP("ed448", test_wc_ed448_export), \
+76
View File
@@ -685,6 +685,82 @@ int test_wc_dilithium(void)
return EXPECT_RESULT();
}
/*
* Test that wc_dilithium_sign_msg() rejects a public-key-only key object.
* A key with prvKeySet=0 must not silently sign with zeroed key data.
*/
int test_wc_dilithium_sign_pubonly_fails(void)
{
EXPECT_DECLS;
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
!defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
!defined(WOLFSSL_DILITHIUM_NO_CTX)
dilithium_key* key;
dilithium_key* pubOnlyKey;
WC_RNG rng;
byte* pubBuf = NULL;
word32 pubLen = DILITHIUM_MAX_PUB_KEY_SIZE;
byte msg[] = "test message for pubonly check";
byte* sig = NULL;
word32 sigLen = DILITHIUM_MAX_SIG_SIZE;
key = (dilithium_key*)XMALLOC(sizeof(*key), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(key);
pubOnlyKey = (dilithium_key*)XMALLOC(sizeof(*pubOnlyKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(pubOnlyKey);
pubBuf = (byte*)XMALLOC(DILITHIUM_MAX_PUB_KEY_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(pubBuf);
sig = (byte*)XMALLOC(DILITHIUM_MAX_SIG_SIZE, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(sig);
if (key != NULL)
XMEMSET(key, 0, sizeof(*key));
if (pubOnlyKey != NULL)
XMEMSET(pubOnlyKey, 0, sizeof(*pubOnlyKey));
XMEMSET(&rng, 0, sizeof(rng));
ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_dilithium_init(key), 0);
ExpectIntEQ(wc_dilithium_init(pubOnlyKey), 0);
#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_44), 0);
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_44), 0);
#elif !defined(WOLFSSL_NO_ML_DSA_65)
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_65), 0);
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_65), 0);
#else
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_87), 0);
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_87), 0);
#endif
/* Generate a real key pair and export its public key. */
ExpectIntEQ(wc_dilithium_make_key(key, &rng), 0);
ExpectIntEQ(wc_dilithium_export_public(key, pubBuf, &pubLen), 0);
/* Import only the public key into a fresh key object. */
ExpectIntEQ(wc_dilithium_import_public(pubBuf, pubLen, pubOnlyKey), 0);
/* Signing with a public-key-only object must fail. */
ExpectIntEQ(wc_dilithium_sign_ctx_msg(NULL, 0, msg, sizeof(msg), sig,
&sigLen, pubOnlyKey, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
DoExpectIntEQ(wc_FreeRng(&rng), 0);
wc_dilithium_free(pubOnlyKey);
wc_dilithium_free(key);
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(pubBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(pubOnlyKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
} /* END test_wc_dilithium_sign_pubonly_fails */
int test_wc_dilithium_make_key(void)
{
EXPECT_DECLS;
+2
View File
@@ -25,6 +25,7 @@
#include <tests/api/api_decl.h>
int test_wc_dilithium(void);
int test_wc_dilithium_sign_pubonly_fails(void);
int test_wc_dilithium_make_key(void);
int test_wc_dilithium_sign(void);
int test_wc_dilithium_verify(void);
@@ -42,6 +43,7 @@ int test_mldsa_pkcs12(void);
#define TEST_MLDSA_DECLS \
TEST_DECL_GROUP("mldsa", test_wc_dilithium), \
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_pubonly_fails), \
TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key), \
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign), \
TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify), \
+76
View File
@@ -3872,3 +3872,79 @@ int test_wc_mlkem_decapsulate_kats(void)
return EXPECT_RESULT();
}
/*
* Test that wc_MlKemKey_Decapsulate() rejects a public-key-only key object.
* A key with MLKEM_FLAG_PUB_SET but not MLKEM_FLAG_PRIV_SET must not
* silently decapsulate with zeroed private key data.
*/
int test_wc_mlkem_decapsulate_pubonly_fails(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_WC_MLKEM) && \
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
MlKemKey* fullKey;
MlKemKey* pubOnlyKey;
WC_RNG rng;
byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
byte ss[WC_ML_KEM_SS_SZ];
byte ssDec[WC_ML_KEM_SS_SZ];
byte pubBuf[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE];
word32 pubLen = 0;
word32 ctLen = 0;
fullKey = (MlKemKey*)XMALLOC(sizeof(*fullKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(fullKey);
pubOnlyKey = (MlKemKey*)XMALLOC(sizeof(*pubOnlyKey), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(pubOnlyKey);
XMEMSET(&rng, 0, sizeof(rng));
ExpectIntEQ(wc_InitRng(&rng), 0);
#ifndef WOLFSSL_NO_ML_KEM_768
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_768, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_768, NULL,
INVALID_DEVID), 0);
#elif !defined(WOLFSSL_NO_ML_KEM_512)
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_512, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_512, NULL,
INVALID_DEVID), 0);
#else
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_1024, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_1024, NULL,
INVALID_DEVID), 0);
#endif
/* Get correct sizes for this key type. */
ExpectIntEQ(wc_MlKemKey_PublicKeySize(fullKey, &pubLen), 0);
ExpectIntEQ(wc_MlKemKey_CipherTextSize(fullKey, &ctLen), 0);
/* Generate a real key pair. */
ExpectIntEQ(wc_MlKemKey_MakeKey(fullKey, &rng), 0);
/* Encapsulate with the full key to get a valid ciphertext. */
ExpectIntEQ(wc_MlKemKey_Encapsulate(fullKey, ct, ss, &rng), 0);
/* Export and import only the public key. */
ExpectIntEQ(wc_MlKemKey_EncodePublicKey(fullKey, pubBuf, pubLen), 0);
ExpectIntEQ(wc_MlKemKey_DecodePublicKey(pubOnlyKey, pubBuf, pubLen), 0);
/* Decapsulating with a public-key-only object must fail. */
ExpectIntEQ(wc_MlKemKey_Decapsulate(pubOnlyKey, ssDec, ct, ctLen),
WC_NO_ERR_TRACE(BAD_STATE_E));
DoExpectIntEQ(wc_FreeRng(&rng), 0);
wc_MlKemKey_Free(pubOnlyKey);
wc_MlKemKey_Free(fullKey);
XFREE(pubOnlyKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(fullKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
} /* END test_wc_mlkem_decapsulate_pubonly_fails */
+3 -1
View File
@@ -27,10 +27,12 @@
int test_wc_mlkem_make_key_kats(void);
int test_wc_mlkem_encapsulate_kats(void);
int test_wc_mlkem_decapsulate_kats(void);
int test_wc_mlkem_decapsulate_pubonly_fails(void);
#define TEST_MLKEM_DECLS \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encapsulate_kats), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats)
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats), \
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_pubonly_fails)
#endif /* WOLFCRYPT_TEST_MLKEM_H */
+3
View File
@@ -10192,6 +10192,9 @@ int wc_dilithium_sign_ctx_msg(const byte* ctx, byte ctxLen, const byte* msg,
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (!key->prvKeySet)) {
ret = BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
+2
View File
@@ -401,6 +401,8 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out,
if (!key->pubKeySet)
return BAD_FUNC_ARG;
if (!key->privKeySet)
return BAD_FUNC_ARG;
/* check and set up out length */
if (*outLen < ED25519_SIG_SIZE) {
+3
View File
@@ -367,6 +367,9 @@ int wc_ed448_sign_msg_ex(const byte* in, word32 inLen, byte* out,
if ((ret == 0) && (!key->pubKeySet)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (!key->privKeySet)) {
ret = BAD_FUNC_ARG;
}
/* check and set up out length */
if ((ret == 0) && (*outLen < ED448_SIG_SIZE)) {
+3
View File
@@ -1481,6 +1481,9 @@ 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. */