id: f494e13a8b2e4169ba24bfdd6ff69b1f
parent_id: 
item_type: 1
item_id: fa9e72ee12554f978a86ab3f7888cc63
item_updated_time: 1782493599767
title_diff: "[{\"diffs\":[[1,\"Keyfob Station — Operating Modes & KLMS Session-Key Offload\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":59}]"
body_diff: "[{\"diffs\":[[1,\"# Keyfob Station — Operating Modes & KLMS Session-Key Offload\\\n\\\n> **Status:** ✅ **Implemented (2026-06-26, rev 4).** The two modes are now two\\\n> binaries — `kf-dev-station` (Mode B + C) and `kf-prod-station` (Mode A) — plus\\\n> the `kf-klms` crate (`KlmsClient` + `MockKlmsClient`), `KlmsProvisioner`, and\\\n> `Scp03Channel::establish_from_session_keys`. 86 tests green.\\\n> See companion note **\\\"Keyfob Station — Two-Binary Split (dev vs production)\\\"**.\\\n> The original design below is retained for reference.\\\n\\\n---\\\n\\\n## 1. Three operating modes\\\n\\\nThe station supports three modes. Provisioning splits into two **key-source strategies**; firmware flashing is independent.\\\n\\\n| Mode | Master / static keys location | Station holds | Use case | Binary |\\\n|---|---|---|---|---|\\\n| **A — Production provisioning (KLMS session-key offload)** | KLMS enterprise HSM (master key never leaves) | **Only ephemeral session keys** for the active channel | Factory floor (untrusted station) | `kf-prod-station` |\\\n| **B — Development provisioning (static keys inline)** | Known dev key set on the station | Static keys (S-ENC/MAC/DEK) in RAM for the run | Lab bring-up, protocol validation, dev samples (OEF B212) | `kf-dev-station` |\\\n| **C — Firmware flashing (SEMS Lite)** | N/A (load-file playback, not SCP03 personalization) | Script + APDU stream | (Re)flash secure-element firmware before/instead of provisioning | `kf-dev-station` |\\\n\\\n### GUI mapping\\\n- `kf-dev-station` top-level switch: **Provisioning** ↔ **Firmware Flashing**.\\\n- `kf-prod-station`: **Provisioning only** (no flashing selector).\\\n\\\n---\\\n\\\n## 2. Mode A — Production: KLMS session-key offload\\\n\\\n### 2.1 Goal\\\nThe **master key and the per-fob static keys never leave the KLMS HSM.** The factory station is treated as untrusted and receives only throwaway session keys plus the already-encrypted personalization payload.\\\n\\\n### 2.2 Why this is safe & feasible\\\nEvery SCP03 operation (KDF3 static derivation, Phase-2 session-key derivation, host/card cryptograms) is an AES-CMAC over data the station can supply. An HSM that holds the master key and performs AES-CMAC on-device can compute the **entire** handshake internally and emit only session keys.\\\n\\\n### 2.3 Event & data flow\\\n\\\n```\\\nSTATION                       KEY FOB (card)                 KLMS (+ HSM)\\\n─────────────────────────────────────────────────────────────────────────────\\\n 1. INITIALIZE UPDATE  ──────►\\\n    80 50 <KVN> 00 08 <host_challenge[8]>\\\n                        ◄─────  2. div_data(10=UID) ‖ key_info ‖ seq_ctr[3]\\\n                                   ‖ card_challenge[8] ‖ card_cryptogram[8]\\\n\\\n 3. POST /provision/session  ──────────────────────────────────►\\\n    { uid, key_version, host_challenge, card_challenge,\\\n      sequence_counter, card_cryptogram,\\\n      fob_identity (SPID/FESN → fetch payload) }\\\n                                  4. HSM internal (master key sealed):\\\n                                     • KDF3(uid) → S-ENC,S-MAC,S-DEK          (Phase 1)\\\n                                     • VERIFY card_cryptogram  ← reject rogue/clone fob\\\n                                     • session KDF(ctx=SeqCnt‖host_chal‖card_chal)\\\n                                         → SES-ENC, SES-MAC, SES-RMAC          (Phase 2)\\\n                                     • host_cryptogram = cmac(SES-MAC, …)\\\n                                     • A003_ciphertext   = AES-CBC(S-DEK, priv_key)\\\n                        ◄──────────────────────────────────── 5. response\\\n    { ses_enc, ses_mac, ses_rmac,\\\n      host_cryptogram,\\\n      personalization_items (A003 already S-DEK-encrypted),\\\n      post_perso_commands, transaction_id }\\\n\\\n 6. EXTERNAL AUTHENTICATE  ──────►\\\n    84 82 <level> 00 <host_cryptogram>   (C-MAC over cmd via SES-MAC)\\\n                        ◄─────  9000  (channel open)\\\n\\\n 7. STORE DATA blocks (DGI payload)  ──────►  (each C-MAC'd with SES-MAC)\\\n 8. Lifecycle transition  84 E2 80 <P2> 00  ──────►   → FACTORY\\\n 9. Post-perso raw commands (no SM)         ──────►\\\n10. DROP session keys → zeroized\\\n```\\\n\\\n### 2.4 What never leaves the HSM\\\n- Master key, S-ENC / S-MAC / S-DEK (static, long-lived), and the **plaintext fob private key** (A003 is pre-encrypted by the HSM).\\\n\\\n### 2.5 What the station holds (RAM-only, zeroized on drop)\\\n- Ephemeral **session keys** for the active channel (unavoidable — the terminal must C-MAC every `STORE DATA`).\\\n- The pre-encrypted A003 ciphertext (already encrypted; not decryptable by the station).\\\n\\\n### 2.6 Security properties gained over \\\"KLMS returns static keys\\\"\\\n- A compromised station **cannot derive keys for other UIDs** (no master key).\\\n- A leaked session key compromises **only that one finished session**, not the fob for life.\\\n- The **card is authenticated by the KLMS** (card_cryptogram check): a rogue/blank fob presenting a UID it doesn't actually hold is rejected before any session keys are issued.\\\n\\\n### 2.7 Implemented in the codebase\\\n- **`Scp03Channel::establish_from_session_keys`** — inject KLMS-issued session keys (no static keys, no card-cryptogram check; the KLMS authenticates the card).\\\n- **`KlmsProvisioner`** (`kf-provision/src/klms.rs`) — production pipeline; rejects items flagged `encrypt_with_dek=true`.\\\n- **`kf-klms`** — `KlmsClient` trait + `SessionRequest/Response/Content` + `MockKlmsClient` (emulates the HSM, verifies card cryptogram).\\\n\\\n### 2.8 Open questions for SecOps / Clypeum\\\n- [ ] REST/DLL wire contract (trait is the interface; implementation pending).\\\n- [ ] mTLS details for the station↔KLMS link.\\\n- [ ] **Does KLMS verify card_cryptogram?** (Recommended — the mock already does.)\\\n- [ ] TTL on issued session keys (recommended ~5 s).\\\n- [ ] Production security level: C-MAC only, or C-MAC + R-MAC + C-DEC?\\\n- [ ] Key-version strategy (how the station knows which KVN the fob expects).\\\n\\\n---\\\n\\\n## 3. Mode B — Development: static keys inline\\\n\\\n- **Dev samples** (OEF B212) ship with a **pre-defined static SCP03 key set** already loaded — no KLMS, no diversification: Phase 2 only, locally.\\\n- The **KDF/diversification path** (Phase 1) can also be exercised in dev using the dev master key + static UID held locally.\\\n- This is what `kf-dev-station` does today against the simulated JCOP applet.\\\n\\\n---\\\n\\\n## 4. Mode C — Firmware flashing (SEMS Lite)\\\n\\\n- Independent of SCP03 personalization: plays a **SEMS Lite load-file script** natively to flash new firmware onto the secure element.\\\n- UI shell exists in `kf-dev-station` (load + preview); the **native player is pending the SEMS Lite script-format example**.\\\n- A dev station will frequently: **flash firmware (C) → then provision (B)**.\\\n\\\n---\\\n\\\n## 5. How the three modes coexist\\\n\\\n```\\\n  kf-dev-station ──┬─ Provisioning (static keys inline)   [Mode B]\\\n                   └─ Firmware Flashing (SEMS Lite)       [Mode C]\\\n\\\n  kf-prod-station ──── Provisioning (KLMS session offload) [Mode A]\\\n```\\\n- Shared core: `Provisioner`/`KlmsProvisioner` + `Scp03Channel` + DGI/STORE-DATA/lifecycle logic.\\\n- Diverges only at: **where keys come from** and **who establishes the channel**.\\\n\\\n---\\\n\\\n## 6. Suggested implementation order (status)\\\n1. ✅ Decouple `Scp03Channel` — `establish_from_session_keys`.\\\n2. ✅ `KlmsProvisioner` externally-established-channel path.\\\n3. ✅ Define the KLMS session-offload serde contract (`SessionRequest/Response/Content`).\\\n4. ✅ `kf-prod-station` GUI (provisioning only).\\\n5. ✅ Keep Mode B as default (`kf-dev-station`).\\\n6. ⏳ DLL-backed `KlmsClient`.\\\n7. ⏳ Real `pcsc` transport.\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":7410}]"
metadata_diff: {"new":{"id":"fa9e72ee12554f978a86ab3f7888cc63","parent_id":"283dd54f183a4ce69366648f091336d1","latitude":"0.00000000","longitude":"0.00000000","altitude":"0.0000","author":"","source_url":"","is_todo":0,"todo_due":0,"todo_completed":0,"source":"joplin-desktop","source_application":"net.cozic.joplin-desktop","application_data":"","order":1782487881636,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":0},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-26T17:07:34.685Z
created_time: 2026-06-26T17:07:34.685Z
type_: 13