--- type: concept tags: [parking, security, integrity, crypto, hardware, threat-model] sources: [] updated: 2026-07-02 --- # Hardware signer options (non-extractable ledger signing key) Where the **[[append-only-event-chain|signed ledger]]'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|open-question #6]], 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|stack constraint]]. - **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 1. **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. 2. **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. 3. **[[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.