Skip to content

Commit 30b1964

Browse files
committed
Add privKeySet checks to Ed25519, Ed448, ML-DSA, and ML-KEM
1 parent b2f1c58 commit 30b1964

12 files changed

Lines changed: 260 additions & 1 deletion

File tree

tests/api/test_ed25519.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,50 @@ int test_wc_ed25519_sign_msg(void)
169169

170170
} /* END test_wc_ed25519_sign_msg */
171171

172+
/*
173+
* Test that wc_ed25519_sign_msg() rejects a public-key-only key object.
174+
* A key with pubKeySet=1 but privKeySet=0 must not silently sign.
175+
*/
176+
int test_wc_ed25519_sign_msg_pubonly_fails(void)
177+
{
178+
EXPECT_DECLS;
179+
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) && \
180+
defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_KEY_EXPORT)
181+
ed25519_key fullKey;
182+
ed25519_key pubOnlyKey;
183+
WC_RNG rng;
184+
byte pubBuf[ED25519_PUB_KEY_SIZE];
185+
word32 pubSz = sizeof(pubBuf);
186+
byte msg[] = "test message for pubonly check";
187+
byte sig[ED25519_SIG_SIZE];
188+
word32 sigLen = sizeof(sig);
189+
190+
XMEMSET(&fullKey, 0, sizeof(fullKey));
191+
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
192+
XMEMSET(&rng, 0, sizeof(rng));
193+
194+
ExpectIntEQ(wc_ed25519_init(&fullKey), 0);
195+
ExpectIntEQ(wc_ed25519_init(&pubOnlyKey), 0);
196+
ExpectIntEQ(wc_InitRng(&rng), 0);
197+
198+
/* Generate a real key pair and export its public key. */
199+
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &fullKey), 0);
200+
ExpectIntEQ(wc_ed25519_export_public(&fullKey, pubBuf, &pubSz), 0);
201+
202+
/* Import only the public key into a fresh key object. */
203+
ExpectIntEQ(wc_ed25519_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
204+
205+
/* Signing with a public-key-only object must fail. */
206+
ExpectIntEQ(wc_ed25519_sign_msg(msg, sizeof(msg), sig, &sigLen,
207+
&pubOnlyKey), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
208+
209+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
210+
wc_ed25519_free(&pubOnlyKey);
211+
wc_ed25519_free(&fullKey);
212+
#endif
213+
return EXPECT_RESULT();
214+
} /* END test_wc_ed25519_sign_msg_pubonly_fails */
215+
172216
/*
173217
* Testing wc_ed25519_import_public()
174218
*/

tests/api/test_ed25519.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
int test_wc_ed25519_make_key(void);
2828
int test_wc_ed25519_init(void);
2929
int test_wc_ed25519_sign_msg(void);
30+
int test_wc_ed25519_sign_msg_pubonly_fails(void);
3031
int test_wc_ed25519_import_public(void);
3132
int test_wc_ed25519_import_private_key(void);
3233
int test_wc_ed25519_export(void);
@@ -40,6 +41,7 @@ int test_wc_Ed25519PrivateKeyToDer(void);
4041
TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \
4142
TEST_DECL_GROUP("ed25519", test_wc_ed25519_init), \
4243
TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg), \
44+
TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg_pubonly_fails), \
4345
TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_public), \
4446
TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_private_key), \
4547
TEST_DECL_GROUP("ed25519", test_wc_ed25519_export), \

tests/api/test_ed448.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,50 @@ int test_wc_ed448_sign_msg(void)
162162
return EXPECT_RESULT();
163163
} /* END test_wc_ed448_sign_msg */
164164

165+
/*
166+
* Test that wc_ed448_sign_msg() rejects a public-key-only key object.
167+
* A key with pubKeySet=1 but privKeySet=0 must not silently sign.
168+
*/
169+
int test_wc_ed448_sign_msg_pubonly_fails(void)
170+
{
171+
EXPECT_DECLS;
172+
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
173+
defined(HAVE_ED448_KEY_IMPORT) && defined(HAVE_ED448_KEY_EXPORT)
174+
ed448_key fullKey;
175+
ed448_key pubOnlyKey;
176+
WC_RNG rng;
177+
byte pubBuf[ED448_PUB_KEY_SIZE];
178+
word32 pubSz = sizeof(pubBuf);
179+
byte msg[] = "test message for pubonly check";
180+
byte sig[ED448_SIG_SIZE];
181+
word32 sigLen = sizeof(sig);
182+
183+
XMEMSET(&fullKey, 0, sizeof(fullKey));
184+
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
185+
XMEMSET(&rng, 0, sizeof(rng));
186+
187+
ExpectIntEQ(wc_ed448_init(&fullKey), 0);
188+
ExpectIntEQ(wc_ed448_init(&pubOnlyKey), 0);
189+
ExpectIntEQ(wc_InitRng(&rng), 0);
190+
191+
/* Generate a real key pair and export its public key. */
192+
ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &fullKey), 0);
193+
ExpectIntEQ(wc_ed448_export_public(&fullKey, pubBuf, &pubSz), 0);
194+
195+
/* Import only the public key into a fresh key object. */
196+
ExpectIntEQ(wc_ed448_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
197+
198+
/* Signing with a public-key-only object must fail. */
199+
ExpectIntEQ(wc_ed448_sign_msg(msg, sizeof(msg), sig, &sigLen,
200+
&pubOnlyKey, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
201+
202+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
203+
wc_ed448_free(&pubOnlyKey);
204+
wc_ed448_free(&fullKey);
205+
#endif
206+
return EXPECT_RESULT();
207+
} /* END test_wc_ed448_sign_msg_pubonly_fails */
208+
165209
/*
166210
* Testing wc_ed448_import_public()
167211
*/

tests/api/test_ed448.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
int test_wc_ed448_make_key(void);
2828
int test_wc_ed448_init(void);
2929
int test_wc_ed448_sign_msg(void);
30+
int test_wc_ed448_sign_msg_pubonly_fails(void);
3031
int test_wc_ed448_import_public(void);
3132
int test_wc_ed448_import_private_key(void);
3233
int test_wc_ed448_export(void);
@@ -40,6 +41,7 @@ int test_wc_Ed448PrivateKeyToDer(void);
4041
TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \
4142
TEST_DECL_GROUP("ed448", test_wc_ed448_init), \
4243
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg), \
44+
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg_pubonly_fails), \
4345
TEST_DECL_GROUP("ed448", test_wc_ed448_import_public), \
4446
TEST_DECL_GROUP("ed448", test_wc_ed448_import_private_key), \
4547
TEST_DECL_GROUP("ed448", test_wc_ed448_export), \

tests/api/test_mldsa.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,82 @@ int test_wc_dilithium(void)
685685
return EXPECT_RESULT();
686686
}
687687

688+
/*
689+
* Test that wc_dilithium_sign_msg() rejects a public-key-only key object.
690+
* A key with prvKeySet=0 must not silently sign with zeroed key data.
691+
*/
692+
int test_wc_dilithium_sign_pubonly_fails(void)
693+
{
694+
EXPECT_DECLS;
695+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
696+
!defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
697+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
698+
!defined(WOLFSSL_DILITHIUM_NO_CTX)
699+
dilithium_key* key;
700+
dilithium_key* pubOnlyKey;
701+
WC_RNG rng;
702+
byte* pubBuf = NULL;
703+
word32 pubLen = DILITHIUM_MAX_PUB_KEY_SIZE;
704+
byte msg[] = "test message for pubonly check";
705+
byte* sig = NULL;
706+
word32 sigLen = DILITHIUM_MAX_SIG_SIZE;
707+
708+
key = (dilithium_key*)XMALLOC(sizeof(*key), NULL,
709+
DYNAMIC_TYPE_TMP_BUFFER);
710+
ExpectNotNull(key);
711+
pubOnlyKey = (dilithium_key*)XMALLOC(sizeof(*pubOnlyKey), NULL,
712+
DYNAMIC_TYPE_TMP_BUFFER);
713+
ExpectNotNull(pubOnlyKey);
714+
pubBuf = (byte*)XMALLOC(DILITHIUM_MAX_PUB_KEY_SIZE, NULL,
715+
DYNAMIC_TYPE_TMP_BUFFER);
716+
ExpectNotNull(pubBuf);
717+
sig = (byte*)XMALLOC(DILITHIUM_MAX_SIG_SIZE, NULL,
718+
DYNAMIC_TYPE_TMP_BUFFER);
719+
ExpectNotNull(sig);
720+
721+
if (key != NULL)
722+
XMEMSET(key, 0, sizeof(*key));
723+
if (pubOnlyKey != NULL)
724+
XMEMSET(pubOnlyKey, 0, sizeof(*pubOnlyKey));
725+
XMEMSET(&rng, 0, sizeof(rng));
726+
727+
ExpectIntEQ(wc_InitRng(&rng), 0);
728+
ExpectIntEQ(wc_dilithium_init(key), 0);
729+
ExpectIntEQ(wc_dilithium_init(pubOnlyKey), 0);
730+
731+
#ifndef WOLFSSL_NO_ML_DSA_44
732+
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_44), 0);
733+
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_44), 0);
734+
#elif !defined(WOLFSSL_NO_ML_DSA_65)
735+
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_65), 0);
736+
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_65), 0);
737+
#else
738+
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_87), 0);
739+
ExpectIntEQ(wc_dilithium_set_level(pubOnlyKey, WC_ML_DSA_87), 0);
740+
#endif
741+
742+
/* Generate a real key pair and export its public key. */
743+
ExpectIntEQ(wc_dilithium_make_key(key, &rng), 0);
744+
ExpectIntEQ(wc_dilithium_export_public(key, pubBuf, &pubLen), 0);
745+
746+
/* Import only the public key into a fresh key object. */
747+
ExpectIntEQ(wc_dilithium_import_public(pubBuf, pubLen, pubOnlyKey), 0);
748+
749+
/* Signing with a public-key-only object must fail. */
750+
ExpectIntEQ(wc_dilithium_sign_ctx_msg(NULL, 0, msg, sizeof(msg), sig,
751+
&sigLen, pubOnlyKey, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
752+
753+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
754+
wc_dilithium_free(pubOnlyKey);
755+
wc_dilithium_free(key);
756+
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
757+
XFREE(pubBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
758+
XFREE(pubOnlyKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
759+
XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER);
760+
#endif
761+
return EXPECT_RESULT();
762+
} /* END test_wc_dilithium_sign_pubonly_fails */
763+
688764
int test_wc_dilithium_make_key(void)
689765
{
690766
EXPECT_DECLS;

tests/api/test_mldsa.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <tests/api/api_decl.h>
2626

2727
int test_wc_dilithium(void);
28+
int test_wc_dilithium_sign_pubonly_fails(void);
2829
int test_wc_dilithium_make_key(void);
2930
int test_wc_dilithium_sign(void);
3031
int test_wc_dilithium_verify(void);
@@ -42,6 +43,7 @@ int test_mldsa_pkcs12(void);
4243

4344
#define TEST_MLDSA_DECLS \
4445
TEST_DECL_GROUP("mldsa", test_wc_dilithium), \
46+
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_pubonly_fails), \
4547
TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key), \
4648
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign), \
4749
TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify), \

tests/api/test_mlkem.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3872,3 +3872,79 @@ int test_wc_mlkem_decapsulate_kats(void)
38723872
return EXPECT_RESULT();
38733873
}
38743874

3875+
/*
3876+
* Test that wc_MlKemKey_Decapsulate() rejects a public-key-only key object.
3877+
* A key with MLKEM_FLAG_PUB_SET but not MLKEM_FLAG_PRIV_SET must not
3878+
* silently decapsulate with zeroed private key data.
3879+
*/
3880+
int test_wc_mlkem_decapsulate_pubonly_fails(void)
3881+
{
3882+
EXPECT_DECLS;
3883+
#if defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_WC_MLKEM) && \
3884+
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
3885+
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
3886+
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
3887+
MlKemKey* fullKey;
3888+
MlKemKey* pubOnlyKey;
3889+
WC_RNG rng;
3890+
byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
3891+
byte ss[WC_ML_KEM_SS_SZ];
3892+
byte ssDec[WC_ML_KEM_SS_SZ];
3893+
byte pubBuf[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE];
3894+
word32 pubLen = 0;
3895+
word32 ctLen = 0;
3896+
3897+
fullKey = (MlKemKey*)XMALLOC(sizeof(*fullKey), NULL,
3898+
DYNAMIC_TYPE_TMP_BUFFER);
3899+
ExpectNotNull(fullKey);
3900+
pubOnlyKey = (MlKemKey*)XMALLOC(sizeof(*pubOnlyKey), NULL,
3901+
DYNAMIC_TYPE_TMP_BUFFER);
3902+
ExpectNotNull(pubOnlyKey);
3903+
3904+
XMEMSET(&rng, 0, sizeof(rng));
3905+
ExpectIntEQ(wc_InitRng(&rng), 0);
3906+
3907+
#ifndef WOLFSSL_NO_ML_KEM_768
3908+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_768, NULL,
3909+
INVALID_DEVID), 0);
3910+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_768, NULL,
3911+
INVALID_DEVID), 0);
3912+
#elif !defined(WOLFSSL_NO_ML_KEM_512)
3913+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_512, NULL,
3914+
INVALID_DEVID), 0);
3915+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_512, NULL,
3916+
INVALID_DEVID), 0);
3917+
#else
3918+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_1024, NULL,
3919+
INVALID_DEVID), 0);
3920+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_1024, NULL,
3921+
INVALID_DEVID), 0);
3922+
#endif
3923+
3924+
/* Get correct sizes for this key type. */
3925+
ExpectIntEQ(wc_MlKemKey_PublicKeySize(fullKey, &pubLen), 0);
3926+
ExpectIntEQ(wc_MlKemKey_CipherTextSize(fullKey, &ctLen), 0);
3927+
3928+
/* Generate a real key pair. */
3929+
ExpectIntEQ(wc_MlKemKey_MakeKey(fullKey, &rng), 0);
3930+
3931+
/* Encapsulate with the full key to get a valid ciphertext. */
3932+
ExpectIntEQ(wc_MlKemKey_Encapsulate(fullKey, ct, ss, &rng), 0);
3933+
3934+
/* Export and import only the public key. */
3935+
ExpectIntEQ(wc_MlKemKey_EncodePublicKey(fullKey, pubBuf, pubLen), 0);
3936+
ExpectIntEQ(wc_MlKemKey_DecodePublicKey(pubOnlyKey, pubBuf, pubLen), 0);
3937+
3938+
/* Decapsulating with a public-key-only object must fail. */
3939+
ExpectIntEQ(wc_MlKemKey_Decapsulate(pubOnlyKey, ssDec, ct, ctLen),
3940+
WC_NO_ERR_TRACE(BAD_STATE_E));
3941+
3942+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
3943+
wc_MlKemKey_Free(pubOnlyKey);
3944+
wc_MlKemKey_Free(fullKey);
3945+
XFREE(pubOnlyKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3946+
XFREE(fullKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3947+
#endif
3948+
return EXPECT_RESULT();
3949+
} /* END test_wc_mlkem_decapsulate_pubonly_fails */
3950+

tests/api/test_mlkem.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
int test_wc_mlkem_make_key_kats(void);
2828
int test_wc_mlkem_encapsulate_kats(void);
2929
int test_wc_mlkem_decapsulate_kats(void);
30+
int test_wc_mlkem_decapsulate_pubonly_fails(void);
3031

3132
#define TEST_MLKEM_DECLS \
3233
TEST_DECL_GROUP("mlkem", test_wc_mlkem_make_key_kats), \
3334
TEST_DECL_GROUP("mlkem", test_wc_mlkem_encapsulate_kats), \
34-
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats)
35+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_kats), \
36+
TEST_DECL_GROUP("mlkem", test_wc_mlkem_decapsulate_pubonly_fails)
3537

3638
#endif /* WOLFCRYPT_TEST_MLKEM_H */

wolfcrypt/src/dilithium.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10192,6 +10192,9 @@ int wc_dilithium_sign_ctx_msg(const byte* ctx, byte ctxLen, const byte* msg,
1019210192
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
1019310193
ret = BAD_FUNC_ARG;
1019410194
}
10195+
if ((ret == 0) && (!key->prvKeySet)) {
10196+
ret = BAD_FUNC_ARG;
10197+
}
1019510198

1019610199
#ifdef WOLF_CRYPTO_CB
1019710200
if (ret == 0) {

wolfcrypt/src/ed25519.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ int wc_ed25519_sign_msg_ex(const byte* in, word32 inLen, byte* out,
401401

402402
if (!key->pubKeySet)
403403
return BAD_FUNC_ARG;
404+
if (!key->privKeySet)
405+
return BAD_FUNC_ARG;
404406

405407
/* check and set up out length */
406408
if (*outLen < ED25519_SIG_SIZE) {

0 commit comments

Comments
 (0)