id: 2366e3a4ba57427e91ac354360f731b8
parent_id: 61f2a696a6024beb8805879af4389563
item_type: 1
item_id: bb3506f46ead40df8a7f965a73339928
item_updated_time: 1786705546301
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"-14 \"],[-1,\"(after M20 completion)\\\n\\\nTwo full workspace\"],[1,\"12:45 (final\"],[0,\" audit\"],[-1,\"s\"],[0,\" com\"],[-1,\"pleted. 80+ issues found and fixed across both rounds.\\\nRemaining items below.\\\n\\\n## KLMS Team Action Required\\\n\\\nThese cannot be fixed on our side without KLMS changes:\\\n\\\n| # | Issue | What's needed |\\\n|---|-------|---------------|\\\n| C1 | `danger_accept_invalid_certs(true)` in klms_client.rs | CA cert returns BadSignature — need correct root CA or fix the cert chain |\\\n| M5 | H\"],[1,\"mitted: aa2fc9e)\\\n\\\n## KLMS Meeting Agenda (today ~13:10) — Private Key Encryption\\\n\\\n**The problem (M5/M10):** The KLMS provides field[8] (DGI A003 private key) as 144 bytes of\\\nAES-128-CBC encrypted PKCS#8 DER (IV=0, S-DEK key, PKCS7 padding). The card expects 48 bytes\\\nof AES-128-ECB encrypted 32-byte EC scalar (ISO 9797-1 M2 padding). Four mismatches:\\\n\\\n| | KLMS sends | Card expects |\\\n|---|---|---|\\\n| Content | full PKCS#8 DER (138 bytes) | 32-byte EC scalar |\\\n| Cipher | AES-128-CBC (chained) | AES-128-ECB (per-block) |\\\n| Padding | PKCS7 | ISO 9797-1 method 2 (`80 00 00...`) |\\\n| Size | 144 bytes | 48 bytes |\\\n\\\n**Our current workaround** (`worker.rs: prepare_klms_private_key`): decrypt with B252 dev S-DEK\\\n(CBC/IV=0), parse DER, scan for `04 20` to extract the 32-byte scalar, re-encrypt with the same\\\nS-DEK (AES-128-ECB + M2). Works on HW (24 APDUs, validated) but relies on:\\\n- the KLMS using the B252 dev S-DEK for its CBC encryption (matches only in dev)\\\n- h\"],[0,\"euri\"]],\"start1\":63,\"start2\":63,\"length1\":434,\"length2\":994},{\"diffs\":[[0,\"tic \"],[-1,\"EC\"],[1,\"`04 20`\"],[0,\" scalar \"],[-1,\"extra\"],[1,\"dete\"],[0,\"ction (\"],[-1,\"`04 20` scan) in worker.rs | KLMS should provide raw PKCS#8 DER or pre-encrypted\"],[1,\"ambiguous for scalars with leading 0x0420)\\\n\\\n**What to ask for (preferred → acceptable):**\\\n1. KLMS sends field[8] already card-ready:\"],[0,\" 48-\"]],\"start1\":1058,\"start2\":1058,\"length1\":110,\"length2\":166},{\"diffs\":[[0,\"dy: 48-byte \"],[-1,\"scalar (\"],[0,\"AES-128-ECB \"]],\"start1\":1217,\"start2\":1217,\"length1\":32,\"length2\":24},{\"diffs\":[[0,\"B + \"],[-1,\"ISO 9797-1 M2) instead of the current AES-128-CBC encrypted full DER (144 bytes) |\\\n| M10 | B252 dev DEK used to decrypt+re-encrypt KLMS private key | Once M5 is fixed (KLMS provides correct format), remove `prepare_klms_private_key()` and `b252_dek()` from the KLMS path entirely |\\\n| — | `report_usage` not wired into KLMS flow | **Awaiting KLMS team alignment** (user coordinating) — method exists in klms_client.rs; needs agreement on status values/payload/serial semantics before wiring in |\\\n\\\n**Private key format issue (M5/M10 detail):**\\\n\\\nThe KLMS currently provides field[8] as 144 bytes of AES-128-CBC encrypted PKCS#8 DER\\\n(IV=0, S-DEK key, PKCS7 padding). The card expects 48 bytes of AES-128-ECB encrypted\\\n32-byte EC scalar (ISO 9797-1 M2 padding). Mismatches: content (DER vs scalar),\\\nencryption (CBC vs ECB), padding (PKCS7 vs M2), size (144 vs 48).\\\n\\\nFix: KLMS provides field[8] as already DEK-encrypted 48-byte scalar (AES-128-ECB + M2) — we forward as-is.\\\n\\\n## Documented as Intentional (won't fix)\\\n\\\n| # | Issue | Resolution |\\\n|---|-------|------------|\\\n| H1 | IAM auth sends empty username/password | Commented — `ccs` is a public client, auth via mTLS only, matches Clypeum reference |\\\n| H4 | Hand-rolled RSA signature verification (textbook `sig^e mod n`) | Commented — Clypeum container format defines textbook RSA |\\\n| M17 | Wrapper key KDF reverse-engineered (NIST 800-108, zero LABEL/CONTEXT) | Commented — works correctly, not formally spec-verified |\\\n\\\n## Refactoring\\\n\\\n| # | Issue | Status |\\\n|---|-------|--------|\\\n| H7 | SCP03 handshake duplicated 5× | **RESOLVED 2026-08-14, HW-VERIFIED** — `kf-provision::handshake` module; all 5 call sites delegate. |\\\n| M20 (run_production) | 219-line god function | **RESOLVED 2026-08-14** — `ProductionRequest` + `ProductionSetup::resolve()` + `reinstall_between_attempts()` + `provision_attempt()` + `validate_attempt()`; run_production is a thin retry loop. |\\\n| M20 (StationApp::update) | 149-line god function | **RESOLVED 2026-08-14** — split into `poll_workers`, `render_klms_only`, `render_log_console`, `render_sidebar`, `on_mode_changed`, `render_central_panel` + `nav_button` helper. |\\\n| M20 (Provisioner::run_inner) | ~193-line god function | **RESOLVED 2026-08-14** — payload tail (encode DGI → split → STORE DATA → lifecycle → post-perso) extracted into shared `helpers::deliver_payload()`; now ~100 lines (UID → fetch → channel → deliver → audit). |\\\n| M20 (KlmsProvisioner::run_inner) | ~165-line god function | **RESOLVED 2026-08-14** — uses same `deliver_payload()` (s_dek=None → KLMS must pre-encrypt); now ~90 lines. NOTE: lifecycle/post-perso now honors `config.skip_lifecycle_transition` (previously ignored in KLMS path; no caller sets it). |\\\n| M20 (run_klms_auto_provision_inner) | ~230-line god function | **RESOLVED 2026-08-14, awaiting HW test** — split into `connect_and_identify` + `init_update_for_klms` + `fetch_klms_payload` + `log_payload_fields` + `establish_klms_channel`; orchestrator now ~60 lines. INIT UPDATE parsing now uses shared `kf_provision::parse_init_update` (struct `InitUpdateResponse` incl. key_version); manual offset parsing removed. |\\\n| ~~H2~~ | Audit trace doc mismatch | **RESOLVED 2026-08-14** |\\\n\\\n**M20 fully resolved — no god functions remain >100 lines except panel renderers (GUI layout, low risk).**\\\n\\\n## Functional Items (within our control)\\\n\\\n| # | Issue | Status |\\\n|---|-------|--------|\\\n| — | KLMS panel retry after first-attempt failure | **RESOLVED 2026-08-14, HW-VERIFIED** — `should_retry_with_reinstall` retries any failed attempt once after applet re-install. |\\\n| — | KLMS provisioning 6A 80 on DGI A003 | **RESOLVED (HW-verified)** — 24 APDUs, full success incl. validation |\\\n| — | Session key logging | **RESOLVED 2026-08-14** — redacted; field logging now in `log_payload_fields` with named constants. |\\\n| — | NFC poll interval | Already 100ms |\\\n\\\n## Cosmetic (acknowledged, no action)\\\n\\\n- `kf-provision/lib.rs` local `KF_APPLET_SELECT_AID` copy (dependency direction)\\\n- `STATIC_KEY_TABLE` in kf-klms duplicates dev_profiles.toml (test isolation)\\\n- ISD SELECT variants (empty-data vs explicit-AID) — cosmetic; SELECTs deliberately stay in callers after H7\\\n- Minimal-variant dead-code warnings (panels exist unconditionally, calls are cfg-gated) — to be tackled in the next full audit\"],[1,\"M2-encrypted scalar (station forwards as-is, zero local crypto)\\\n2. KLMS sends raw unencrypted PKCS#8 DER (station extracts scalar + encrypts with session S-DEK)\\\n3. Keep CBC blob but document: which key encrypts (S-DEK per OEF?), which IV, and provide the EC\\\n   scalar offset in `extra` (removes the `04 20` heuristic)\\\n\\\n**Also raise:**\\\n- `cryptoDataContainerId` response header is empty ('') — should carry the container UUID;\\\n  we have `validate_cid` ready to enforce it once populated\\\n- Container signature: we have textbook-RSA `verify_signature` (Clypeum scheme) + pubkey PEM\\\n  (`data/clypeum_signing_pubkey.pem`) — confirm the signing key so verification can be wired in\\\n- TLS: `Clypeum_Root_CA.pem` fails signature validation (BadSignature) → we run\\\n  `danger_accept_invalid_certs(true)` — need a valid CA cert/chain\\\n- `report_usage`: align status values/payload/productSerial semantics; client method is ready\\\n\\\n## KLMS Team Action Required\\\n\\\n| # | Issue | What's needed |\\\n|---|-------|---------------|\\\n| C1 | `danger_accept_invalid_certs(true)` | Valid root CA or fixed cert chain |\\\n| M5/M10 | Private key format (see agenda above) | Card-ready 48-byte ECB+M2 scalar preferred |\\\n| — | Empty `cryptoDataContainerId` header | Populate with container UUID |\\\n| — | Container signature key confirmation | Confirm signing pubkey matches dev KLMS |\\\n| — | `report_usage` alignment | Status values/payload semantics |\\\n\\\n## Documented as Intentional (won't fix)\\\n\\\n| # | Issue | Resolution |\\\n|---|-------|------------|\\\n| H1 | IAM empty username/password | mTLS public client, matches Clypeum reference |\\\n| H4 | Textbook RSA signature verify | Clypeum container format |\\\n| M17 | Wrapper key KDF (NIST 800-108, zero LABEL/CONTEXT) | Works, constants documented |\\\n| — | kf-provision local KF_APPLET_SELECT_AID copy | Dependency direction |\\\n| — | STATIC_KEY_TABLE duplication in kf-klms | Test isolation |\\\n| — | ISD SELECT variants (empty vs explicit AID) | Deliberate, callers own SELECTs |\\\n| — | prod-station dev-keys copy | Gated `dev-keys` feature, provenance documented |\\\n\\\n## Refactoring — COMPLETE\\\n\\\n| # | Item | Status |\\\n|---|------|--------|\\\n| H7 | SCP03 handshake ×5 | **DONE, HW-verified** — `kf-provision::handshake` |\\\n| M20 | All god functions | **DONE, HW-verified** — run_production, StationApp::update, run_klms_auto_provision_inner, Provisioner/KlmsProvisioner (shared `deliver_payload`) |\\\n| H2 | Audit trace doc | **DONE** |\\\n\\\n## Deferred Polish (non-blocking, next audit)\\\n\\\n| # | Item |\\\n|---|------|\\\n| M6 | Scp03SetupError/ResetError/FactoryError are clones of HandshakeError — unify |\\\n| M11 | Mock applet C-MAC verify block duplicated ×3 — extract helper |\\\n| M17' | ProdMsg progress is stringly-typed (\\\"in progress\\\"/\\\"FAILED\\\") — typed enum |\\\n| L15 | 10 inline SELECT APDU constructions — add builders::select |\\\n| L12 | kf-klms has zero in-crate tests |\\\n| M7 | Host-challenge RNG duplicated (generate_host_challenge / inline / _pub) |\\\n\\\n## Final Audit State (commit aa2fc9e)\\\n\\\n- **All 3 build variants compile with ZERO warnings** (full / test / minimal) — dead code\\\n  cfg-gated per feature; FFI unsafe silenced with module-level SAFETY note\\\n- clippy clean (all variants), rustfmt clean, cargo doc clean, 94 tests pass\\\n- KLMS flow HW-verified end-to-end (retry-on-failure + re-install works)\\\n- Session keys/private scalar redacted in logs; encrypted private key shown\"]],\"start1\":1239,\"start2\":1239,\"length1\":4311,\"length2\":3371}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-08-14T11:10:01.418Z
created_time: 2026-08-14T11:10:01.418Z
is_locked: 0
type_: 13