2026-05-02 12:14:21 -04:00
|
|
|
# Partition table for OTA. Two equal app slots (ota_0 / ota_1), no
|
|
|
|
|
# `factory` partition — once OTA is enabled we always boot from one of
|
|
|
|
|
# the OTA slots, with the other holding the previous (or pending) image.
|
|
|
|
|
#
|
|
|
|
|
# Flash layout (4 MB total):
|
|
|
|
|
# 0x001000-0x008FFF bootloader (~28 KB)
|
Add cosign signature verification (OTA phase 4a)
Each OTA fetch is now gated on a Sigstore Bundle signature whose Fulcio
cert identifies a member of an allowlist hardcoded in src/trust.rs
(currently imjasonh@gmail.com / accounts.google.com). The allowlist
cannot be changed via OTA -- only by editing source and reflashing
over USB.
Publisher (Makefile): make publish now runs cosign sign --yes after
the publisher push. Cosign keyless OIDC pops a browser the first time;
subsequent signs reuse the cached token within ~10min.
Firmware:
- src/trust.rs: TRUSTED_IDENTITIES list + bundled Sigstore root and
intermediate CA PEMs (trust/fulcio_root.pem, fulcio_intermediate.pem).
- src/sig.rs: parse Sigstore Bundle v0.3 (DSSE envelope), verify (a)
Fulcio cert SAN email + OID 1.3.6.1.4.1.57264.1.1 issuer match the
allowlist, (b) leaf cert chains to bundled Sigstore root via
P-384 ECDSA-SHA384, (c) DSSE signature verifies via P-256 ECDSA-SHA256
over the PAE, (d) in-toto Statement subject digest binds to our
manifest digest.
- src/ota.rs: fetch_manifest now returns the manifest's own SHA256 (the
digest cosign signed), not just the parsed body. New
fetch_signature_bundle walks the OCI 1.1 referrers layout cosign uses
(image index -> inner manifest -> bundle blob). Sig is fetched and
verified before the firmware download starts.
Crates: p256, p384, x509-cert (with pem feature), base64. Adds
~350 KB to the firmware -- repartitioned ota slots from 1.5MB to
1.75MB to fit (USB-only migration). Bumped OTA thread stack to
48KB for cert-parsing headroom.
Verified end-to-end: Jason signed :latest with cosign keyless,
device polled, all four verification steps passed (identity, chain,
DSSE sig, in-toto binding), then downloaded and applied as before.
Phase 4b (Rekor SET / transparency log inclusion proof) and 4c
(operational hardening) remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:50:03 -04:00
|
|
|
# 0x008000-0x008FFF partition table itself (4 KB)
|
2026-05-02 12:14:21 -04:00
|
|
|
# 0x009000-0x00EFFF nvs (24 KB)
|
|
|
|
|
# 0x00F000-0x010FFF otadata (8 KB) - which slot to boot
|
|
|
|
|
# 0x011000-0x011FFF phy_init (4 KB)
|
2026-05-02 15:39:33 -04:00
|
|
|
# 0x020000-0x20FFFF ota_0 (1.9375 MB)
|
|
|
|
|
# 0x210000-0x3FFFFF ota_1 (1.9375 MB)
|
Add cosign signature verification (OTA phase 4a)
Each OTA fetch is now gated on a Sigstore Bundle signature whose Fulcio
cert identifies a member of an allowlist hardcoded in src/trust.rs
(currently imjasonh@gmail.com / accounts.google.com). The allowlist
cannot be changed via OTA -- only by editing source and reflashing
over USB.
Publisher (Makefile): make publish now runs cosign sign --yes after
the publisher push. Cosign keyless OIDC pops a browser the first time;
subsequent signs reuse the cached token within ~10min.
Firmware:
- src/trust.rs: TRUSTED_IDENTITIES list + bundled Sigstore root and
intermediate CA PEMs (trust/fulcio_root.pem, fulcio_intermediate.pem).
- src/sig.rs: parse Sigstore Bundle v0.3 (DSSE envelope), verify (a)
Fulcio cert SAN email + OID 1.3.6.1.4.1.57264.1.1 issuer match the
allowlist, (b) leaf cert chains to bundled Sigstore root via
P-384 ECDSA-SHA384, (c) DSSE signature verifies via P-256 ECDSA-SHA256
over the PAE, (d) in-toto Statement subject digest binds to our
manifest digest.
- src/ota.rs: fetch_manifest now returns the manifest's own SHA256 (the
digest cosign signed), not just the parsed body. New
fetch_signature_bundle walks the OCI 1.1 referrers layout cosign uses
(image index -> inner manifest -> bundle blob). Sig is fetched and
verified before the firmware download starts.
Crates: p256, p384, x509-cert (with pem feature), base64. Adds
~350 KB to the firmware -- repartitioned ota slots from 1.5MB to
1.75MB to fit (USB-only migration). Bumped OTA thread stack to
48KB for cert-parsing headroom.
Verified end-to-end: Jason signed :latest with cosign keyless,
device polled, all four verification steps passed (identity, chain,
DSSE sig, in-toto binding), then downloaded and applied as before.
Phase 4b (Rekor SET / transparency log inclusion proof) and 4c
(operational hardening) remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:50:03 -04:00
|
|
|
#
|
2026-05-02 15:39:33 -04:00
|
|
|
# Bumped from 1.75 MB to 1.9375 MB slots so we can absorb future feature
|
|
|
|
|
# work (e-ink driver, more crates) without immediately running out. There
|
|
|
|
|
# is no unused space left at the end of flash; if we need it later (extra
|
|
|
|
|
# nvs, spiffs, etc.) we'd have to shrink slots again. Repartitioning is
|
|
|
|
|
# a USB-only migration (`make flash-all`).
|
2026-05-02 12:14:21 -04:00
|
|
|
#
|
2026-05-02 15:39:33 -04:00
|
|
|
# Name, Type, SubType, Offset, Size
|
|
|
|
|
nvs, data, nvs, 0x9000, 0x6000
|
|
|
|
|
otadata, data, ota, 0xf000, 0x2000
|
|
|
|
|
phy_init, data, phy, 0x11000, 0x1000
|
|
|
|
|
ota_0, app, ota_0, 0x20000, 0x1F0000
|
|
|
|
|
ota_1, app, ota_1, 0x210000, 0x1F0000
|