id: c9486e169a634d1aa5a1322da698202d
parent_id: ffacef8cd9ba4582b2e38c4a7ca0af8b
item_type: 1
item_id: bb3506f46ead40df8a7f965a73339928
item_updated_time: 1786716055801
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"14 1\"],[-1,\"4:05 (audit round 5 committed: aabf7b6, pushed to origin)\\\n\\\n## KLMS Team Action Required (external — unchanged)\\\n\\\n**The problem (M5/M10\"],[1,\"5:55 (M5/M10 closed, commit 3bc2f97, pushed to origin)\\\n\\\n## KLMS Team — RESOLVED & OPEN items\\\n\\\n**RESOLVED — M5/M10 private key format (2026-08-14\"],[0,\"):**\"]],\"start1\":64,\"start2\":64,\"length1\":141,\"length2\":152},{\"diffs\":[[0,\"he KLMS \"],[-1,\"provide\"],[1,\"now deliver\"],[0,\"s field[\"]],\"start1\":218,\"start2\":218,\"length1\":23,\"length2\":27},{\"diffs\":[[0,\"field[8]\"],[-1,\" \"],[1,\"\\\n\"],[0,\"(DGI A00\"]],\"start1\":239,\"start2\":239,\"length1\":17,\"length2\":17},{\"diffs\":[[0,\"A003\"],[-1,\" private key) as 144 bytes of\\\n\"],[1,\") **card-ready**: 48-byte \"],[0,\"AES-128-\"],[1,\"E\"],[0,\"CB\"],[-1,\"C encrypted PKCS#8 DER (IV=0, S-DEK key, PKCS7 padding). The card expects 48 bytes\\\nof AES-128-ECB \"],[1,\" + ISO 9797-1 M2-\"],[0,\"encr\"]],\"start1\":253,\"start2\":253,\"length1\":146,\"length2\":62},{\"diffs\":[[0,\"e EC\"],[-1,\" \"],[1,\"\\\n\"],[0,\"scalar \"],[-1,\"(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), extract the 32-byte scalar via the shared `kf_source::extract_ec_scalar` helper,\\\nre-encrypt with the same S-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- heuristic `04 20` scalar detection (ambiguous for scal\"],[1,\"— the preferred option from the meeting. Station change (commit 3bc2f97):\\\n`worker.rs::card_ready_private_key` validates structure (48 bytes, `0x80||0*15` M2 tail)\\\nand forw\"],[0,\"ar\"],[1,\"d\"],[0,\"s \"],[-1,\"with leading 0x0420)\\\n\\\n**What to ask for (preferred → acceptable):**\\\n1. KLMS sends field[8] already card-ready: 48-byte AES-128-ECB + 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 scalar offset in `extra` (removes the `04 20` heuristic)\\\n\\\n**Also raise:**\\\n- `cryptoDataContainerId` response header is empty ('') — should carry the container UUID; we have `validate_cid` ready to enforce it once populated\\\n- Container signature: we have textbook-RSA `verify_signature` (Clypeum scheme) + pubkey PEM (`data/clypeum_signing_pubkey.pem`) — confirm the signing key so verification can be wired in\\\n- Container payload checksum: parsed but never validated — algorithm for header checksum-algorithm values 0x00-0x03 is unspecified; need the spec to wire verification\\\n- TLS: `Clypeum_Root_CA.pem` fails signature validation (BadSignature) → we run `danger_accept_invalid_certs(true)` — need a valid CA cert/chain (C1)\\\n- `report_usage`: align status values/payload/productSerial semantics; client method is ready\"],[1,\"the blob as-is with `encrypt_with_dek: false` — **zero local crypto**, the\\\nCBC-decrypt / `04 20` scalar-scan / re-encrypt workaround and its heuristics are deleted.\\\nPrivate key field now logs length only (no ciphertext in logs). `extract_ec_scalar`\\\nre-privatized in kf-source (file-container parser only). Release binary builds clean\\\n(`cargo build -p kf-dev-station --no-default-features --release`); deploy via kfs-prod.bat.\\\n**First tap after deploy = live verification of the new format.**\\\n\\\n**Still open (external — KLMS team):**\"],[0,\"\\\n\\\n| \"]],\"start1\":327,\"start2\":327,\"length1\":1960,\"length2\":723},{\"diffs\":[[0,\"n |\\\n\"],[-1,\"| M5/M10 | Private key format (see above) | Card-ready 48-byte ECB+M2 scalar preferred |\\\n\"],[0,\"| — \"]],\"start1\":1185,\"start2\":1185,\"length1\":97,\"length2\":8},{\"diffs\":[[0,\"ner UUID\"],[1,\" (`validate_cid` ready to enforce)\"],[0,\" |\\\n| — |\"]],\"start1\":1254,\"start2\":1254,\"length1\":16,\"length2\":50},{\"diffs\":[[0,\"dev KLMS\"],[1,\" (`verify_signature` ready to wire)\"],[0,\" |\\\n| — |\"]],\"start1\":1375,\"start2\":1375,\"length1\":16,\"length2\":51},{\"diffs\":[[0,\" | Spec \"],[1,\"for header values 0x00–0x03 \"],[0,\"so verif\"]],\"start1\":1467,\"start2\":1467,\"length1\":16,\"length2\":44},{\"diffs\":[[0,\"load\"],[-1,\" semantics\"],[1,\"/productSerial semantics (client method ready)\"],[0,\" |\\\n\\\n\"]],\"start1\":1584,\"start2\":1584,\"length1\":18,\"length2\":54},{\"diffs\":[[0,\"ELECTs (\"],[-1,\"now \"],[0,\"via `bui\"]],\"start1\":2194,\"start2\":2194,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"* |\\\n\"],[-1,\"\\\n## Audit round 5 (aabf7b6, 2026-08-14) — deferred polish CLOSED\\\n\\\nAll six deferred findings confirmed still present by fresh audit, then fixed:\\\n\\\n| # | Item | Resolution |\\\n|---|------|------------|\\\n| M6 | Scp03SetupError/ResetError/FactoryError clones of HandshakeError | Unified: `Scp03SetupError = Handshake(#[from], transparent) + CardRejected`; ResetError/FactoryError wrap `Setup(#[from])` + Transport/Scp03; fabricated 6A80 conversion deleted |\\\n| M7 | Host-challenge RNG ×3 | Single `handshake::random_host_challenge()`; used by provisioner, KLMS exchange, dev-station readers (32-byte GA challenge stays separate — different semantic) |\\\n| M11 | Mock \"],[1,\"| M6/M7/M11/M17'/L12/L15 | All deferred polish | **DONE** (aabf7b6, 2026-08-14 audit round 5) |\\\n\\\n## Audit round 5 (aabf7b6) — summary\\\n\\\nError-enum unification (HandshakeError transparent source); typed `StepStatus` progress on\\\nall worker→UI channels; mock-\"],[0,\"applet \"],[-1,\"C-MAC block ×4 | Extracted `verify_trailing_c_mac()` in kf-transport applet.rs |\\\n| M17' | Stringly-typed progress | New `progress::StepStatus { Started/Done/Failed(detail) }` on all worker→UI channels; `DONE_STATUSES` allowlist and string matching deleted |\\\n| L15 | 12 inline SELECT constructions | `kf_apdu::builders::select(Option<&[u8]>)` + `CLA_ISO`/`ins::SELECT`; all sites migrated |\\\n| L12 |\"],[1,\"`verify_trailing_c_mac` helper; `builders::select` for\\\n12 inline sites;\"],[0,\" kf-\"]],\"start1\":2660,\"start2\":2660,\"length1\":1068,\"length2\":341},{\"diffs\":[[0,\"lms \"],[-1,\"zero \"],[0,\"tests \"],[-1,\"| 3 tests: unknown OEF rejection, wrong cryptogram rejection, session-key/host-cryptogram consistency + cert population |\\\n\\\nAdditional fixes from the same audit:\\\n\\\n- **F\"],[1,\"(0→3); f\"],[0,\"ail-\"]],\"start1\":3002,\"start2\":3002,\"length1\":186,\"length2\":22},{\"diffs\":[[0,\"aths\"],[-1,\"**: `\"],[1,\" (\"],[0,\"run_\"]],\"start1\":3043,\"start2\":3043,\"length1\":13,\"length2\":10},{\"diffs\":[[0,\"hardware\"],[-1,\"`\"],[0,\" reconne\"]],\"start1\":3053,\"start2\":3053,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"nect\"],[-1,\" failure now fails the run (was silently skipping signature validation); `run_klms_provision_on_channel` validation error now sets success=false (was reporting success with error set); prod-panel tracker update keyed off the updated step instead of `last()` (SCP03-authenticate step no longer stuck IN PROGRESS)\\\n- **KLMS field extraction tag-keyed**: session keys, private key, plain-field map and logging now match `TlvTag` variants via `container_parser::field_by_tag` (reordered/extended containers can no longer silently mis-map); `KLMS_FIELD_NAMES` string table deleted\\\n- **EC-scalar dedup**: worker uses `kf_source::extract_ec_scalar` (now pub)\\\n- **Panic-safe workers**: catch_unwind in spawn_hardware/spawn_hw_applet_reset/spawn_validate/spawn_production; UI polls handle channel Disconnected → UI can no longer spin forever\\\n- **container_parser hardening**: RSA block_len<32 underflow guard; ASN.1 BIT STRING bs_len bounds check\\\n- **applet_reset pre-check**: GET STATUS errors reported with real reason (was masking transport errors as \\\"not supported\\\")\\\n- **HandshakeError::MalformedResponse now carries the parse message** (was discarding detail)\\\n- **Deps removed**: kf-dev-station: rustls, rustls-pemfile, webpki-roots, pkcs12, pcsc; kf-provision: zeroize, pcsc dev-dep, dup hex dev-dep. `Key24` deleted from kf-crypto\\\n- Hand-rolled audit JSON → serde_json::json!; doc typo length(20)→length(0x20)\\\n\\\n## Remaining open (ours, low priority)\\\n\\\n- Container payload checksum not validated (blocked on KLMS algorithm\"],[1,\",\\\nKLMS validation error, prod-panel tracker); tag-keyed KLMS field extraction\\\n(`field_by_tag`); panic-safe workers + Disconnected handling; container_parser underflow\\\nguards; unused-dep cleanup (rustls/webpki-roots/pkcs12/pcsc/zeroize); `Key24` removed.\\\n\\\n## Remaining open (ours)\\\n\\\n- HW smoke run of the KLMS flow (tap one fob after deploying 3bc2f97) — validates the\\\n  card-ready private key path end-to-end; the GA signature check in validation is the\\\n  integrity proof that the KLMS encrypted with the correct S-DEK\\\n\\\n## Workspace\\\n\\\n- Single canonical location: `D:\\\\Development\\\\Workspaces\\\\\rust-workspace\\\\keyfob-station`\\\n  (git, origin at ssh://192.168.1.2). The obsolete pre-repo scratch copy\\\n  `D:\\\\Development\\\\Workspaces\\\\\rusteworkspace` was deleted 2026-08-14 (contained one\\\n  outdated draft file,\"],[0,\" s\"],[1,\"u\"],[0,\"pe\"],[-1,\"c — moved to external table)\\\n- `danger_accept_invalid_certs(true)` stays until C1 CA fix lands\\\n\\\n## Final Audit S\"],[1,\"rseded in-repo; nothing unique lost).\\\n\\\n## Current s\"],[0,\"tate\"]],\"start1\":3067,\"start2\":3067,\"length1\":1641,\"length2\":862},{\"diffs\":[[0,\"mit \"],[-1,\"aabf7b6\"],[1,\"3bc2f97\"],[0,\")\\\n\\\n- \"],[-1,\"**\"],[0,\"All \"]],\"start1\":3934,\"start2\":3934,\"length1\":22,\"length2\":20},{\"diffs\":[[0,\"ings\"],[-1,\"** (full / test / minimal) — the pre-existing\\\n  minimal-variant `needless_return` in app.rs was also fixed\\\n- clippy clean (all variants)\"],[1,\"; clippy clean\"],[0,\", ru\"]],\"start1\":3993,\"start2\":3993,\"length1\":144,\"length2\":22},{\"diffs\":[[0,\"ean,\"],[-1,\" cargo\"],[0,\" doc\"]],\"start1\":4023,\"start2\":4023,\"length1\":14,\"length2\":8},{\"diffs\":[[0,\"lean\"],[-1,\", **99\"],[1,\"\\\n- 104\"],[0,\" tes\"]],\"start1\":4033,\"start2\":4033,\"length1\":14,\"length2\":14},{\"diffs\":[[0,\"pass\"],[-1,\"** (was 94)\\\n- KLMS flow unchanged behaviorally (field mapping is now tag-based; HW re-validation recommended\\\n \"],[1,\" (5 new for the card-ready private key path)\\\n- KLMS flow: field extraction tag-based; private key card-ready forwarding — needs the\\\n  one HW smoke tap above\"],[0,\" before \"],[1,\"the \"],[0,\"next\"]],\"start1\":4050,\"start2\":4050,\"length1\":126,\"length2\":176},{\"diffs\":[[0,\"ion \"],[-1,\"run)\"],[1,\"batch\"]],\"start1\":4234,\"start2\":4234,\"length1\":8,\"length2\":9}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-08-14T14:10:01.207Z
created_time: 2026-08-14T14:10:01.207Z
is_locked: 0
type_: 13