Skip to content

Commit 5701f5d

Browse files
authored
Merge pull request #10083 from anhu/privkeyset
Add privKeySet checks to Ed25519, Ed448, ML-DSA, and ML-KEM
2 parents d6fa846 + 7be3d1f commit 5701f5d

12 files changed

Lines changed: 268 additions & 1 deletion

File tree

tests/api/test_ed25519.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,52 @@ 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_FIPS) || FIPS_VERSION3_GE(7,0,0)
180+
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) && \
181+
defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_KEY_EXPORT)
182+
ed25519_key fullKey;
183+
ed25519_key pubOnlyKey;
184+
WC_RNG rng;
185+
byte pubBuf[ED25519_PUB_KEY_SIZE];
186+
word32 pubSz = sizeof(pubBuf);
187+
byte msg[] = "test message for pubonly check";
188+
byte sig[ED25519_SIG_SIZE];
189+
word32 sigLen = sizeof(sig);
190+
191+
XMEMSET(&fullKey, 0, sizeof(fullKey));
192+
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
193+
XMEMSET(&rng, 0, sizeof(rng));
194+
195+
ExpectIntEQ(wc_ed25519_init(&fullKey), 0);
196+
ExpectIntEQ(wc_ed25519_init(&pubOnlyKey), 0);
197+
ExpectIntEQ(wc_InitRng(&rng), 0);
198+
199+
/* Generate a real key pair and export its public key. */
200+
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &fullKey), 0);
201+
ExpectIntEQ(wc_ed25519_export_public(&fullKey, pubBuf, &pubSz), 0);
202+
203+
/* Import only the public key into a fresh key object. */
204+
ExpectIntEQ(wc_ed25519_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
205+
206+
/* Signing with a public-key-only object must fail. */
207+
ExpectIntEQ(wc_ed25519_sign_msg(msg, sizeof(msg), sig, &sigLen,
208+
&pubOnlyKey), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
209+
210+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
211+
wc_ed25519_free(&pubOnlyKey);
212+
wc_ed25519_free(&fullKey);
213+
#endif
214+
#endif
215+
return EXPECT_RESULT();
216+
} /* END test_wc_ed25519_sign_msg_pubonly_fails */
217+
172218
/*
173219
* Testing wc_ed25519_import_public()
174220
*/

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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,52 @@ 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_FIPS) || FIPS_VERSION3_GE(7,0,0)
173+
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
174+
defined(HAVE_ED448_KEY_IMPORT) && defined(HAVE_ED448_KEY_EXPORT)
175+
ed448_key fullKey;
176+
ed448_key pubOnlyKey;
177+
WC_RNG rng;
178+
byte pubBuf[ED448_PUB_KEY_SIZE];
179+
word32 pubSz = sizeof(pubBuf);
180+
byte msg[] = "test message for pubonly check";
181+
byte sig[ED448_SIG_SIZE];
182+
word32 sigLen = sizeof(sig);
183+
184+
XMEMSET(&fullKey, 0, sizeof(fullKey));
185+
XMEMSET(&pubOnlyKey, 0, sizeof(pubOnlyKey));
186+
XMEMSET(&rng, 0, sizeof(rng));
187+
188+
ExpectIntEQ(wc_ed448_init(&fullKey), 0);
189+
ExpectIntEQ(wc_ed448_init(&pubOnlyKey), 0);
190+
ExpectIntEQ(wc_InitRng(&rng), 0);
191+
192+
/* Generate a real key pair and export its public key. */
193+
ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &fullKey), 0);
194+
ExpectIntEQ(wc_ed448_export_public(&fullKey, pubBuf, &pubSz), 0);
195+
196+
/* Import only the public key into a fresh key object. */
197+
ExpectIntEQ(wc_ed448_import_public(pubBuf, pubSz, &pubOnlyKey), 0);
198+
199+
/* Signing with a public-key-only object must fail. */
200+
ExpectIntEQ(wc_ed448_sign_msg(msg, sizeof(msg), sig, &sigLen,
201+
&pubOnlyKey, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
202+
203+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
204+
wc_ed448_free(&pubOnlyKey);
205+
wc_ed448_free(&fullKey);
206+
#endif
207+
#endif
208+
return EXPECT_RESULT();
209+
} /* END test_wc_ed448_sign_msg_pubonly_fails */
210+
165211
/*
166212
* Testing wc_ed448_import_public()
167213
*/

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: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,84 @@ int test_wc_dilithium(void)
686686
return EXPECT_RESULT();
687687
}
688688

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

4647
#define TEST_MLDSA_DECLS \
4748
TEST_DECL_GROUP("mldsa", test_wc_dilithium), \
49+
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_pubonly_fails), \
4850
TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key), \
4951
TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign), \
5052
TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify), \

tests/api/test_mlkem.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3872,3 +3872,81 @@ 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(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
3884+
#if defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_WC_MLKEM) && \
3885+
!defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
3886+
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
3887+
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
3888+
MlKemKey* fullKey;
3889+
MlKemKey* pubOnlyKey;
3890+
WC_RNG rng;
3891+
byte ct[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
3892+
byte ss[WC_ML_KEM_SS_SZ];
3893+
byte ssDec[WC_ML_KEM_SS_SZ];
3894+
byte pubBuf[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE];
3895+
word32 pubLen = 0;
3896+
word32 ctLen = 0;
3897+
3898+
fullKey = (MlKemKey*)XMALLOC(sizeof(*fullKey), NULL,
3899+
DYNAMIC_TYPE_TMP_BUFFER);
3900+
ExpectNotNull(fullKey);
3901+
pubOnlyKey = (MlKemKey*)XMALLOC(sizeof(*pubOnlyKey), NULL,
3902+
DYNAMIC_TYPE_TMP_BUFFER);
3903+
ExpectNotNull(pubOnlyKey);
3904+
3905+
XMEMSET(&rng, 0, sizeof(rng));
3906+
ExpectIntEQ(wc_InitRng(&rng), 0);
3907+
3908+
#ifndef WOLFSSL_NO_ML_KEM_768
3909+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_768, NULL,
3910+
INVALID_DEVID), 0);
3911+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_768, NULL,
3912+
INVALID_DEVID), 0);
3913+
#elif !defined(WOLFSSL_NO_ML_KEM_512)
3914+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_512, NULL,
3915+
INVALID_DEVID), 0);
3916+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_512, NULL,
3917+
INVALID_DEVID), 0);
3918+
#else
3919+
ExpectIntEQ(wc_MlKemKey_Init(fullKey, WC_ML_KEM_1024, NULL,
3920+
INVALID_DEVID), 0);
3921+
ExpectIntEQ(wc_MlKemKey_Init(pubOnlyKey, WC_ML_KEM_1024, NULL,
3922+
INVALID_DEVID), 0);
3923+
#endif
3924+
3925+
/* Get correct sizes for this key type. */
3926+
ExpectIntEQ(wc_MlKemKey_PublicKeySize(fullKey, &pubLen), 0);
3927+
ExpectIntEQ(wc_MlKemKey_CipherTextSize(fullKey, &ctLen), 0);
3928+
3929+
/* Generate a real key pair. */
3930+
ExpectIntEQ(wc_MlKemKey_MakeKey(fullKey, &rng), 0);
3931+
3932+
/* Encapsulate with the full key to get a valid ciphertext. */
3933+
ExpectIntEQ(wc_MlKemKey_Encapsulate(fullKey, ct, ss, &rng), 0);
3934+
3935+
/* Export and import only the public key. */
3936+
ExpectIntEQ(wc_MlKemKey_EncodePublicKey(fullKey, pubBuf, pubLen), 0);
3937+
ExpectIntEQ(wc_MlKemKey_DecodePublicKey(pubOnlyKey, pubBuf, pubLen), 0);
3938+
3939+
/* Decapsulating with a public-key-only object must fail. */
3940+
ExpectIntEQ(wc_MlKemKey_Decapsulate(pubOnlyKey, ssDec, ct, ctLen),
3941+
WC_NO_ERR_TRACE(BAD_STATE_E));
3942+
3943+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
3944+
wc_MlKemKey_Free(pubOnlyKey);
3945+
wc_MlKemKey_Free(fullKey);
3946+
XFREE(pubOnlyKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3947+
XFREE(fullKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3948+
#endif
3949+
#endif
3950+
return EXPECT_RESULT();
3951+
} /* END test_wc_mlkem_decapsulate_pubonly_fails */
3952+

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
@@ -10237,6 +10237,9 @@ int wc_dilithium_sign_ctx_msg(const byte* ctx, byte ctxLen, const byte* msg,
1023710237
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
1023810238
ret = BAD_FUNC_ARG;
1023910239
}
10240+
if ((ret == 0) && (!key->prvKeySet)) {
10241+
ret = BAD_FUNC_ARG;
10242+
}
1024010243

1024110244
#ifdef WOLF_CRYPTO_CB
1024210245
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)