Skip to content
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
- **`image_verify.c`:** The CRC32 integrity path now fails closed on a flash read error. `eos_crc32()` returned `0` when `eos_hal_flash_read()` failed, which is indistinguishable from a region that genuinely hashes to `0`, so an image whose payload could not be read passed `eos_image_verify_integrity()` when the stored CRC was `0`. The stored CRC lives in the unauthenticated header, so setting it to `0` is trivial. The SHA-256 path already propagated the read error; the two now behave the same.
- **`image_verify.c`:** `eos_image_verify_integrity` rejects a zero `image_size`, and an `addr + hdr_size` that wraps `uint32_t`, instead of computing a payload address that is not the payload.

### Fixed
- **The tree did not compile.** `include/eos_image.h` declared `eos_crc32()` as `int eos_crc32(uint32_t, size_t, uint32_t *)` while `core/image_verify.c` defined it as `uint32_t eos_crc32(uint32_t, size_t)` -- a conflicting-types error that stopped the build at the first core source file. The declaration now matches the definition and the documented behaviour.
- **`ed25519_verify.c`:** `eos_ed25519_verify()` never performed the verification. Two merged copies of the challenge-hash step had been left in the function, the second referring to identifiers that do not exist (`sha512_ctx_t`, `sc_reduce`), and RFC 8032 step 4 -- the `[S]B == R + [k]A` check -- was absent entirely, leaving the function returning an undeclared `diff`. The duplicate is removed and the group-equation check restored; the function now passes the RFC 8032 test vectors and rejects tampered messages, every single-bit signature flip, wrong keys and malleated signatures.
- **`recovery.c`:** `recovery_handle_write()` declared `slot_size` twice, which does not compile. The bounds check now calls `eos_recovery_write_in_range()` -- the helper the unit tests already exercise -- so the wire-input rule has one definition, and an unmapped slot (`base == 0`) is rejected too.
- **SHA-512 had two incompatible declarations.** `include/eos_sha512.h` declared `sha512_init/update/final` over a `sha512_ctx_t`, while `include/eos_crypto_boot.h` declared `eos_sha512_init/update/final` plus a one-shot `eos_sha512()` over an `eos_sha512_ctx_t`. `core/sha512.c` implemented the first set; `core/ed25519_verify.c` and the unit tests called the second, which nothing defined. `core/sha512.c` now implements the `eos_`-prefixed API (including the missing one-shot), `eos_sha512_ctx_t` carries the 128-bit length counter FIPS 180-4 requires, and the duplicate `include/eos_sha512.h` is removed.
- **`CMakeLists.txt`:** `core/sha512.c` and `core/rollback.c` were never compiled, so `eboot_core` could not resolve `eos_sha512_*` or `eos_rollback_*` and eleven unit-test executables failed to link. `core/boot_log.c` was listed twice. Both fixed.
- **`CMakeLists.txt`:** the `EBLDR_BOARD` dispatch chain was duplicated from `cortex_m3` onwards, and a stray `message(FATAL_ERROR ...)` sat inside the `kalimba` branch, so `cmake -DEBLDR_BOARD=kalimba` aborted configuration for a supported board and 110 later branches were unreachable. The duplicate is removed. (This is the regression `tests/unit/test_cmake_board_dispatch.py` was written to catch; it had returned.)
- **`tests/unit/test_slot_manager.c`:** the file was two different test files spliced together mid-function -- stub definitions cut in half, `main()` calling twenty functions that do not exist. Rebuilt as one suite that exercises the real `core/slot_manager.c` through scriptable per-slot verification mocks.
- **`tests/unit/test_recovery.c`:** local stand-ins for the boot-log API conflicted with `include/eos_boot_log.h` and duplicated symbols now linked from `core/boot_log.c`. Removed.
- **`tests/CMakeLists.txt`:** `unit/test_fw_transport.c` existed but was never built or run. It is now registered.

### Added
- **`eos_crc32_checked()`** — CRC32 over a flash region that reports read failures through its return value. `eos_crc32()` is retained for API compatibility and documented as unsuitable for verification decisions.

Expand Down
114 changes: 2 additions & 112 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ add_library(eboot_core STATIC
core/image_verify.c
core/slot_manager.c
core/boot_policy.c
core/boot_log.c
core/recovery.c
core/fw_services.c
core/fw_update.c
Expand All @@ -111,6 +110,8 @@ add_library(eboot_core STATIC
core/debug_lock.c
core/fw_decrypt.c
core/image_tlv.c
core/sha512.c
core/rollback.c
)
target_include_directories(eboot_core PUBLIC ${EBLDR_INCLUDE_DIR})
target_link_libraries(eboot_core PUBLIC eboot_hal)
Expand Down Expand Up @@ -207,118 +208,7 @@ if(NOT EBLDR_BOARD STREQUAL "none")
eboot_add_board(cortex_m0 boards/cortex_m0/board_cortex_m0.c boards/cortex_m0)
elseif(EBLDR_BOARD STREQUAL "cortex_m0plus")
eboot_add_board(cortex_m0plus boards/cortex_m0plus/board_cortex_m0plus.c boards/cortex_m0plus)
elseif(EBLDR_BOARD STREQUAL "cortex_m3")
eboot_add_board(cortex_m3 boards/cortex_m3/board_cortex_m3.c boards/cortex_m3)
elseif(EBLDR_BOARD STREQUAL "cortex_m23")
eboot_add_board(cortex_m23 boards/cortex_m23/board_cortex_m23.c boards/cortex_m23)
elseif(EBLDR_BOARD STREQUAL "cortex_m33")
eboot_add_board(cortex_m33 boards/cortex_m33/board_cortex_m33.c boards/cortex_m33)
elseif(EBLDR_BOARD STREQUAL "cortex_m55")
eboot_add_board(cortex_m55 boards/cortex_m55/board_cortex_m55.c boards/cortex_m55)
elseif(EBLDR_BOARD STREQUAL "cortex_m85")
eboot_add_board(cortex_m85 boards/cortex_m85/board_cortex_m85.c boards/cortex_m85)
elseif(EBLDR_BOARD STREQUAL "cortex_r4")
eboot_add_board(cortex_r4 boards/cortex_r4/board_cortex_r4.c boards/cortex_r4)
elseif(EBLDR_BOARD STREQUAL "cortex_r52")
eboot_add_board(cortex_r52 boards/cortex_r52/board_cortex_r52.c boards/cortex_r52)
elseif(EBLDR_BOARD STREQUAL "cortex_a5")
eboot_add_board(cortex_a5 boards/cortex_a5/board_cortex_a5.c boards/cortex_a5)
elseif(EBLDR_BOARD STREQUAL "cortex_a9")
eboot_add_board(cortex_a9 boards/cortex_a9/board_cortex_a9.c boards/cortex_a9)
elseif(EBLDR_BOARD STREQUAL "cortex_a15")
eboot_add_board(cortex_a15 boards/cortex_a15/board_cortex_a15.c boards/cortex_a15)
elseif(EBLDR_BOARD STREQUAL "cortex_a35")
eboot_add_board(cortex_a35 boards/cortex_a35/board_cortex_a35.c boards/cortex_a35)
elseif(EBLDR_BOARD STREQUAL "cortex_a55")
eboot_add_board(cortex_a55 boards/cortex_a55/board_cortex_a55.c boards/cortex_a55)
elseif(EBLDR_BOARD STREQUAL "cortex_a76")
eboot_add_board(cortex_a76 boards/cortex_a76/board_cortex_a76.c boards/cortex_a76)
elseif(EBLDR_BOARD STREQUAL "arm7tdmi")
eboot_add_board(arm7tdmi boards/arm7tdmi/board_arm7tdmi.c boards/arm7tdmi)
elseif(EBLDR_BOARD STREQUAL "arm9")
eboot_add_board(arm9 boards/arm9/board_arm9.c boards/arm9)
elseif(EBLDR_BOARD STREQUAL "arm11")
eboot_add_board(arm11 boards/arm11/board_arm11.c boards/arm11)
elseif(EBLDR_BOARD STREQUAL "avr")
eboot_add_board(avr boards/avr/board_avr.c boards/avr)
elseif(EBLDR_BOARD STREQUAL "avr32")
eboot_add_board(avr32 boards/avr32/board_avr32.c boards/avr32)
elseif(EBLDR_BOARD STREQUAL "pic16")
eboot_add_board(pic16 boards/pic16/board_pic16.c boards/pic16)
elseif(EBLDR_BOARD STREQUAL "pic18")
eboot_add_board(pic18 boards/pic18/board_pic18.c boards/pic18)
elseif(EBLDR_BOARD STREQUAL "pic24")
eboot_add_board(pic24 boards/pic24/board_pic24.c boards/pic24)
elseif(EBLDR_BOARD STREQUAL "dspic")
eboot_add_board(dspic boards/dspic/board_dspic.c boards/dspic)
elseif(EBLDR_BOARD STREQUAL "pic32")
eboot_add_board(pic32 boards/pic32/board_pic32.c boards/pic32)
elseif(EBLDR_BOARD STREQUAL "msp430")
eboot_add_board(msp430 boards/msp430/board_msp430.c boards/msp430)
elseif(EBLDR_BOARD STREQUAL "c28x")
eboot_add_board(c28x boards/c28x/board_c28x.c boards/c28x)
elseif(EBLDR_BOARD STREQUAL "c6000")
eboot_add_board(c6000 boards/c6000/board_c6000.c boards/c6000)
elseif(EBLDR_BOARD STREQUAL "pru")
eboot_add_board(pru boards/pru/board_pru.c boards/pru)
elseif(EBLDR_BOARD STREQUAL "rl78")
eboot_add_board(rl78 boards/rl78/board_rl78.c boards/rl78)
elseif(EBLDR_BOARD STREQUAL "rx")
eboot_add_board(rx boards/rx/board_rx.c boards/rx)
elseif(EBLDR_BOARD STREQUAL "tricore")
eboot_add_board(tricore boards/tricore/board_tricore.c boards/tricore)
elseif(EBLDR_BOARD STREQUAL "c166")
eboot_add_board(c166 boards/c166/board_c166.c boards/c166)
elseif(EBLDR_BOARD STREQUAL "microblaze")
eboot_add_board(microblaze boards/microblaze/board_microblaze.c boards/microblaze)
elseif(EBLDR_BOARD STREQUAL "nios2")
eboot_add_board(nios2 boards/nios2/board_nios2.c boards/nios2)
elseif(EBLDR_BOARD STREQUAL "openrisc")
eboot_add_board(openrisc boards/openrisc/board_openrisc.c boards/openrisc)
elseif(EBLDR_BOARD STREQUAL "lm32")
eboot_add_board(lm32 boards/lm32/board_lm32.c boards/lm32)
elseif(EBLDR_BOARD STREQUAL "blackfin")
eboot_add_board(blackfin boards/blackfin/board_blackfin.c boards/blackfin)
elseif(EBLDR_BOARD STREQUAL "sharc")
eboot_add_board(sharc boards/sharc/board_sharc.c boards/sharc)
elseif(EBLDR_BOARD STREQUAL "hexagon")
eboot_add_board(hexagon boards/hexagon/board_hexagon.c boards/hexagon)
elseif(EBLDR_BOARD STREQUAL "ceva")
eboot_add_board(ceva boards/ceva/board_ceva.c boards/ceva)
elseif(EBLDR_BOARD STREQUAL "xtensa_hifi")
eboot_add_board(xtensa_hifi boards/xtensa_hifi/board_xtensa_hifi.c boards/xtensa_hifi)
elseif(EBLDR_BOARD STREQUAL "arc")
eboot_add_board(arc boards/arc/board_arc.c boards/arc)
elseif(EBLDR_BOARD STREQUAL "8051")
eboot_add_board(8051 boards/8051/board_8051.c boards/8051)
elseif(EBLDR_BOARD STREQUAL "riscv32")
eboot_add_board(riscv32 boards/riscv32/board_riscv32.c boards/riscv32)
elseif(EBLDR_BOARD STREQUAL "esp32s3")
eboot_add_board(esp32s3 boards/esp32s3/board_esp32s3.c boards/esp32s3)
elseif(EBLDR_BOARD STREQUAL "esp32c3")
eboot_add_board(esp32c3 boards/esp32c3/board_esp32c3.c boards/esp32c3)
elseif(EBLDR_BOARD STREQUAL "mips64")
eboot_add_board(mips64 boards/mips64/board_mips64.c boards/mips64)
elseif(EBLDR_BOARD STREQUAL "sparc64")
eboot_add_board(sparc64 boards/sparc64/board_sparc64.c boards/sparc64)
elseif(EBLDR_BOARD STREQUAL "ppc64")
eboot_add_board(ppc64 boards/ppc64/board_ppc64.c boards/ppc64)
elseif(EBLDR_BOARD STREQUAL "loongarch")
eboot_add_board(loongarch boards/loongarch/board_loongarch.c boards/loongarch)
elseif(EBLDR_BOARD STREQUAL "parisc")
eboot_add_board(parisc boards/parisc/board_parisc.c boards/parisc)
elseif(EBLDR_BOARD STREQUAL "ia64")
eboot_add_board(ia64 boards/ia64/board_ia64.c boards/ia64)
elseif(EBLDR_BOARD STREQUAL "alpha")
eboot_add_board(alpha boards/alpha/board_alpha.c boards/alpha)
elseif(EBLDR_BOARD STREQUAL "s390")
eboot_add_board(s390 boards/s390/board_s390.c boards/s390)
elseif(EBLDR_BOARD STREQUAL "cris")
eboot_add_board(cris boards/cris/board_cris.c boards/cris)
elseif(EBLDR_BOARD STREQUAL "kalimba")
eboot_add_board(kalimba boards/kalimba/board_kalimba.c boards/kalimba)
# --- New ARM Cortex-M boards ---
message(FATAL_ERROR "Unknown board: ${EBLDR_BOARD}. See boards/ directory for available ports.")
elseif(EBLDR_BOARD STREQUAL "cortex_m3")
eboot_add_board(cortex_m3 boards/cortex_m3/board_cortex_m3.c boards/cortex_m3)
elseif(EBLDR_BOARD STREQUAL "cortex_m23")
Expand Down
31 changes: 17 additions & 14 deletions core/ed25519_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include "eos_crypto_boot.h"
#include "eos_types.h"
#include <string.h>
#include "eos_sha512.h"

/* ================================================================
* Field arithmetic mod p = 2^255 - 19
Expand Down Expand Up @@ -428,19 +427,23 @@ int eos_ed25519_verify(const uint8_t signature[64],
eos_sha512_final(&ctx, k);
reduce_hash(k);

/* Step 3: Compute k = SHA-256(R || A || M) reduced mod L */
/* Step 3: Compute k = SHA-512(R || A || M) reduced mod L */
uint8_t k_hash[64];
sha512_ctx_t ctx;

sha512_init(&ctx);
sha512_update(&ctx, signature, 32); /* R */
sha512_update(&ctx, public_key, 32); /* A */
sha512_update(&ctx, message, msg_len); /* M */
sha512_final(&ctx, k_hash);

uint8_t k[32];
sc_reduce(k, k_hash);
/* Accept iff [S]B == R + [k]A, checked as [S]B - [k]A == R.
*
* A already holds -A (unpackneg() negates on decode), so [k]A' is the
* subtraction. scalarmult() consumes its point argument in place, which is
* why q is reloaded with the base point only after [k]A' has been formed. */
gf p[4], q[4];
scalarmult(p, A, k); /* p = [k](-A) */
scalarbase(q, &signature[32]); /* q = [S]B */
point_add(p, (const gf *)q); /* p = [S]B - [k]A */

uint8_t check[32];
point_pack(check, p);

/* Compare against R without an early exit, so a rejected signature costs
* the same time whatever byte it first differs at. */
uint8_t diff = 0;
for (int i = 0; i < 32; i++) diff |= (uint8_t)(check[i] ^ signature[i]);

return diff == 0 ? EOS_OK : EOS_ERR_SIGNATURE;
}
8 changes: 5 additions & 3 deletions core/recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,11 @@ static int recovery_handle_write(eos_slot_t slot, uint32_t offset, uint16_t len)

/* offset/len come straight from the wire; without this check a
* recovery client can write past the slot boundary into the other
* slot, boot-control blocks, or the boot log. */
uint32_t slot_size = eos_hal_slot_size(slot);
if (slot_size == 0 || (uint64_t)offset + len > (uint64_t)slot_size)
* slot, boot-control blocks, or the boot log. eos_recovery_write_in_range()
* is the single definition of that rule: it is what the unit tests
* exercise, and it also rejects an unmapped slot (base == 0) and a
* base + offset that wraps the address space. */
if (eos_recovery_write_in_range(base, slot_size, offset, len) != EOS_OK)
return recovery_send_nack();

recovery_send_ack();
Expand Down
26 changes: 18 additions & 8 deletions core/sha512.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "eos_sha512.h"
#include "eos_crypto_boot.h"
#include <string.h>

#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
Expand Down Expand Up @@ -119,7 +119,7 @@ static void store_be64(uint8_t *p, uint64_t x)
p[7] = (uint8_t)x;
}

static void sha512_transform(sha512_ctx_t *ctx,
static void sha512_transform(eos_sha512_ctx_t *ctx,
const uint8_t block[128])
{
uint64_t w[80];
Expand Down Expand Up @@ -164,7 +164,7 @@ static void sha512_transform(sha512_ctx_t *ctx,
ctx->state[7] += h;
}

void sha512_init(sha512_ctx_t *ctx)
void eos_sha512_init(eos_sha512_ctx_t *ctx)
{
ctx->state[0] = 0x6a09e667f3bcc908ULL;
ctx->state[1] = 0xbb67ae8584caa73bULL;
Expand All @@ -180,9 +180,9 @@ void sha512_init(sha512_ctx_t *ctx)
ctx->buffer_len = 0;
}

void sha512_update(sha512_ctx_t *ctx,
const uint8_t *data,
size_t len)
void eos_sha512_update(eos_sha512_ctx_t *ctx,
const uint8_t *data,
size_t len)
{
while (len > 0) {
size_t copy = 128 - ctx->buffer_len;
Expand Down Expand Up @@ -213,8 +213,8 @@ void sha512_update(sha512_ctx_t *ctx,
}
}

void sha512_final(sha512_ctx_t *ctx,
uint8_t digest[64])
void eos_sha512_final(eos_sha512_ctx_t *ctx,
uint8_t digest[EOS_SHA512_DIGEST_SIZE])
{
size_t i = ctx->buffer_len;

Expand All @@ -241,3 +241,13 @@ void sha512_final(sha512_ctx_t *ctx,

memset(ctx, 0, sizeof(*ctx));
}

void eos_sha512(const uint8_t *data, size_t len,
uint8_t digest[EOS_SHA512_DIGEST_SIZE])
{
eos_sha512_ctx_t ctx;

eos_sha512_init(&ctx);
eos_sha512_update(&ctx, data, len);
eos_sha512_final(&ctx, digest);
}
5 changes: 4 additions & 1 deletion include/eos_crypto_boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ void eos_sha256_final(eos_sha256_ctx_t *ctx, uint8_t digest[EOS_SHA256_DIGEST_SI

typedef struct {
uint64_t state[8];
uint64_t count;
/* FIPS 180-4 gives SHA-512 a 128-bit length field, so the bit count is
* carried in two words rather than one. */
uint64_t bitlen[2];
uint8_t buffer[EOS_SHA512_BLOCK_SIZE];
size_t buffer_len;
} eos_sha512_ctx_t;

/**
Expand Down
2 changes: 1 addition & 1 deletion include/eos_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ int eos_crc32_checked(uint32_t addr, size_t len, uint32_t *out_crc);
* @param len Length in bytes.
* @return CRC32 value, or 0 if the region could not be read.
*/
int eos_crc32(uint32_t addr, size_t len, uint32_t *out);
uint32_t eos_crc32(uint32_t addr, size_t len);

#ifdef __cplusplus
}
Expand Down
23 changes: 0 additions & 23 deletions include/eos_sha512.h

This file was deleted.

5 changes: 5 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ add_executable(test_storage unit/test_storage.c)
target_link_libraries(test_storage PRIVATE eboot_core)
add_test(NAME test_storage COMMAND test_storage)

# --- test_fw_transport: XMODEM/YMODEM firmware transport ---
add_executable(test_fw_transport unit/test_fw_transport.c)
target_link_libraries(test_fw_transport PRIVATE eboot_core)
add_test(NAME test_fw_transport COMMAND test_fw_transport)

# --- Valgrind test targets ---
find_program(VALGRIND valgrind)
if(VALGRIND)
Expand Down
5 changes: 0 additions & 5 deletions tests/unit/test_recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
#include <stdint.h>
#include "eos_boot_log.h"

/* Mock implementations for unresolved symbols */
void eos_boot_log_append(uint32_t event, uint32_t slot, uint32_t detail) { (void)event; (void)slot; (void)detail; }
int eos_boot_log_read(uint32_t index, eos_boot_log_entry_t *out) { (void)index; (void)out; return EOS_OK; }
uint32_t eos_boot_log_get_head(void) { return 0; }

/* ---- Simulated Flash ---- */

#define SIM_FLASH_SIZE (128 * 1024)
Expand Down
Loading