Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 12 additions & 44 deletions core/ed25519_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,16 @@ static int point_is_identity(gf p[4])
return diff == 0;
}

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);
}

/* Reject a public key outside the prime-order subgroup.
*
* Decoding a point is not enough. Ed25519 has eight points of low order, and
Expand All @@ -304,51 +314,9 @@ static int point_is_identity(gf p[4])
* 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.
* The key arrives negated from unpackneg(). [L](-A) = -[L]A and the identity
* is its own negation, so neither condition is affected by the sign.
*/
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.
* 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])
{
uint8_t order_l[32];
Expand Down
16 changes: 10 additions & 6 deletions include/eos_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ EOS_IMG_STATIC_ASSERT(offsetof(eos_image_header_t, tlv_hash) +

/* Every remaining field, pinned.
*
* Four of the fourteen fields were asserted. Transposing two adjacent
* Three of the thirteen field offsets were asserted (the fourth pre-existing
* assert is sizeof, which is not a field). Transposing two adjacent
* same-width fields moves neither sizeof nor any of those four offsets, so it
* compiled clean: with load_addr and entry_addr swapped, all four existing
* asserts still passed and the bootloader would load an image at its entry
Expand All @@ -132,15 +133,18 @@ 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");
/* tlv_len and tlv_hash are asserted above, where #93 introduced them; the
* 30 bytes they occupy are the ones this block used to pin as reserved[]. */

/* 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 -- the 30 bytes at 62 absorb exactly
* that, which is why both halves of that span carry a width assert. */
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_len) == 2,
"tlv_len is 2 bytes on the wire");
EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->tlv_hash) == 28,
"tlv_hash is 28 bytes on the wire");
EOS_IMG_STATIC_ASSERT(sizeof(((eos_image_header_t *)0)->signature) == 64,
"signature[] is 64 bytes on the wire");

Expand Down
41 changes: 35 additions & 6 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,36 @@
# --- test_bootctl: Boot control block ---
add_executable(eboot_test_bootctl unit/test_bootctl.c)
target_link_libraries(eboot_test_bootctl PRIVATE eboot_core)
# Every unit suite registered below appends its ctest name here, so the
# Valgrind block at the bottom derives its list instead of repeating it.
set(EBLDR_UNIT_TESTS "")

add_test(NAME test_bootctl COMMAND eboot_test_bootctl)
list(APPEND EBLDR_UNIT_TESTS test_bootctl)

# --- test_crypto: SHA-256 against known vectors ---
add_executable(eboot_test_crypto unit/test_crypto.c)
target_link_libraries(eboot_test_crypto PRIVATE eboot_core)
add_test(NAME test_crypto COMMAND eboot_test_crypto)
list(APPEND EBLDR_UNIT_TESTS test_crypto)

# --- test_image_verify: Image header parse bounds ---
add_executable(eboot_test_image_verify unit/test_image_verify.c)
target_link_libraries(eboot_test_image_verify PRIVATE eboot_core)
add_test(NAME test_image_verify COMMAND eboot_test_image_verify)
list(APPEND EBLDR_UNIT_TESTS test_image_verify)

# --- test_image_abi: pins the .efw image header wire format against eFirmware ---
add_executable(eboot_test_image_abi unit/test_image_abi.c)
target_link_libraries(eboot_test_image_abi PRIVATE eboot_core)
add_test(NAME test_image_abi COMMAND eboot_test_image_abi)
list(APPEND EBLDR_UNIT_TESTS test_image_abi)

# --- test_secure_boot: Secure boot policy gates ---
add_executable(eboot_test_secure_boot unit/test_secure_boot.c)
target_link_libraries(eboot_test_secure_boot PRIVATE eboot_core)
add_test(NAME test_secure_boot COMMAND eboot_test_secure_boot)
list(APPEND EBLDR_UNIT_TESTS test_secure_boot)

# --- test_recovery: UART recovery write range ---
# eboot_stage1 links eboot_core PUBLIC, so naming eboot_core here too put it on
Expand All @@ -34,92 +43,112 @@ add_test(NAME test_secure_boot COMMAND eboot_test_secure_boot)
add_executable(eboot_test_recovery unit/test_recovery.c)
target_link_libraries(eboot_test_recovery PRIVATE eboot_stage1)
add_test(NAME test_recovery COMMAND eboot_test_recovery)
list(APPEND EBLDR_UNIT_TESTS test_recovery)

# --- test_fw_transport: UART raw/XMODEM/YMODEM firmware transport ---
add_executable(eboot_test_fw_transport unit/test_fw_transport.c)
target_link_libraries(eboot_test_fw_transport PRIVATE eboot_core)
add_test(NAME test_fw_transport COMMAND eboot_test_fw_transport)
list(APPEND EBLDR_UNIT_TESTS test_fw_transport)

# --- test_slot_size_bounds: verify_slot() must reject image_size > slot capacity ---
add_executable(eboot_test_slot_size_bounds unit/test_slot_size_bounds.c)
target_link_libraries(eboot_test_slot_size_bounds PRIVATE eboot_core)
add_test(NAME test_slot_size_bounds COMMAND eboot_test_slot_size_bounds)
list(APPEND EBLDR_UNIT_TESTS test_slot_size_bounds)

# --- test_device_table: UEFI-style device table ---
add_executable(eboot_test_device_table unit/test_device_table.c)
target_link_libraries(eboot_test_device_table PRIVATE eboot_core)
add_test(NAME test_device_table COMMAND eboot_test_device_table)
list(APPEND EBLDR_UNIT_TESTS test_device_table)

# --- test_runtime_svc: Runtime variable store ---
add_executable(eboot_test_runtime_svc unit/test_runtime_svc.c)
target_link_libraries(eboot_test_runtime_svc PRIVATE eboot_core)
add_test(NAME test_runtime_svc COMMAND eboot_test_runtime_svc)
list(APPEND EBLDR_UNIT_TESTS test_runtime_svc)

# --- test_board_config: Declarative hardware config ---
add_executable(eboot_test_board_config unit/test_board_config.c)
target_link_libraries(eboot_test_board_config PRIVATE eboot_core)
add_test(NAME test_board_config COMMAND eboot_test_board_config)
list(APPEND EBLDR_UNIT_TESTS test_board_config)

# --- test_multicore: Multicore boot management ---
add_executable(eboot_test_multicore unit/test_multicore.c)
target_link_libraries(eboot_test_multicore PRIVATE eboot_core)
add_test(NAME test_multicore COMMAND eboot_test_multicore)
list(APPEND EBLDR_UNIT_TESTS test_multicore)

# --- test_board_registry: Runtime board selection ---
add_executable(eboot_test_board_registry unit/test_board_registry.c)
target_link_libraries(eboot_test_board_registry PRIVATE eboot_core)
add_test(NAME test_board_registry COMMAND eboot_test_board_registry)
list(APPEND EBLDR_UNIT_TESTS test_board_registry)

# --- test_slot_manager: Firmware slot management ---
add_executable(eboot_test_slot_manager unit/test_slot_manager.c)
target_link_libraries(eboot_test_slot_manager PRIVATE eboot_core)
add_test(NAME test_slot_manager COMMAND eboot_test_slot_manager)
list(APPEND EBLDR_UNIT_TESTS test_slot_manager)

# --- test_boot_log: Boot log subsystem ---
add_executable(eboot_test_boot_log unit/test_boot_log.c)
target_link_libraries(eboot_test_boot_log PRIVATE eboot_core)
add_test(NAME test_boot_log COMMAND eboot_test_boot_log)
list(APPEND EBLDR_UNIT_TESTS test_boot_log)

# --- test_ed25519: Ed25519 signature verification ---
add_executable(eboot_test_ed25519 unit/test_ed25519.c)
target_link_libraries(eboot_test_ed25519 PRIVATE eboot_core)
add_test(NAME test_ed25519 COMMAND eboot_test_ed25519)
list(APPEND EBLDR_UNIT_TESTS test_ed25519)

# --- test_keystore: Key management ---
add_executable(eboot_test_keystore unit/test_keystore.c)
target_link_libraries(eboot_test_keystore PRIVATE eboot_core)
add_test(NAME test_keystore COMMAND eboot_test_keystore)
list(APPEND EBLDR_UNIT_TESTS test_keystore)

# --- test_rollback: Anti-rollback security counter ---
add_executable(eboot_test_rollback unit/test_rollback.c)
target_link_libraries(eboot_test_rollback PRIVATE eboot_core)
add_test(NAME test_rollback COMMAND eboot_test_rollback)
list(APPEND EBLDR_UNIT_TESTS test_rollback)

# --- test_tlv_auth: TLV area must be bound to the signed header ---
add_executable(eboot_test_tlv_auth unit/test_tlv_auth.c)
target_link_libraries(eboot_test_tlv_auth PRIVATE eboot_core)
add_test(NAME test_tlv_auth COMMAND eboot_test_tlv_auth)
list(APPEND EBLDR_UNIT_TESTS test_tlv_auth)

# --- test_storage: Unified storage abstraction ---
add_executable(eboot_test_storage unit/test_storage.c)
target_link_libraries(eboot_test_storage PRIVATE eboot_core)
add_test(NAME test_storage COMMAND eboot_test_storage)
list(APPEND EBLDR_UNIT_TESTS test_storage)

# --- test_ecc: ECC memory range validation ---
add_executable(eboot_test_ecc unit/test_ecc.c)
target_link_libraries(eboot_test_ecc PRIVATE eboot_core)
add_test(NAME test_ecc COMMAND eboot_test_ecc)
list(APPEND EBLDR_UNIT_TESTS test_ecc)

# --- Valgrind test targets ---
#
# The list is derived from the suites registered above, not written out again.
# Hand-maintained, it drifted: it had 17 of the 21 registered tests, missing
# test_ecc, test_rollback, test_secure_boot and test_storage -- and nothing
# failed when a name was forgotten, because a missing entry is simply a test
# that never gets a memory-safety run.
#
# EBLDR_UNIT_TESTS is appended by each add_test() above, so a suite added
# without touching this block still gets a Valgrind target.
find_program(VALGRIND valgrind)
if(VALGRIND)
set(VALGRIND_OPTS --leak-check=full --error-exitcode=1 --quiet)
foreach(TEST_NAME test_bootctl test_crypto test_ed25519 test_keystore
test_device_table test_runtime_svc test_board_config
test_multicore test_board_registry test_slot_manager
test_boot_log test_image_verify test_image_abi
test_recovery test_slot_size_bounds test_fw_transport
test_tlv_auth)
foreach(TEST_NAME ${EBLDR_UNIT_TESTS})
add_test(
NAME valgrind_${TEST_NAME}
COMMAND ${VALGRIND} ${VALGRIND_OPTS} $<TARGET_FILE:eboot_${TEST_NAME}>
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_board_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static int tests_passed = 0;
static void name(void); \
static void run_##name(void) { \
printf(" %-50s ", #name); \
tests_run++; \
name(); \
tests_passed++; \
printf("[PASS]\n"); \
Expand Down Expand Up @@ -188,7 +189,6 @@ int main(void)
run_test_total_ram();
run_test_total_flash();

tests_run = 9;
printf("\n%d/%d tests passed\n", tests_passed, tests_run);
return (tests_passed == tests_run) ? 0 : 1;
}
2 changes: 1 addition & 1 deletion tests/unit/test_board_registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static int tests_passed = 0;
static void name(void); \
static void run_##name(void) { \
printf(" %-50s ", #name); \
tests_run++; \
name(); \
tests_passed++; \
printf("[PASS]\n"); \
Expand Down Expand Up @@ -134,7 +135,6 @@ int main(void)
run_test_get_by_index();
run_test_register_null();

tests_run = 8;
printf("\n%d/%d tests passed\n", tests_passed, tests_run);
return (tests_passed == tests_run) ? 0 : 1;
}
2 changes: 1 addition & 1 deletion tests/unit/test_boot_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,6 @@
RUN(test_clear_reports_erase_failure);
RUN(test_append_does_not_advance_head_when_write_fails);
RUN(test_entry_layout_is_stable);
printf("\n%d/11 tests passed\n", tests_passed);
printf("\n%d/%d tests passed\n", tests_passed);
return 0;
}
2 changes: 1 addition & 1 deletion tests/unit/test_bootctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static int tests_passed = 0;
static void run_##name(void) { \
setup(); \
printf(" %-50s ", #name); \
tests_run++; \
name(); \
tests_passed++; \
printf("[PASS]\n"); \
Expand Down Expand Up @@ -292,7 +293,6 @@ int main(void)
run_test_version_encoding();
run_test_validate_null();

tests_run = 12;
printf("\n%d/%d tests passed\n", tests_passed, tests_run);
return (tests_passed == tests_run) ? 0 : 1;
}
2 changes: 1 addition & 1 deletion tests/unit/test_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static int tests_passed = 0;
static void name(void); \
static void run_##name(void) { \
printf(" %-50s ", #name); \
tests_run++; \
name(); \
tests_passed++; \
printf("[PASS]\n"); \
Expand Down Expand Up @@ -118,7 +119,6 @@ int main(void)
run_test_sha256_incremental();
run_test_crypto_null_args();

tests_run = 5;
printf("\n%d/%d tests passed\n", tests_passed, tests_run);
return (tests_passed == tests_run) ? 0 : 1;
}
2 changes: 1 addition & 1 deletion tests/unit/test_device_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static int tests_passed = 0;
static void name(void); \
static void run_##name(void) { \
printf(" %-50s ", #name); \
tests_run++; \
name(); \
tests_passed++; \
printf("[PASS]\n"); \
Expand Down Expand Up @@ -145,7 +146,6 @@ int main(void)
run_test_corrupt_crc_fails();
run_test_oversized_counts_fail();

tests_run = 8;
printf("\n%d/%d tests passed\n", tests_passed, tests_run);
return (tests_passed == tests_run) ? 0 : 1;
}
Loading
Loading