diff --git a/tests/api.c b/tests/api.c index b655df9931..4a4bf471d2 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34758,6 +34758,42 @@ static int test_DhAgree_rejects_p_minus_1(void) return EXPECT_RESULT(); } +/* Test: Ed448 must reject identity public key (0,1) */ +static int test_ed448_rejects_identity_key(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED448) && !defined(HAVE_SELFTEST) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) + ed448_key key; + byte identity[ED448_PUB_KEY_SIZE]; + byte forged_sig[ED448_SIG_SIZE]; + const byte msg[] = "test message"; + int res = 0; + + XMEMSET(identity, 0, sizeof(identity)); + identity[0] = 0x01; /* identity (0,1) encoding */ + + XMEMSET(forged_sig, 0, sizeof(forged_sig)); + forged_sig[0] = 0x01; /* R = identity, S = 0 */ + + ExpectIntEQ(wc_ed448_init(&key), 0); + + /* The identity public key must be rejected at import time. */ + ExpectIntNE(wc_ed448_import_public(identity, sizeof(identity), &key), 0); + + /* If import somehow succeeded, verify must also reject the forgery. */ + if (EXPECT_SUCCESS() && key.pubKeySet) { + int verifyRet = wc_ed448_verify_msg(forged_sig, sizeof(forged_sig), + msg, sizeof(msg) - 1, + &res, &key, NULL, 0); + ExpectTrue(verifyRet != 0 || res == 0); + } + + wc_ed448_free(&key); +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35573,6 +35609,7 @@ TEST_CASE testCases[] = { TEST_TLS_DECLS, TEST_DECL(test_wc_DhSetNamedKey), TEST_DECL(test_DhAgree_rejects_p_minus_1), + TEST_DECL(test_ed448_rejects_identity_key), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) TEST_DECL(test_sniffer_chain_input_overflow), diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 37674cad51..6525b5bb4a 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -711,6 +711,18 @@ static int ed448_verify_msg_final_with_sha(const byte* sig, word32 sigLen, if (i == -1) return BAD_FUNC_ARG; + /* Reject identity public key (0,1): 0x01 followed by 56 zero bytes. */ + { + int isIdentity = (key->p[0] == 0x01); + int j; + for (j = 1; j < ED448_PUB_KEY_SIZE && isIdentity; j++) { + if (key->p[j] != 0x00) + isIdentity = 0; + } + if (isIdentity) + return BAD_FUNC_ARG; + } + /* uncompress A (public key), test if valid, and negate it */ if (ge448_from_bytes_negate_vartime(&A, key->p) != 0) return BAD_FUNC_ARG; @@ -1335,14 +1347,28 @@ int wc_ed448_check_key(ed448_key* key) } /* No private key, check Y is valid. */ else if ((ret == 0) && (!key->privKeySet)) { - /* Verify that Q is not identity element 0. - * 0 has no representation for Ed448. */ + /* Reject the identity element (0, 1). + * Encoding: 0x01 followed by 56 zero bytes. */ + { + int isIdentity = 1; + int i; + if (key->p[0] != 0x01) + isIdentity = 0; + for (i = 1; i < ED448_PUB_KEY_SIZE && isIdentity; i++) { + if (key->p[i] != 0x00) + isIdentity = 0; + } + if (isIdentity) { + WOLFSSL_MSG("Ed448 public key is the identity element"); + ret = PUBLIC_KEY_E; + } + } /* Verify that xQ and yQ are integers in the interval [0, p - 1]. * Only have yQ so check that ordinate. * p = 2^448-2^224-1 = 0xff..fe..ff */ - { + if (ret == 0) { int i; ret = PUBLIC_KEY_E;