Repository navigation
Description
The .efw image header is a wire format with two independent definitions:
| repo | file | type | pins |
|---|---|---|---|
| eFirmware | include/efw/efw_image.h |
efw_image_header_t |
tests/test_abi.c, 75 checks (eFirmware#2) |
| eBoot | include/eos_image.h |
eos_image_header_t |
4 _Static_asserts |
eBoot parses the images eFirmware produces, so the layout is shared. Both sides
pin something. Neither pins enough, and nobody compares the two sets of numbers.
1. Four of fourteen fields are pinned — a field swap passes silently
eBoot asserts sizeof == 156 and the offsets of hash, sig_type, signature.
Ten fields have no assert. Swapping two adjacent same-width fields moves neither
the size nor any pinned offset. I compiled the header with load_addr and
entry_addr transposed — a plausible editing slip:
all four existing static asserts: PASSED
load_addr is now at offset 16 (was 12)
entry_addr is now at offset 12 (was 16)
Clean compile. The bootloader then loads the image at its entry point and jumps
to its load address.
2. No constant's value is pinned on this side
EOS_IMG_MAGIC, EOS_HASH_SIZE, EOS_SIG_MAX_SIZE, EOS_IMG_SIGNED_LEN and
the five eos_sig_type_t values all travel inside the image, and all are wire
format. Changing EOS_SIG_ED25519 = 3 to 4, or reordering the enum, compiles
cleanly, passes all four static asserts, and makes eBoot misread the signature
type of every image already in the field. eFirmware pins EFW_IMAGE_MAGIC == 0x454F5349u and EFW_SIG_ED25519 == 3 on its side; eBoot pins neither.
3. The two repos disagree on header version — found while measuring the above
| value | |
|---|---|
EFW_IMAGE_HDR_VERSION (eFirmware, stamped into every image by efw_image.c:55) |
1 |
EOS_IMAGE_HDR_VERSION (eBoot) |
2 |
eBoot's own header documents what the difference means:
v1 — the digital signature covered
hash[]only.
v2 — the signature covers the whole header prefix.
v1 images must be re-signed; their signatures do not verify under v2.
core/image_verify.c:89 accepts any version in 1..2. core/image_verify.c:202
then verifies over EOS_IMG_SIGNED_LEN unconditionally — there is no branch
on hdr_version anywhere in the file. So a v1 image is admitted by the parser and
then fails signature verification, by eBoot's own stated definition of v1.
This fails closed — it rejects, it does not accept a forgery — so it is not an
escalation. But every signed image the organisation's own tool produces is stamped
v1, and eBoot supports only v2. Either the parser should reject v1 outright with
EOS_ERR_VERSION, or the verifier should branch, or efwtool should stamp 2. That
is a security-policy call, so it is not part of the PR below — it needs a
decision.
What the PR does
Adds tests/unit/test_image_abi.c: every field offset and width, sizeof, and
the value of every shared constant including the signature-type enum — the same
treatment eFirmware#2 gives its side, plus the constant values eFirmware's test
covers and eBoot's asserts do not.
Scope is deliberately the drift detector only. Items 1 and 2 are closed by it.
Item 3 is reported, not fixed.
Blocked on
master does not build (eos_crc32 conflicting types); #58 repairs it. This
branches from #58.
Activity
[-]Pin the .efw wire format in eBoot — two independent definitions with no cross-check[/-][+]Pin the .efw wire format in eBoot — a field swap and every constant value drift undetected[/+]test: pin the .efw image header wire format
test: pin the .efw image header wire format (38 checks) (#67)
fix(image): pin the whole .efw header, not four fields of it (#87)