No secure element is on-site: event signing runs on the software HMAC (EVENT_SIGNING_KEY, an env var on the host disk), so the ledger is tamper-EVIDENT but forgeable by anyone who owns the host. Several pages overstated it as present-tense "ATECC608-signed / unforgeable"; correct them. - NEW concepts/hardware-signer-options.md: four options for a non-extractable signing key (USB HSM / YubiKey / reuse the TPM / plain-dongle trap) + the recommendation (TPM interim → USB-HSM target; ATECC608 stays for the embedded ESP32, wrong part for a PC host). - entities/atecc608.md: UPCOMING-not-present status banner + PC-vs-embedded. - disk-os-hardening.md: fix the live-USB row (BIOS boot-order password is load-bearing, not Secure Boot — a signed live USB runs); add a physical-tamper chain (Dell 7070 CMOS-reset → live-USB → PCR-7 same-signer unseal) + accepted risks (that unseal, unsigned-initramfs evil-maid, operator-USB read TODO). - open-questions #6 reframed; standing-decisions / overview / threat-model / index de-overstated; log query entry. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
5.5 KiB
type, tags, sources, updated
| type | tags | sources | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|
| concept |
|
2026-07-02 |
Hardware signer options (non-extractable ledger signing key)
Where the append-only-event-chain's private signing key should live so that an adversary who owns the host — including one who decrypts the disk via the disk-os-hardening physical-tamper chain — still cannot forge the ledger. This is the still-open open-questions, reframed once it became clear (2026-07-02) that no secure element is on-site and the atecc608 isn't even the right part for a PC.
The problem in one line
Today signing uses the software SoftwareSigner — HMAC-SHA256, key in EVENT_SIGNING_KEY, an env
var on the host disk. HMAC is symmetric: the same secret signs and verifies, and it sits on the
box. So the chain is tamper-evident (a blind editor without the key breaks it, and verifyChain()
pinpoints where) but not unforgeable — anyone who reads the key re-signs a doctored chain and it
verifies clean. The fix is a signer whose private key is non-extractable: the host can ask it to
sign, but can never read the key. That is a property of an HSM / smartcard / TPM — not of anything
that merely stores a key.
The four options
1. USB HSM — Nitrokey HSM 2 / SmartCard-HSM (recommended target)
A ~€50 USB device with a non-extractable EC key (secp256r1 — same curve family as the ATECC608). The
host signs each event over PKCS#11; the key never leaves the token. It's essentially "the
ATECC608, but on USB instead of soldered", so it drops into the existing signer.ts seam (the
keyId field + the TODO(atecc608) public-key-verifier hook).
- License fit: OpenSC (the PKCS#11 stack) is LGPL/permissive — no vendor lock-in, matches the all-MIT/Apache/BSD technology-stack.
- Downside: it's removable — an operator can pocket it. But that fails closed (no token → can't sign → visibly noticed), and it can be epoxied / locked inside the case.
2. YubiKey (PIV or OpenPGP applet)
Also a non-extractable EC/RSA key over PKCS#11; very robust, widely deployed; works the same way as the Nitrokey for this purpose. Slightly more oriented to human 2FA than to an always-present signing oracle, and the vendor stack is less fully-open than OpenSC. ~$50–70. Fine as a substitute for option 1.
3. Reuse the on-board TPM 2.0 (recommended interim — free)
The booth PC already has a TPM (it's what seals LUKS — tpm). A TPM can also hold a non-extractable signing key and sign over it. Zero extra hardware, closes the "key in a plaintext env file" hole immediately.
- The nuance: don't reuse the LUKS sealing arrangement. That key is PCR-7-sealed, and the battery-pull → live-USB → PCR-7 chain (disk-os-hardening) defeats PCR-7-only policies. Bind the signing key to the TPM without a PCR policy (or with a PIN) so it's about non-extractability, not boot-state — then an attacker who decrypts the disk still can't pull it.
- Weaker than a dedicated HSM against a sophisticated bus-sniffing attacker, but far stronger than today's on-disk HMAC.
4. Plain USB "sentinel" / dongle (the trap — avoid)
A generic USB flash drive holding a key file, or a license-dongle that only gates "is this USB present". Useless here: if the key is readable off the stick, the host-owner copies it, exactly like the env var. Presence-gating is not integrity. Only a device that signs internally delivers non-extractability. Do not go this route.
Recommendation
- Now (free): move signing to a TPM-held key (option 3, no PCR policy) — kills the plaintext-key-on-disk exposure using hardware already present.
- Target (purchasable): a USB HSM (Nitrokey HSM 2) as the concrete stand-in for the still-
planned atecc608 — same non-extractable-EC model, fits
signer.ts, open tooling; physically lock it in the case. - atecc608 stays reserved for the deferred esp32-custom-controller (embedded), not the PC host.
Whichever is chosen, the code seam already exists: each event stores its keyId, so a swap is a new
Signer impl and old events stay verifiable under their original key — no migration of history.
The mental model to keep
Against the physical adversary who opens the case, the only real protection for the financial record is a signing key even root can't read. That is an HSM/smartcard/TPM property, never a USB-storage property — and it's precisely what's missing today. Note the layering: disk-os-hardening raises the cost of reaching the disk; a hardware signer makes the ledger unforgeable even after the disk is reached; and reconciliation against records the box doesn't hold is the backstop that survives a fully-owned host. They are complements, not substitutes.
Relates
- append-only-event-chain — the ledger this key signs; why software signing isn't enough.
- atecc608 — the originally-specified secure element (upcoming; embedded, not PC).
- tpm — the on-board part; option 3, and the LUKS-sealing analysis.
- disk-os-hardening — the physical-tamper chain that makes a non-extractable key necessary.
- open-questions #6 (secure-element integration), #7 (JWT symmetric→asymmetric — same key-custody argument for auth tokens).
- threat-model / reconciliation — the adversary and the backstop control.