Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion full-ai-cluster/INJECTION-POINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,37 @@ Allowlist from `zflash.ts`:
| **Stage** | Cluster console at install time → encrypted blob persisted to USB ESP after successful auth (post-install service trigger) |
| **Content class** | **Secret material** (encrypted-at-rest; key never hits disk) |
| **Operator-driven via** | Boot-sequence auth-method picker (4 options: restore-from-blob / fresh-device-flow / operator-PAT / skip) + operator passphrase |
| **Encryption** | AES-256-GCM with key derived from `HKDF(USB-UUID \|\| operator-passphrase, salt, info)` |
| **Encryption** | AES-256-GCM; key derived via 2-layer scrypt → HKDF chain (full mechanism + parameters below) |
| **ESP filenames** | `/esp/zeta-creds.enc` (encrypted) + `/esp/zeta-creds-manifest.yaml` (declarative + operator-readable) |
| **Backlog** | [B-0852](../docs/backlog/P1/B-0852-credential-persistence-on-usb-esp-plus-boot-sequence-auth-method-picker-encrypted-blob-bound-to-usb-uuid-plus-operator-passphrase-aaron-2026-05-27.md) (P1, open, M-effort) |
| **Covers credentials** | per declarative manifest: `gh-cli` (`~/.config/gh/hosts.yml`), `claude` (per-persona), `gemini` (per-persona), `codex` (per-persona), `ssh-host-keys`, `ssh-operator-pubkey` |
| **Constitutional-rail compliance** | Secret material; encrypted-at-rest on ESP IS allowed because the operator-passphrase + USB-UUID binding means the ESP-stored blob is useless without operator presence — the consent floor stays at operator-typed passphrase, not at USB-physical possession alone |

#### KDF chain detail (mechanism + parameters)

The 32-byte AES-256-GCM key is derived in two layers; full implementation in `tools/installer/zeta-creds-crypto.ts` (the `deriveKey` function + the `SCRYPT_*` + `KEY_LEN` + `SALT_LEN` + `HKDF_INFO` constants declared near the top of the file).

**Layer 1 — scrypt** (memory-hard work-factor KDF):

```text
stretched = scrypt(passphrase, salt, length=32, N=2^17, r=8, p=1, maxmem=256MB)
```

scrypt does NOT increase the underlying entropy of the operator passphrase (a weak passphrase remains weak in information-theoretic terms). What scrypt provides is a tunable **work-factor cost** per guess: with the parameters below, each candidate passphrase requires ~128MB of memory and (empirically per `zeta-creds-crypto.ts` Layer 1 source comments, on the maintainer's modern CPU at parameter-selection time) ~1-2 seconds of CPU per derivation. This makes brute-force attacks memory-prohibitively expensive on GPU/ASIC (per the 2026-05-27 security-review HIGH finding documented in the source: HKDF alone assumes high-entropy IKM, which user-typed passphrases violate; scrypt is the layer that makes the IKM cryptographically suitable for HKDF input).

Parameter selection: `N=2^17`, `r=8`, `p=1` — per [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt) recommended scrypt parameters (current at parameter-selection time 2026-05-27; bump procedure: visit the cheat sheet at next security-review cadence, update both the cheat-sheet-citation date here AND the `SCRYPT_N`/`SCRYPT_R`/`SCRYPT_P` constants in `zeta-creds-crypto.ts`). Per-machine operational cost will vary with CPU + memory bandwidth; the ~1-2s figure is anchored to the source-code comment's empirical timing context.

**Layer 2 — HKDF-SHA256** (binds key to USB UUID):

```text
ikm = concat(usbUuid_utf8, "|", stretched)
key = HKDF-SHA256(ikm, salt, info="zeta-b0852-cred-persistence-v1", length=32)
```

HKDF binds the stretched secret to the USB UUID via IKM concatenation. Wrong USB → different IKM → different HKDF output → AES-GCM auth tag verification fails → structured error returned (not garbled plaintext). Defends against copy-blob-to-different-USB attack (operator-named threat 2026-05-27: *"we can put a key on the usb too if wnated tied to the uuid so it can't be copied to uuid"*).

Both layers must reproduce identically at decrypt time for the AES-GCM auth tag to verify; salt is per-blob (generated at encrypt time; stored in envelope; required at decrypt).

### 6. GitHub-creds-at-flash-time variants (B-0852 picker options 1 + 3)

Per operator 2026-05-27 verbatim: *"the current ones on my machine OR a token i generate on the website."*
Expand Down
Loading