diff --git a/CMakeLists.txt b/CMakeLists.txt index a6d5f5d..308acb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,6 @@ target_include_directories(eboot_hal PUBLIC ${EBLDR_INCLUDE_DIR}) # ---- Core boot logic ---- add_library(eboot_core STATIC core/bootctl.c - core/boot_log.c core/image_verify.c core/slot_manager.c core/boot_policy.c @@ -107,7 +106,9 @@ add_library(eboot_core STATIC core/bmc_handoff.c core/os_adapter.c core/ed25519_verify.c + core/sha512.c core/keystore.c + core/rollback.c core/debug_lock.c core/fw_decrypt.c core/image_tlv.c diff --git a/core/ed25519_verify.c b/core/ed25519_verify.c index 90ce54f..d301ba5 100644 --- a/core/ed25519_verify.c +++ b/core/ed25519_verify.c @@ -29,7 +29,7 @@ #include "eos_crypto_boot.h" #include "eos_types.h" #include -#include "eos_sha512.h" + /* ================================================================ * Field arithmetic mod p = 2^255 - 19 @@ -428,19 +428,21 @@ 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; + /* Compute [k](-A) + [S]B, which equals R for a valid signature. */ + gf kA[4], sB[4]; + scalarmult(kA, A, k); + scalarbase(sB, &signature[32]); + point_add(kA, (const gf *)sB); + + uint8_t recovered[32]; + point_pack(recovered, kA); + + uint8_t diff = 0; + for (int i = 0; i < 32; i++) diff |= (uint8_t)(recovered[i] ^ signature[i]); -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); + /* Wipe the challenge scalar rather than leave it in boot-path memory. */ + memset(k, 0, sizeof(k)); -uint8_t k[32]; -sc_reduce(k, k_hash); return diff == 0 ? EOS_OK : EOS_ERR_SIGNATURE; } diff --git a/core/recovery.c b/core/recovery.c index 18a88ba..26b9415 100644 --- a/core/recovery.c +++ b/core/recovery.c @@ -284,7 +284,6 @@ 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) return recovery_send_nack(); diff --git a/core/sha512.c b/core/sha512.c index 999baf2..4a1ecb5 100644 --- a/core/sha512.c +++ b/core/sha512.c @@ -1,243 +1,155 @@ -#include "eos_sha512.h" +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 EoS Project +// ISO/IEC 25000 | ISO/IEC/IEEE 15288:2023 + +/** + * @file sha512.c + * @brief SHA-512 (NIST FIPS 180-4) — required by Ed25519 (RFC 8032) + * + * Ed25519 as specified in RFC 8032 derives its challenge scalar from + * SHA-512. A verifier using any other hash cannot check a signature made + * by a conforming signer, so this primitive is not optional for + * interoperability with standard tooling. + * + * Self-contained, no dynamic allocation, suitable for a bootloader. + */ + +#include "eos_crypto_boot.h" #include -#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) - -#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) - -#define BSIG0(x) (ROTR64((x), 28) ^ ROTR64((x), 34) ^ ROTR64((x), 39)) -#define BSIG1(x) (ROTR64((x), 14) ^ ROTR64((x), 18) ^ ROTR64((x), 41)) - -#define SSIG0(x) (ROTR64((x), 1) ^ ROTR64((x), 8) ^ ((x) >> 7)) -#define SSIG1(x) (ROTR64((x), 19) ^ ROTR64((x), 61) ^ ((x) >> 6)) - -static const uint64_t K[80] = { - 0x428a2f98d728ae22ULL, - 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, - 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, - 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, - 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, - 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, - 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, - 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, - 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, - 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, - 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, - 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, - 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, - 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, - 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, - 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, - 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, - 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, - 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, - 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, - 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, - 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, - 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, - 0xd69906245565a910ULL, - 0xf40e35855771202aULL, - 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, - 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, - 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, - 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, - 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, - 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, - 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, - 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, - 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, - 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, - 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, - 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, - 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, - 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, - 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, - 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, - 0x6c44198c4a475817ULL +static const uint64_t K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; -static uint64_t load_be64(const uint8_t *p) -{ - return ((uint64_t)p[0] << 56) | - ((uint64_t)p[1] << 48) | - ((uint64_t)p[2] << 40) | - ((uint64_t)p[3] << 32) | - ((uint64_t)p[4] << 24) | - ((uint64_t)p[5] << 16) | - ((uint64_t)p[6] << 8) | - ((uint64_t)p[7]); -} - -static void store_be64(uint8_t *p, uint64_t x) -{ - p[0] = (uint8_t)(x >> 56); - p[1] = (uint8_t)(x >> 48); - p[2] = (uint8_t)(x >> 40); - p[3] = (uint8_t)(x >> 32); - p[4] = (uint8_t)(x >> 24); - p[5] = (uint8_t)(x >> 16); - p[6] = (uint8_t)(x >> 8); - p[7] = (uint8_t)x; -} - -static void sha512_transform(sha512_ctx_t *ctx, - const uint8_t block[128]) +#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) +#define CH64(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ64(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0_64(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define EP1_64(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SIG0_64(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ ((x) >> 7)) +#define SIG1_64(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ ((x) >> 6)) + +static void sha512_transform(eos_sha512_ctx_t *ctx) { uint64_t w[80]; + uint64_t a, b, c, d, e, f, g, h, t1, t2; + + for (int i = 0; i < 16; i++) { + w[i] = ((uint64_t)ctx->buffer[i * 8 + 0] << 56) | + ((uint64_t)ctx->buffer[i * 8 + 1] << 48) | + ((uint64_t)ctx->buffer[i * 8 + 2] << 40) | + ((uint64_t)ctx->buffer[i * 8 + 3] << 32) | + ((uint64_t)ctx->buffer[i * 8 + 4] << 24) | + ((uint64_t)ctx->buffer[i * 8 + 5] << 16) | + ((uint64_t)ctx->buffer[i * 8 + 6] << 8) | + ((uint64_t)ctx->buffer[i * 8 + 7]); + } + for (int i = 16; i < 80; i++) { + w[i] = SIG1_64(w[i - 2]) + w[i - 7] + SIG0_64(w[i - 15]) + w[i - 16]; + } - for (int i = 0; i < 16; i++) - w[i] = load_be64(block + i * 8); - - for (int i = 16; i < 80; i++) - w[i] = SSIG1(w[i - 2]) + w[i - 7] + - SSIG0(w[i - 15]) + w[i - 16]; - - uint64_t a = ctx->state[0]; - uint64_t b = ctx->state[1]; - uint64_t c = ctx->state[2]; - uint64_t d = ctx->state[3]; - uint64_t e = ctx->state[4]; - uint64_t f = ctx->state[5]; - uint64_t g = ctx->state[6]; - uint64_t h = ctx->state[7]; + a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; + e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; for (int i = 0; i < 80; i++) { - uint64_t t1 = h + BSIG1(e) + CH(e, f, g) + K[i] + w[i]; - uint64_t t2 = BSIG0(a) + MAJ(a, b, c); - - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; + t1 = h + EP1_64(e) + CH64(e, f, g) + K512[i] + w[i]; + t2 = EP0_64(a) + MAJ64(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; } - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; + ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; + ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; 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; - ctx->state[2] = 0x3c6ef372fe94f82bULL; - ctx->state[3] = 0xa54ff53a5f1d36f1ULL; - ctx->state[4] = 0x510e527fade682d1ULL; - ctx->state[5] = 0x9b05688c2b3e6c1fULL; - ctx->state[6] = 0x1f83d9abfb41bd6bULL; - ctx->state[7] = 0x5be0cd19137e2179ULL; - - ctx->bitlen[0] = 0; - ctx->bitlen[1] = 0; - ctx->buffer_len = 0; + ctx->state[0] = 0x6a09e667f3bcc908ULL; ctx->state[1] = 0xbb67ae8584caa73bULL; + ctx->state[2] = 0x3c6ef372fe94f82bULL; ctx->state[3] = 0xa54ff53a5f1d36f1ULL; + ctx->state[4] = 0x510e527fade682d1ULL; ctx->state[5] = 0x9b05688c2b3e6c1fULL; + ctx->state[6] = 0x1f83d9abfb41bd6bULL; ctx->state[7] = 0x5be0cd19137e2179ULL; + ctx->count = 0; + memset(ctx->buffer, 0, sizeof(ctx->buffer)); } -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; - - if (copy > len) - copy = len; - - memcpy(ctx->buffer + ctx->buffer_len, data, copy); + size_t idx = (size_t)(ctx->count % EOS_SHA512_BLOCK_SIZE); - ctx->buffer_len += copy; - data += copy; - len -= copy; + ctx->count += len; - uint64_t bits = (uint64_t)copy << 3; - - uint64_t old_low = ctx->bitlen[1]; - ctx->bitlen[1] += bits; - - if (ctx->bitlen[1] < old_low) - ctx->bitlen[0]++; - - ctx->bitlen[0] += (uint64_t)copy >> 61; - - if (ctx->buffer_len == 128) { - sha512_transform(ctx, ctx->buffer); - ctx->buffer_len = 0; + while (len > 0) { + size_t take = EOS_SHA512_BLOCK_SIZE - idx; + if (take > len) take = len; + memcpy(ctx->buffer + idx, data, take); + idx += take; + data += take; + len -= take; + if (idx == EOS_SHA512_BLOCK_SIZE) { + sha512_transform(ctx); + idx = 0; } } } -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; - - ctx->buffer[i++] = 0x80; - - if (i > 112) { - while (i < 128) - ctx->buffer[i++] = 0; - - sha512_transform(ctx, ctx->buffer); - i = 0; + /* SHA-512 encodes the message length as a 128-bit big-endian bit count. + * A bootloader never hashes anywhere near 2^61 bytes, so the high 64 + * bits are always zero; they are still written so the padding block is + * byte-exact against FIPS 180-4. */ + uint64_t bits = ctx->count * 8ULL; + size_t idx = (size_t)(ctx->count % EOS_SHA512_BLOCK_SIZE); + + ctx->buffer[idx++] = 0x80; + + if (idx > 112) { + while (idx < EOS_SHA512_BLOCK_SIZE) ctx->buffer[idx++] = 0; + sha512_transform(ctx); + idx = 0; } + while (idx < 112) ctx->buffer[idx++] = 0; - while (i < 112) - ctx->buffer[i++] = 0; - - store_be64(ctx->buffer + 112, ctx->bitlen[0]); - store_be64(ctx->buffer + 120, ctx->bitlen[1]); - - sha512_transform(ctx, ctx->buffer); + memset(ctx->buffer + 112, 0, 8); /* high 64 bits of length */ + for (int i = 0; i < 8; i++) { + ctx->buffer[120 + i] = (uint8_t)(bits >> (56 - 8 * i)); + } + sha512_transform(ctx); - for (int i2 = 0; i2 < 8; i2++) - store_be64(digest + i2 * 8, ctx->state[i2]); + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + digest[i * 8 + j] = (uint8_t)(ctx->state[i] >> (56 - 8 * j)); + } + } + /* Do not leave hash state on the stack of a boot path. */ 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); +} diff --git a/include/eos_boot_log.h b/include/eos_boot_log.h index 79b9b60..860833f 100644 --- a/include/eos_boot_log.h +++ b/include/eos_boot_log.h @@ -54,15 +54,12 @@ int eos_boot_log_init(void); void eos_boot_log_append(uint32_t event, uint32_t slot, uint32_t detail); /** - * @brief Read all boot log entries into a caller-provided buffer. - * - * Entries are returned in chronological order (oldest first). - * - * @param entries Output buffer for log entries. - * @param max_count Maximum number of entries the buffer can hold. - * @return Number of entries read, or negative error code. + * @brief Read one boot log entry by index. + * @param index Entry index (0 to EOS_BOOT_LOG_MAX - 1). + * @param out Receives the entry at @p index. + * @return EOS_OK on success, EOS_ERR_INVALID on a bad index or null @p out. */ -int eos_boot_log_read(eos_boot_log_entry_t *entries, uint32_t max_count); +int eos_boot_log_read(uint32_t index, eos_boot_log_entry_t *out); /** * @brief Get the number of log entries currently stored. diff --git a/include/eos_image.h b/include/eos_image.h index 049e6c8..a2a9d18 100644 --- a/include/eos_image.h +++ b/include/eos_image.h @@ -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 } diff --git a/include/eos_sha512.h b/include/eos_sha512.h deleted file mode 100644 index e089561..0000000 --- a/include/eos_sha512.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef EOS_SHA512_H -#define EOS_SHA512_H - -#include -#include - -typedef struct { - uint64_t state[8]; - uint64_t bitlen[2]; - uint8_t buffer[128]; - size_t buffer_len; -} sha512_ctx_t; - -void sha512_init(sha512_ctx_t *ctx); - -void sha512_update(sha512_ctx_t *ctx, - const uint8_t *data, - size_t len); - -void sha512_final(sha512_ctx_t *ctx, - uint8_t digest[64]); - -#endif \ No newline at end of file