From 71d22d96582c1881f59b37c70978550b8d2d1e10 Mon Sep 17 00:00:00 2001 From: Sumit Kumar <2303031050618@paruluniversity.ac.in> Date: Fri, 4 Sep 2026 14:49:18 +0530 Subject: [PATCH] fix: repo does not build at all with EBLDR_BUILD_TESTS=ON --- core/ed25519_verify.c | 58 ++++++++------------------------------- include/eos_image.h | 16 +++++++---- tests/unit/test_ed25519.c | 57 ++++++++++++++++++++++++++++---------- 3 files changed, 64 insertions(+), 67 deletions(-) diff --git a/core/ed25519_verify.c b/core/ed25519_verify.c index d36cc7c..47cbeb6 100644 --- a/core/ed25519_verify.c +++ b/core/ed25519_verify.c @@ -276,6 +276,16 @@ static void scalarmult(gf r[4], gf q[4], const uint8_t *s) } } +static void scalarbase(gf r[4], const uint8_t *s) +{ + gf q[4]; + fe_copy16(q[0], BX); + fe_copy16(q[1], BY); + fe_copy16(q[2], gf1); + fe_mul(q[3], BX, BY); + scalarmult(r, q, s); +} + /* The identity encodes as y = 1 with the sign bit clear. Takes a point rather * than an encoding so both callers below can pass one directly. */ static int point_is_identity(gf p[4]) @@ -300,53 +310,7 @@ static int point_is_identity(gf p[4]) * for every point whose order divides L -- including the identity itself, * whose order is 1 -- so the identity must also be excluded explicitly. * - * The scalar is derived from ORDER_L rather than written out a second time, - * so there is no separate constant to transcribe wrongly: a mistyped L would - * reject valid keys, and only in the field. - * - * A arrives negated from unpackneg(). [L](-A) = -[L]A and the identity is its - * own negation, so neither condition is affected by the sign. - * - * Formulation taken from eBoot#57 by @muhammadburhandevv-hub, which reached - * this before I did and states both conditions in one expression. - */ -static int key_has_prime_order(gf A[4]) -{ - uint8_t order_l[32]; - gf q[4], multiple[4]; - int i; - - for (i = 0; i < 32; i++) - order_l[i] = (uint8_t)ORDER_L[i]; - for (i = 0; i < 4; i++) - fe_copy16(q[i], A[i]); - - scalarmult(multiple, q, order_l); - return point_is_identity(multiple) && !point_is_identity(A); -} - -static void scalarbase(gf r[4], const uint8_t *s) -{ - gf q[4]; - fe_copy16(q[0], BX); - fe_copy16(q[1], BY); - fe_copy16(q[2], gf1); - fe_mul(q[3], BX, BY); - scalarmult(r, q, s); -} - -static int point_is_identity(gf p[4]) -{ - uint8_t encoded[32]; - point_pack(encoded, p); - - uint8_t diff = (uint8_t)(encoded[0] ^ 1U); - for (int i = 1; i < 32; i++) - diff |= encoded[i]; - return diff == 0; -} - -/* Public keys must be non-identity points in Ed25519's prime-order subgroup. + * Public keys must be non-identity points in Ed25519's prime-order subgroup. * Merely decoding a point is insufficient: an identity or torsion key can * make the verification equation true without knowledge of a private key. */ static int public_key_is_valid_subgroup(gf public_key[4]) diff --git a/include/eos_image.h b/include/eos_image.h index 62744d0..a4deec4 100644 --- a/include/eos_image.h +++ b/include/eos_image.h @@ -132,15 +132,21 @@ EOS_IMG_STATIC_ASSERT(offsetof(eos_image_header_t, flags) == 24, "flags must stay at offset 24"); EOS_IMG_STATIC_ASSERT(offsetof(eos_image_header_t, sig_len) == 61, "sig_len must stay at offset 61"); -EOS_IMG_STATIC_ASSERT(offsetof(eos_image_header_t, reserved) == 62, - "reserved[] must stay at offset 62"); +/* offsetof(tlv_len) == 62 and offsetof(tlv_hash) == 64 are already asserted + * above, next to EOS_IMG_TLV_HASH_LEN -- this used to be a single reserved[30] + * field before #93 split it into tlv_len + tlv_hash for anti-rollback, and + * this block was never updated to match, leaving `reserved` referring to a + * struct member that no longer exists (a hard compile error, not just a stale + * comment: eos_image_header_t has never had every field pinned since #93). + */ /* Field widths. An offset assert cannot see a field growing into padding that - * happens to keep every later offset -- reserved[] absorbs exactly that. */ + * happens to keep every later offset -- tlv_hash[] absorbs exactly that. */ EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->hash) == 32, "hash[] is 32 bytes on the wire"); -EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->reserved) == 30, - "reserved[] is 30 bytes on the wire"); +EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->tlv_hash) == + EOS_IMG_TLV_HASH_LEN, + "tlv_hash[] is EOS_IMG_TLV_HASH_LEN bytes on the wire"); EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->signature) == 64, "signature[] is 64 bytes on the wire"); diff --git a/tests/unit/test_ed25519.c b/tests/unit/test_ed25519.c index f78012d..698dd8e 100644 --- a/tests/unit/test_ed25519.c +++ b/tests/unit/test_ed25519.c @@ -89,6 +89,46 @@ static const struct rfc_vector k_vectors[] = { #define N_VECTORS (sizeof(k_vectors) / sizeof(k_vectors[0])) +/* All eight compressed encodings of edwards25519's order-8 torsion subgroup + * (the cyclic group for any order-8 point G, i.e. {1G, 2G, ..., 8G=O}). + * Order 8/gcd(k,8) for kG: orders present are 1 (identity), 2, 4, 4, 8, 8, 8, 8. + * + * Not hand-transcribed: derived by decoding an order-8 generator from this + * file's own unpackneg()/point_add()/point_is_identity(), then enumerating + * 1G..8G with point_add() and re-encoding with point_pack() -- so their + * correctness rests on the same curve arithmetic this file already uses for + * real verification, not on a separately-copied constant that could carry a + * transcription error. Cross-checked: point k and point (8-k) differ only in + * the sign bit (byte 31), as required since (8-k)G = -(kG); the order-2 + * element (k=4) is its own negation, as required since 2P = O implies P = -P. + */ +static const uint8_t k_low_order[8][32] = { + {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}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,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}, + {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}, + {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}, + {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}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {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}, + {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}, +}; + +/* A handful of distinct messages, so a low-order-R rejection isn't pinned + * against only one k = SHA-512(R || A || M) value. */ +static const char *messages[] = { + "", + "a", + "untrusted firmware", +}; + /* ---- positive tests: a conforming signature MUST be accepted ---- */ TEST(test_ed25519_rfc8032_vectors_accepted) @@ -260,19 +300,6 @@ TEST(test_ed25519_zero_signature_rejected) ASSERT(eos_ed25519_verify(sig, pk, msg, 1) != EOS_OK); } -TEST(test_ed25519_identity_key_forgery_rejected) -{ - /* The identity point has compressed encoding 01 00...00. With both the - * public key and R set to the identity and S set to zero, the verification - * equation is true for every message unless low-order keys are rejected. */ - uint8_t identity_pub[32] = {1}; - uint8_t identity_sig[64] = {1}; - const uint8_t msg[] = "untrusted firmware"; - - ASSERT(eos_ed25519_verify(identity_sig, identity_pub, - msg, sizeof(msg) - 1) != EOS_OK); -} - /* ---- SHA-512, the hash Ed25519 is defined over (FIPS 180-4) ---- */ TEST(test_ed25519_low_order_R_with_a_valid_key_is_not_a_forgery) @@ -369,11 +396,11 @@ int main(void) run_test_ed25519_low_order_keys_rejected(); run_test_ed25519_zero_pubkey_rejected(); run_test_ed25519_zero_signature_rejected(); - run_test_ed25519_identity_key_forgery_rejected(); + run_test_ed25519_low_order_R_with_a_valid_key_is_not_a_forgery(); run_test_sha512_known_answers(); run_test_sha512_streaming_matches_one_shot(); - tests_run = 11; + tests_run = 13; printf("\n%d/%d tests passed\n", tests_passed, tests_run); return (tests_passed == tests_run) ? 0 : 1; }