Skip to content

Commit af19d9d

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

2 files changed

Lines changed: 65 additions & 3 deletions

File tree

tests/api.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34714,6 +34714,41 @@ static int test_DhAgree_rejects_p_minus_1(void)
3471434714
return EXPECT_RESULT();
3471534715
}
3471634716

34717+
/* Test: Ed448 must reject identity public key (0,1) */
34718+
static int test_ed448_rejects_identity_key(void)
34719+
{
34720+
EXPECT_DECLS;
34721+
#ifdef HAVE_ED448
34722+
ed448_key key;
34723+
byte identity[ED448_PUB_KEY_SIZE];
34724+
byte forged_sig[ED448_SIG_SIZE];
34725+
const byte msg[] = "test message";
34726+
int res = 0;
34727+
34728+
XMEMSET(identity, 0, sizeof(identity));
34729+
identity[0] = 0x01; /* identity (0,1) encoding */
34730+
34731+
XMEMSET(forged_sig, 0, sizeof(forged_sig));
34732+
forged_sig[0] = 0x01; /* R = identity, S = 0 */
34733+
34734+
ExpectIntEQ(wc_ed448_init(&key), 0);
34735+
34736+
/* The identity public key must be rejected at import time. */
34737+
ExpectIntNE(wc_ed448_import_public(identity, sizeof(identity), &key), 0);
34738+
34739+
/* If import somehow succeeded, verify must also reject the forgery. */
34740+
if (EXPECT_SUCCESS() && key.pubKeySet) {
34741+
int verifyRet = wc_ed448_verify_msg(forged_sig, sizeof(forged_sig),
34742+
msg, sizeof(msg) - 1,
34743+
&res, &key, NULL, 0);
34744+
ExpectTrue(verifyRet != 0 || res == 0);
34745+
}
34746+
34747+
wc_ed448_free(&key);
34748+
#endif
34749+
return EXPECT_RESULT();
34750+
}
34751+
3471734752
TEST_CASE testCases[] = {
3471834753
TEST_DECL(test_fileAccess),
3471934754

@@ -35528,6 +35563,7 @@ TEST_CASE testCases[] = {
3552835563
TEST_TLS_DECLS,
3552935564
TEST_DECL(test_wc_DhSetNamedKey),
3553035565
TEST_DECL(test_DhAgree_rejects_p_minus_1),
35566+
TEST_DECL(test_ed448_rejects_identity_key),
3553135567

3553235568
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
3553335569
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)