Skip to content

Commit 2237297

Browse files
committed
Properly reject Ed448 identity public key
Reported by: Nicholas Carlini <npc@anthropic.com>
1 parent cece804 commit 2237297

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

tests/api.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34758,6 +34758,42 @@ static int test_DhAgree_rejects_p_minus_1(void)
3475834758
return EXPECT_RESULT();
3475934759
}
3476034760

34761+
/* Test: Ed448 must reject identity public key (0,1) */
34762+
static int test_ed448_rejects_identity_key(void)
34763+
{
34764+
EXPECT_DECLS;
34765+
#if defined(HAVE_ED448) && !defined(HAVE_SELFTEST) && \
34766+
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
34767+
ed448_key key;
34768+
byte identity[ED448_PUB_KEY_SIZE];
34769+
byte forged_sig[ED448_SIG_SIZE];
34770+
const byte msg[] = "test message";
34771+
int res = 0;
34772+
34773+
XMEMSET(identity, 0, sizeof(identity));
34774+
identity[0] = 0x01; /* identity (0,1) encoding */
34775+
34776+
XMEMSET(forged_sig, 0, sizeof(forged_sig));
34777+
forged_sig[0] = 0x01; /* R = identity, S = 0 */
34778+
34779+
ExpectIntEQ(wc_ed448_init(&key), 0);
34780+
34781+
/* The identity public key must be rejected at import time. */
34782+
ExpectIntNE(wc_ed448_import_public(identity, sizeof(identity), &key), 0);
34783+
34784+
/* If import somehow succeeded, verify must also reject the forgery. */
34785+
if (EXPECT_SUCCESS() && key.pubKeySet) {
34786+
int verifyRet = wc_ed448_verify_msg(forged_sig, sizeof(forged_sig),
34787+
msg, sizeof(msg) - 1,
34788+
&res, &key, NULL, 0);
34789+
ExpectTrue(verifyRet != 0 || res == 0);
34790+
}
34791+
34792+
wc_ed448_free(&key);
34793+
#endif
34794+
return EXPECT_RESULT();
34795+
}
34796+
3476134797
TEST_CASE testCases[] = {
3476234798
TEST_DECL(test_fileAccess),
3476334799

@@ -35573,6 +35609,7 @@ TEST_CASE testCases[] = {
3557335609
TEST_TLS_DECLS,
3557435610
TEST_DECL(test_wc_DhSetNamedKey),
3557535611
TEST_DECL(test_DhAgree_rejects_p_minus_1),
35612+
TEST_DECL(test_ed448_rejects_identity_key),
3557635613

3557735614
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
3557835615
TEST_DECL(test_sniffer_chain_input_overflow),

wolfcrypt/src/ed448.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,18 @@ static int ed448_verify_msg_final_with_sha(const byte* sig, word32 sigLen,
711711
if (i == -1)
712712
return BAD_FUNC_ARG;
713713

714+
/* Reject identity public key (0,1): 0x01 followed by 56 zero bytes. */
715+
{
716+
int isIdentity = (key->p[0] == 0x01);
717+
int j;
718+
for (j = 1; j < ED448_PUB_KEY_SIZE && isIdentity; j++) {
719+
if (key->p[j] != 0x00)
720+
isIdentity = 0;
721+
}
722+
if (isIdentity)
723+
return BAD_FUNC_ARG;
724+
}
725+
714726
/* uncompress A (public key), test if valid, and negate it */
715727
if (ge448_from_bytes_negate_vartime(&A, key->p) != 0)
716728
return BAD_FUNC_ARG;
@@ -1335,14 +1347,28 @@ int wc_ed448_check_key(ed448_key* key)
13351347
}
13361348
/* No private key, check Y is valid. */
13371349
else if ((ret == 0) && (!key->privKeySet)) {
1338-
/* Verify that Q is not identity element 0.
1339-
* 0 has no representation for Ed448. */
1350+
/* Reject the identity element (0, 1).
1351+
* Encoding: 0x01 followed by 56 zero bytes. */
1352+
{
1353+
int isIdentity = 1;
1354+
int i;
1355+
if (key->p[0] != 0x01)
1356+
isIdentity = 0;
1357+
for (i = 1; i < ED448_PUB_KEY_SIZE && isIdentity; i++) {
1358+
if (key->p[i] != 0x00)
1359+
isIdentity = 0;
1360+
}
1361+
if (isIdentity) {
1362+
WOLFSSL_MSG("Ed448 public key is the identity element");
1363+
ret = PUBLIC_KEY_E;
1364+
}
1365+
}
13401366

13411367
/* Verify that xQ and yQ are integers in the interval [0, p - 1].
13421368
* Only have yQ so check that ordinate.
13431369
* p = 2^448-2^224-1 = 0xff..fe..ff
13441370
*/
1345-
{
1371+
if (ret == 0) {
13461372
int i;
13471373
ret = PUBLIC_KEY_E;
13481374

0 commit comments

Comments
 (0)