mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 14:00:48 +02:00
Merge pull request #10540 from Frauschi/small_order_check
Reject small-order public keys for Ed25519 and Ed448
This commit is contained in:
@@ -725,3 +725,184 @@ int test_wc_Ed25519KeyToDer_oneasymkey_version(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Ed25519 identity and small-order public keys must be rejected. When
|
||||
* the public key is the identity point (or any small-order point), any
|
||||
* signature of the form (R = [S]B, S) verifies for arbitrary messages
|
||||
* because h*A is the neutral element. Gated on FIPS_VERSION3_GE(7,0,0)
|
||||
* because older FIPS-certified modules do not have this check in their
|
||||
* frozen copy of ed25519.c and would fail this test. */
|
||||
int test_wc_ed25519_reject_small_order_keys(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
|
||||
defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
|
||||
/* Each entry holds an encoded small-order Ed25519 public key. The
|
||||
* sign-bit variants of each y-coordinate are listed explicitly so
|
||||
* the test catches both possible encodings of each y. */
|
||||
static const byte small_order_keys[][ED25519_PUB_KEY_SIZE] = {
|
||||
/* identity (y = 1) */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/* identity with x-sign bit set */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80},
|
||||
/* order 2: y = p - 1 */
|
||||
{0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
/* order 2: y = p - 1 with x-sign bit set */
|
||||
{0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
|
||||
/* non-canonical y = p (decodes to y = 0) */
|
||||
{0xed,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
/* non-canonical y = p with x-sign bit set */
|
||||
{0xed,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
|
||||
/* non-canonical y = p + 1 (decodes to y = 1) */
|
||||
{0xee,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
/* non-canonical y = p + 1 with x-sign bit set */
|
||||
{0xee,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
|
||||
/* order 4: y = 0 */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/* order 4 with x-sign bit set */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80},
|
||||
/* order 8 */
|
||||
{0x26,0xe8,0x95,0x8f,0xc2,0xb2,0x27,0xb0,
|
||||
0x45,0xc3,0xf4,0x89,0xf2,0xef,0x98,0xf0,
|
||||
0xd5,0xdf,0xac,0x05,0xd3,0xc6,0x33,0x39,
|
||||
0xb1,0x38,0x02,0x88,0x6d,0x53,0xfc,0x05},
|
||||
/* order 8 with x-sign bit set */
|
||||
{0x26,0xe8,0x95,0x8f,0xc2,0xb2,0x27,0xb0,
|
||||
0x45,0xc3,0xf4,0x89,0xf2,0xef,0x98,0xf0,
|
||||
0xd5,0xdf,0xac,0x05,0xd3,0xc6,0x33,0x39,
|
||||
0xb1,0x38,0x02,0x88,0x6d,0x53,0xfc,0x85},
|
||||
/* order 8 (other y) */
|
||||
{0xc7,0x17,0x6a,0x70,0x3d,0x4d,0xd8,0x4f,
|
||||
0xba,0x3c,0x0b,0x76,0x0d,0x10,0x67,0x0f,
|
||||
0x2a,0x20,0x53,0xfa,0x2c,0x39,0xcc,0xc6,
|
||||
0x4e,0xc7,0xfd,0x77,0x92,0xac,0x03,0x7a},
|
||||
/* order 8 (other y) with x-sign bit set */
|
||||
{0xc7,0x17,0x6a,0x70,0x3d,0x4d,0xd8,0x4f,
|
||||
0xba,0x3c,0x0b,0x76,0x0d,0x10,0x67,0x0f,
|
||||
0x2a,0x20,0x53,0xfa,0x2c,0x39,0xcc,0xc6,
|
||||
0x4e,0xc7,0xfd,0x77,0x92,0xac,0x03,0xfa},
|
||||
};
|
||||
/* Forged signature: R = B (base point), S = 1.
|
||||
* With public key A = identity, S*B - h*A = B = R for any message. */
|
||||
static const byte forged_sig[ED25519_SIG_SIZE] = {
|
||||
0x58,0x66,0x66,0x66,0x66,0x66,0x66,0x66,
|
||||
0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,
|
||||
0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,
|
||||
0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,
|
||||
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
};
|
||||
ed25519_key key;
|
||||
word32 i;
|
||||
word32 num_keys = (word32)(sizeof(small_order_keys) / ED25519_PUB_KEY_SIZE);
|
||||
|
||||
/* (1) Untrusted wc_ed25519_import_public must reject every small-order
|
||||
* encoding (it runs wc_ed25519_check_key as part of the import). */
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
int rc;
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed25519_init(&key), 0);
|
||||
rc = wc_ed25519_import_public(small_order_keys[i],
|
||||
ED25519_PUB_KEY_SIZE, &key);
|
||||
if (rc != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
fprintf(stderr, "small_order_keys[%u]: import_public returned %d, "
|
||||
"expected PUBLIC_KEY_E\n", (unsigned)i, rc);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
||||
wc_ed25519_free(&key);
|
||||
}
|
||||
|
||||
/* (2) wc_ed25519_check_key called directly must also reject. Guards
|
||||
* against a refactor that moves the small-order check out of
|
||||
* check_key and into the import path: (1) would still pass, but the
|
||||
* documented check_key contract would silently regress. */
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
int rc;
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed25519_init(&key), 0);
|
||||
/* trusted = 1 bypasses the import-time check_key call so the
|
||||
* direct check_key below is what's under test. */
|
||||
ExpectIntEQ(wc_ed25519_import_public_ex(small_order_keys[i],
|
||||
ED25519_PUB_KEY_SIZE, &key, 1), 0);
|
||||
rc = wc_ed25519_check_key(&key);
|
||||
if (rc != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
fprintf(stderr, "small_order_keys[%u]: check_key returned %d, "
|
||||
"expected PUBLIC_KEY_E\n", (unsigned)i, rc);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
||||
wc_ed25519_free(&key);
|
||||
}
|
||||
|
||||
/* (3) Even a "trusted" import (which bypasses wc_ed25519_check_key)
|
||||
* must not let wc_ed25519_verify_msg accept a forged signature against
|
||||
* an identity public key. Test both the canonical encoding (y = 1,
|
||||
* small_order_keys[0]) and the non-canonical encoding (y = p + 1,
|
||||
* small_order_keys[6]) so the verify-side check is exercised against
|
||||
* the canonical-form bypass route, not just the byte-for-byte
|
||||
* identity. The forged sig (R = B, S = 1) verifies for an identity
|
||||
* public key only - other small-order points would reject it on the
|
||||
* math alone, so they aren't useful here. */
|
||||
{
|
||||
static const word32 identity_indices[] = { 0, 6 };
|
||||
const char* msg = "forged message";
|
||||
word32 j;
|
||||
|
||||
for (j = 0;
|
||||
j < sizeof(identity_indices)/sizeof(identity_indices[0]);
|
||||
j++) {
|
||||
word32 idx = identity_indices[j];
|
||||
int verify_result = 1;
|
||||
int rc;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed25519_init(&key), 0);
|
||||
ExpectIntEQ(wc_ed25519_import_public_ex(small_order_keys[idx],
|
||||
ED25519_PUB_KEY_SIZE, &key, 1), 0);
|
||||
rc = wc_ed25519_verify_msg(forged_sig, sizeof(forged_sig),
|
||||
(const byte*)msg, (word32)XSTRLEN(msg), &verify_result, &key);
|
||||
if (rc != WC_NO_ERR_TRACE(BAD_FUNC_ARG) || verify_result != 0) {
|
||||
fprintf(stderr, "verify_msg with identity-equiv "
|
||||
"small_order_keys[%u]: rc=%d verify_result=%d "
|
||||
"(expected BAD_FUNC_ARG and 0)\n",
|
||||
(unsigned)idx, rc, verify_result);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_result, 0);
|
||||
wc_ed25519_free(&key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ int test_wc_Ed25519PublicKeyToDer(void);
|
||||
int test_wc_Ed25519KeyToDer(void);
|
||||
int test_wc_Ed25519PrivateKeyToDer(void);
|
||||
int test_wc_Ed25519KeyToDer_oneasymkey_version(void);
|
||||
int test_wc_ed25519_reject_small_order_keys(void);
|
||||
|
||||
#define TEST_ED25519_DECLS \
|
||||
TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \
|
||||
@@ -51,6 +52,7 @@ int test_wc_Ed25519KeyToDer_oneasymkey_version(void);
|
||||
TEST_DECL_GROUP("ed25519", test_wc_Ed25519PublicKeyToDer), \
|
||||
TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer), \
|
||||
TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyToDer), \
|
||||
TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer_oneasymkey_version)
|
||||
TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer_oneasymkey_version), \
|
||||
TEST_DECL_GROUP("ed25519", test_wc_ed25519_reject_small_order_keys)
|
||||
|
||||
#endif /* WOLFCRYPT_TEST_ED25519_H */
|
||||
|
||||
@@ -649,3 +649,224 @@ int test_wc_Ed448KeyToDer_oneasymkey_version(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Ed448 identity and small-order public keys must be rejected.
|
||||
* Edwards448 has cofactor 4, so the small-order subgroup contains the
|
||||
* identity, an order-2 point, and two order-4 points. With any of these
|
||||
* as the public key, h*A is the neutral element and forged signatures
|
||||
* verify for arbitrary messages. Gated on FIPS_VERSION3_GE(7,0,0)
|
||||
* because older FIPS-certified modules do not have this check in their
|
||||
* frozen copy of ed448.c. */
|
||||
int test_wc_ed448_reject_small_order_keys(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
|
||||
defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
|
||||
/* Two regressions are guarded here. Both sign-bit variants of each
|
||||
* y are listed so weakening the "clear all of byte 56" mask in
|
||||
* ed448_is_small_order() would be caught. The non-canonical rows
|
||||
* (y = p, y = p + 1) guard against dropping the canonical-form
|
||||
* coverage: fe448_from_bytes reads bytes 0-55 modulo p with no
|
||||
* canonical-form check, so y = p decodes to 0 and y = p + 1
|
||||
* decodes to 1, both of which are small order. */
|
||||
static const byte small_order_keys[][ED448_PUB_KEY_SIZE] = {
|
||||
/* identity (y = 1), sign 0 */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00},
|
||||
/* identity (y = 1), sign bit set */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x80},
|
||||
/* order 4: y = 0, x-sign 0 */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00},
|
||||
/* order 4: y = 0, x-sign 1 */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x80},
|
||||
/* order 2: y = p - 1, x = 0, sign 0 */
|
||||
{0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
/* order 2: y = p - 1, sign bit set */
|
||||
{0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x80},
|
||||
/* non-canonical y = p (decodes to y = 0), sign 0 */
|
||||
{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
/* non-canonical y = p, sign bit set */
|
||||
{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x80},
|
||||
/* non-canonical y = p + 1 (decodes to y = 1), sign 0 */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
/* non-canonical y = p + 1, sign bit set */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x80},
|
||||
};
|
||||
/* Arbitrary signature bytes: S = 1 (must be below the Ed448 group
|
||||
* order or wc_ed448_verify_msg() returns BAD_FUNC_ARG before the
|
||||
* small-order check has a chance to fire). The R bytes do not need
|
||||
* to encode a valid curve point for this test - the small-order
|
||||
* defence in ed448_verify_msg_final_with_sha() rejects the public
|
||||
* key before the R/S verification math runs. */
|
||||
static const byte forged_sig[ED448_SIG_SIZE] = {
|
||||
/* R: 57 bytes of arbitrary data (last byte 0 to satisfy the
|
||||
* spec-mandated zero of byte 56 bits 0-6; sign bit doesn't
|
||||
* matter here). */
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,
|
||||
/* S = 1 */
|
||||
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00
|
||||
};
|
||||
ed448_key key;
|
||||
word32 i;
|
||||
word32 num_keys = (word32)(sizeof(small_order_keys) / ED448_PUB_KEY_SIZE);
|
||||
|
||||
/* (1) Untrusted wc_ed448_import_public must reject every small-order
|
||||
* encoding (it runs wc_ed448_check_key as part of the import). */
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
int rc;
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed448_init(&key), 0);
|
||||
rc = wc_ed448_import_public(small_order_keys[i],
|
||||
ED448_PUB_KEY_SIZE, &key);
|
||||
if (rc != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
fprintf(stderr, "small_order_keys[%u]: import_public returned %d, "
|
||||
"expected PUBLIC_KEY_E\n", (unsigned)i, rc);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
||||
wc_ed448_free(&key);
|
||||
}
|
||||
|
||||
/* (2) wc_ed448_check_key called directly must also reject. Guards
|
||||
* against a refactor that moves the small-order check out of
|
||||
* check_key and into the import path: (1) would still pass, but the
|
||||
* documented check_key contract would silently regress. */
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
int rc;
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed448_init(&key), 0);
|
||||
/* trusted = 1 bypasses the import-time check_key call so the
|
||||
* direct check_key below is what's under test. */
|
||||
ExpectIntEQ(wc_ed448_import_public_ex(small_order_keys[i],
|
||||
ED448_PUB_KEY_SIZE, &key, 1), 0);
|
||||
rc = wc_ed448_check_key(&key);
|
||||
if (rc != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
fprintf(stderr, "small_order_keys[%u]: check_key returned %d, "
|
||||
"expected PUBLIC_KEY_E\n", (unsigned)i, rc);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
||||
wc_ed448_free(&key);
|
||||
}
|
||||
|
||||
/* (3) Even a "trusted" import (which bypasses wc_ed448_check_key)
|
||||
* must not let wc_ed448_verify_msg accept a forged signature against
|
||||
* an identity public key. Test both the canonical encoding (y = 1,
|
||||
* small_order_keys[0]) and the non-canonical encoding (y = p + 1,
|
||||
* small_order_keys[8]) so the verify-side check is exercised against
|
||||
* the canonical-form bypass route, not just the byte-for-byte
|
||||
* identity. */
|
||||
{
|
||||
static const word32 identity_indices[] = { 0, 8 };
|
||||
const char* msg = "forged message";
|
||||
word32 j;
|
||||
|
||||
for (j = 0;
|
||||
j < sizeof(identity_indices)/sizeof(identity_indices[0]);
|
||||
j++) {
|
||||
word32 idx = identity_indices[j];
|
||||
int verify_result = 1;
|
||||
int rc;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ed448_init(&key), 0);
|
||||
ExpectIntEQ(wc_ed448_import_public_ex(small_order_keys[idx],
|
||||
ED448_PUB_KEY_SIZE, &key, 1), 0);
|
||||
rc = wc_ed448_verify_msg(forged_sig, sizeof(forged_sig),
|
||||
(const byte*)msg, (word32)XSTRLEN(msg), &verify_result,
|
||||
&key, NULL, 0);
|
||||
if (rc != WC_NO_ERR_TRACE(BAD_FUNC_ARG) || verify_result != 0) {
|
||||
fprintf(stderr, "verify_msg with identity-equiv "
|
||||
"small_order_keys[%u]: rc=%d verify_result=%d "
|
||||
"(expected BAD_FUNC_ARG and 0)\n",
|
||||
(unsigned)idx, rc, verify_result);
|
||||
}
|
||||
ExpectIntEQ(rc, WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_result, 0);
|
||||
wc_ed448_free(&key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ int test_wc_Ed448PublicKeyToDer(void);
|
||||
int test_wc_Ed448KeyToDer(void);
|
||||
int test_wc_Ed448PrivateKeyToDer(void);
|
||||
int test_wc_Ed448KeyToDer_oneasymkey_version(void);
|
||||
int test_wc_ed448_reject_small_order_keys(void);
|
||||
|
||||
#define TEST_ED448_DECLS \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \
|
||||
@@ -51,6 +52,7 @@ int test_wc_Ed448KeyToDer_oneasymkey_version(void);
|
||||
TEST_DECL_GROUP("ed448", test_wc_Ed448PublicKeyToDer), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyToDer), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer_oneasymkey_version)
|
||||
TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer_oneasymkey_version), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_reject_small_order_keys)
|
||||
|
||||
#endif /* WOLFCRYPT_TEST_ED448_H */
|
||||
|
||||
+72
-3
@@ -205,6 +205,64 @@ static int ed25519_hash(ed25519_key* key, const byte* in, word32 inLen,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reject small-order Ed25519 public keys: h*A vanishes during verification
|
||||
* so any (R = [S]B, S) verifies for an arbitrary message. */
|
||||
static int ed25519_is_small_order(const byte p[ED25519_PUB_KEY_SIZE])
|
||||
{
|
||||
/* y-coordinates of every order-1/2/4/8 point plus the two non-canonical
|
||||
* encodings y = p / y = p+1. Sign bit masked before compare. Only
|
||||
* {y, y + p} fits in 32 bytes (2p overflows the 255-bit y field), so
|
||||
* listing y and y + p exhausts the reachable encodings for each
|
||||
* small-order y. */
|
||||
static const byte small_order_y[][ED25519_PUB_KEY_SIZE] = {
|
||||
/* order 4: y = 0 */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/* order 1: y = 1 (identity) */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/* order 8 */
|
||||
{0x26,0xe8,0x95,0x8f,0xc2,0xb2,0x27,0xb0,
|
||||
0x45,0xc3,0xf4,0x89,0xf2,0xef,0x98,0xf0,
|
||||
0xd5,0xdf,0xac,0x05,0xd3,0xc6,0x33,0x39,
|
||||
0xb1,0x38,0x02,0x88,0x6d,0x53,0xfc,0x05},
|
||||
/* order 8 */
|
||||
{0xc7,0x17,0x6a,0x70,0x3d,0x4d,0xd8,0x4f,
|
||||
0xba,0x3c,0x0b,0x76,0x0d,0x10,0x67,0x0f,
|
||||
0x2a,0x20,0x53,0xfa,0x2c,0x39,0xcc,0xc6,
|
||||
0x4e,0xc7,0xfd,0x77,0x92,0xac,0x03,0x7a},
|
||||
/* order 2: y = p - 1 */
|
||||
{0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
/* non-canonical y = p (decodes to y = 0) */
|
||||
{0xed,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
/* non-canonical y = p + 1 (decodes to y = 1) */
|
||||
{0xee,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f},
|
||||
};
|
||||
byte y[ED25519_PUB_KEY_SIZE];
|
||||
word32 i;
|
||||
|
||||
XMEMCPY(y, p, ED25519_PUB_KEY_SIZE);
|
||||
y[ED25519_PUB_KEY_SIZE - 1] &= 0x7f;
|
||||
for (i = 0; i < sizeof(small_order_y) / ED25519_PUB_KEY_SIZE; i++) {
|
||||
if (XMEMCMP(y, small_order_y[i], ED25519_PUB_KEY_SIZE) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ED25519_MAKE_KEY
|
||||
#if FIPS_VERSION3_GE(6,0,0)
|
||||
/* Performs a Pairwise Consistency Test on an Ed25519 key pair.
|
||||
@@ -808,6 +866,13 @@ static int ed25519_verify_msg_final_with_sha(const byte* sig, word32 sigLen,
|
||||
if (i == -1)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
/* Defence in depth: also catch small-order keys imported with trusted=1. */
|
||||
if (ed25519_is_small_order(key->p)) {
|
||||
WOLFSSL_MSG("Ed25519 small-order public key rejected during "
|
||||
"signature verification");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* uncompress A (public key), test if valid, and negate it */
|
||||
#ifndef FREESCALE_LTC_ECC
|
||||
if (ge_frombytes_negate_vartime(&A, key->p) != 0)
|
||||
@@ -1502,6 +1567,13 @@ int wc_ed25519_check_key(ed25519_key* key)
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
/* Reject small-order pub key before the priv-vs-pub compare so the
|
||||
* diagnostic isn't masked by a "mismatch" error. */
|
||||
if ((ret == 0) && ed25519_is_small_order(key->p)) {
|
||||
WOLFSSL_MSG("Ed25519 small-order public key rejected during key check");
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ED25519_MAKE_KEY
|
||||
/* If we have a private key just make the public key and compare. */
|
||||
if ((ret == 0) && (key->privKeySet)) {
|
||||
@@ -1521,9 +1593,6 @@ int wc_ed25519_check_key(ed25519_key* key)
|
||||
&& (!key->privKeySet)
|
||||
#endif
|
||||
) {
|
||||
/* Verify that Q is not identity element 0.
|
||||
* 0 has no representation for Ed25519. */
|
||||
|
||||
/* Verify that xQ and yQ are integers in the interval [0, p - 1].
|
||||
* Only have yQ so check that ordinate. p = 2^255 - 19 */
|
||||
if ((key->p[ED25519_PUB_KEY_SIZE - 1] & 0x7f) == 0x7f) {
|
||||
|
||||
+83
-27
@@ -238,6 +238,77 @@ static int ed448_pairwise_consistency_test(ed448_key* key, WC_RNG* rng)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reject small-order Ed448 public keys: h*A vanishes during verification
|
||||
* so any (R = [S]B, S) verifies for an arbitrary message. Cofactor is 4. */
|
||||
static int ed448_is_small_order(const byte p[ED448_PUB_KEY_SIZE])
|
||||
{
|
||||
/* y-coordinates of every order-1/2/4 point plus the non-canonical
|
||||
* encodings y = p / y = p+1. Byte 56 is cleared in both table and
|
||||
* input before compare, masking the x-sign bit and the
|
||||
* spec-mandated-zero (but decoder-ignored) bits 0-6. The decoder
|
||||
* (fe448_from_bytes) reads bytes 0-55 modulo p with no canonical-form
|
||||
* check, so y = p decodes to 0 and y = p+1 decodes to 1; both must
|
||||
* be rejected here. Only {y, y + p} fits in 56 bytes (2p overflows),
|
||||
* so listing y and y + p exhausts the reachable encodings. */
|
||||
static const byte small_order_y[][ED448_PUB_KEY_SIZE] = {
|
||||
/* order 1: identity y = 1, x = 0 */
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00},
|
||||
/* order 4: y = 0 (x = +/-1; sign bit covered by mask) */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00},
|
||||
/* order 2: y = p - 1, x = 0; p = 2^448 - 2^224 - 1 */
|
||||
{0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
/* non-canonical y = p (decodes to y = 0) */
|
||||
{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
/* non-canonical y = p + 1 (decodes to y = 1) */
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00},
|
||||
};
|
||||
byte y[ED448_PUB_KEY_SIZE];
|
||||
word32 i;
|
||||
|
||||
XMEMCPY(y, p, ED448_PUB_KEY_SIZE);
|
||||
y[ED448_PUB_KEY_SIZE - 1] = 0;
|
||||
for (i = 0; i < sizeof(small_order_y) / ED448_PUB_KEY_SIZE; i++) {
|
||||
if (XMEMCMP(y, small_order_y[i], ED448_PUB_KEY_SIZE) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Derive the public key for the private key.
|
||||
*
|
||||
* key [in] Ed448 key object.
|
||||
@@ -731,16 +802,11 @@ 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;
|
||||
/* Defence in depth: also catch small-order keys imported with trusted=1. */
|
||||
if (ed448_is_small_order(key->p)) {
|
||||
WOLFSSL_MSG("Ed448 small-order public key rejected during "
|
||||
"signature verification");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* uncompress A (public key), test if valid, and negate it */
|
||||
@@ -1412,6 +1478,13 @@ int wc_ed448_check_key(ed448_key* key)
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
/* Reject small-order pub key before the priv-vs-pub compare so the
|
||||
* diagnostic isn't masked by a "mismatch" error. */
|
||||
if ((ret == 0) && ed448_is_small_order(key->p)) {
|
||||
WOLFSSL_MSG("Ed448 small-order public key rejected during key check");
|
||||
ret = PUBLIC_KEY_E;
|
||||
}
|
||||
|
||||
/* If we have a private key just make the public key and compare. */
|
||||
if ((ret == 0) && key->privKeySet) {
|
||||
ret = wc_ed448_make_public(key, pubKey, sizeof(pubKey));
|
||||
@@ -1421,23 +1494,6 @@ int wc_ed448_check_key(ed448_key* key)
|
||||
}
|
||||
/* No private key, check Y is valid. */
|
||||
else if ((ret == 0) && (!key->privKeySet)) {
|
||||
/* 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
|
||||
|
||||
+63
-4
@@ -44767,7 +44767,9 @@ static wc_test_ret_t ed25519_test_check_key(void)
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
|
||||
};
|
||||
/* Y-ordinate value equal to prime - 1. */
|
||||
/* Y-ordinate value equal to prime - 1. Older FIPS modules accept
|
||||
* this as a valid key; the current source rejects it as an order-2
|
||||
* point. */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte key_y_is_p_minus_1[] = {
|
||||
0x40,
|
||||
0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
@@ -44775,6 +44777,15 @@ static wc_test_ret_t ed25519_test_check_key(void)
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
|
||||
};
|
||||
/* RFC 8032 section 7.1 test-vector public key: a genuinely valid
|
||||
* Ed25519 point used as a positive control. */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte key_good[] = {
|
||||
0x40,
|
||||
0xd7,0x5a,0x98,0x01,0x82,0xb1,0x0a,0xb7,
|
||||
0xd5,0x4b,0xfe,0xd3,0xc9,0x64,0x07,0x3a,
|
||||
0x0e,0xe1,0x72,0xf3,0xda,0xa6,0x23,0x25,
|
||||
0xaf,0x02,0x1a,0x68,0xf7,0x07,0x51,0x1a,
|
||||
};
|
||||
ed25519_key key;
|
||||
int ret;
|
||||
int res = 0;
|
||||
@@ -44807,9 +44818,26 @@ static wc_test_ret_t ed25519_test_check_key(void)
|
||||
}
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Load good public key only and perform checks. */
|
||||
ret = wc_ed25519_import_public(key_y_is_p_minus_1,
|
||||
ED25519_PUB_KEY_SIZE + 1, &key);
|
||||
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
|
||||
/* y = p - 1 is an order-2 point; check_key rejects it because
|
||||
* h*A is the neutral element for small-order public keys and
|
||||
* forged signatures would otherwise verify. */
|
||||
if (ret != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
#else
|
||||
/* Older FIPS modules accept this order-2 point. */
|
||||
if (ret != 0) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Positive control: a real Ed25519 public key must be accepted. */
|
||||
ret = wc_ed25519_import_public(key_good, ED25519_PUB_KEY_SIZE + 1,
|
||||
&key);
|
||||
if (ret != 0) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
@@ -46484,7 +46512,9 @@ static wc_test_ret_t ed448_test_check_key(void)
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff
|
||||
};
|
||||
/* Y-ordinate value equal to prime - 1. */
|
||||
/* Y-ordinate value equal to prime - 1. Older FIPS modules accept
|
||||
* this as a valid key; the current source rejects it as an order-2
|
||||
* point. */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte key_y_is_p_minus_1[] = {
|
||||
0x40,
|
||||
0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
@@ -46496,6 +46526,19 @@ static wc_test_ret_t ed448_test_check_key(void)
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff
|
||||
};
|
||||
/* RFC 8032 section 7.4 test-vector public key: a genuinely valid
|
||||
* Ed448 point used as a positive control. */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte key_good[] = {
|
||||
0x40,
|
||||
0x5f,0xd7,0x44,0x9b,0x59,0xb4,0x61,0xfd,
|
||||
0x2c,0xe7,0x87,0xec,0x61,0x6a,0xd4,0x6a,
|
||||
0x1d,0xa1,0x34,0x24,0x85,0xa7,0x0e,0x1f,
|
||||
0x8a,0x0e,0xa7,0x5d,0x80,0xe9,0x67,0x78,
|
||||
0xed,0xf1,0x24,0x76,0x9b,0x46,0xc7,0x06,
|
||||
0x1b,0xd6,0x78,0x3d,0xf1,0xe5,0x0f,0x6c,
|
||||
0xd1,0xfa,0x1a,0xbe,0xaf,0xe8,0x25,0x61,
|
||||
0x80
|
||||
};
|
||||
ed448_key key;
|
||||
int ret;
|
||||
int res = 0;
|
||||
@@ -46528,9 +46571,25 @@ static wc_test_ret_t ed448_test_check_key(void)
|
||||
}
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Load good public key only and perform checks. */
|
||||
ret = wc_ed448_import_public(key_y_is_p_minus_1, ED448_PUB_KEY_SIZE + 1,
|
||||
&key);
|
||||
#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)
|
||||
/* y = p - 1 is an order-2 point; check_key rejects it because
|
||||
* h*A is the neutral element for small-order public keys and
|
||||
* forged signatures would otherwise verify. */
|
||||
if (ret != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
#else
|
||||
/* Older FIPS modules accept this order-2 point. */
|
||||
if (ret != 0) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Positive control: a real Ed448 public key must be accepted. */
|
||||
ret = wc_ed448_import_public(key_good, ED448_PUB_KEY_SIZE + 1, &key);
|
||||
if (ret != 0) {
|
||||
res = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user