id: 3e4aeea9fc154d38847949de899c440c
parent_id: b90f06b3b05449fdb9ad031b7ce57a2b
item_type: 1
item_id: 1afe8b0b762043189ace149c4fc388b7
item_updated_time: 1781538354655
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"n rework\"],[1,\" → second review pass\"],[0,\").\\\n\\\n> Co\"]],\"start1\":147,\"start2\":147,\"length1\":16,\"length2\":37},{\"diffs\":[[0,\"ed** in \"],[-1,\"the \"],[0,\"review p\"]],\"start1\":323,\"start2\":323,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"iew pass\"],[1,\" 1\"],[0,\"; **2 re\"]],\"start1\":334,\"start2\":334,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\" by \"],[-1,\"the \"],[0,\"JCShell\"],[-1,\"-driven\"],[0,\" rew\"]],\"start1\":360,\"start2\":360,\"length1\":26,\"length2\":15},{\"diffs\":[[0,\"noted.\\\n\\\n\"],[1,\"Second review pass (post-JCShell rework) found **3 CRITICAL** + **7 WARNING** + several **SUGGESTION** items. All CRITICAL and WARNING items fixed. Remaining items are dead-code cleanup or deferred to real-card.\\\n\\\n\"],[0,\"**Verifi\"]],\"start1\":431,\"start2\":431,\"length1\":16,\"length2\":229},{\"diffs\":[[0,\":** \"],[-1,\"63\"],[1,\"**64\"],[0,\" tes\"]],\"start1\":671,\"start2\":671,\"length1\":10,\"length2\":12},{\"diffs\":[[0,\"sts pass\"],[1,\"**\"],[0,\", `clipp\"]],\"start1\":682,\"start2\":682,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"ngInput)\"],[1,\" + `StaticCertificates`\"],[0,\" |\\\n\\\n### \"]],\"start1\":2024,\"start2\":2024,\"length1\":16,\"length2\":39},{\"diffs\":[[0,\".der\"],[-1,\" |\\\n\\\n### New Data Types\\\n\\\n```rust\\\n// kf-source/src/types.rs\\\npub struct PersonalizationItem {\\\n    pub dgi: Vec<u8>,           // 2-byte DGI tag\\\n    pub data: Vec<u8>,          // raw payload\\\n    pub encrypt_with_dek: bool, // S-DEK pre-encrypt before wrapping\\\n}\\\n\\\npub struct RawApdu {            // non-SM commands (post-perso)\\\n    pub cla: u8, pub ins: u8, pub p1: u8, pub p2: u8, pub data: Vec<u8>,\\\n}\\\n```\\\n\\\n`ProvisioningInput` now carries `personalization_items: Vec<PersonalizationItem>` + `post_perso_commands: Vec<RawApdu>` instead of the old `payload_apdus` + `lock_isd`.\"],[1,\"; `StaticCertificates` for ICA/CMS certs |\"],[0,\"\\\n\\\n##\"]],\"start1\":2389,\"start2\":2389,\"length1\":580,\"length2\":50},{\"diffs\":[[0,\"\\\n## \"],[-1,\"Fixed in Initial\"],[1,\"Second\"],[0,\" Rev\"]],\"start1\":2917,\"start2\":2917,\"length1\":24,\"length2\":14},{\"diffs\":[[0,\"ew Pass \"],[1,\"Fixes \"],[0,\"(2026-06\"]],\"start1\":2932,\"start2\":2932,\"length1\":16,\"length2\":22},{\"diffs\":[[0,\"6-15\"],[1,\" rev 3\"],[0,\")\\\n\\\n### \"],[-1,\"B1 — Audit timestamp was not ISO-8601 ✅ FIXED (HIGH)\\\n**Was:** `now_iso8601(\"],[1,\"CRITICAL — All Fixed\\\n\\\n| # | Issue | Fix |\\\n|---|---|---|\\\n| C1 | **DGI tag indexing panic** — `encode_dgi` indexed `dgi[0]`/`dgi[1]` without length check → could panic on malformed input | Added `ProvisionError::InvalidInput` validation: DGI must be exactly 2 bytes, data non-empty |\\\n| C2 | **Empty `personalization_items` silent success** — pipeline would proceed through SCP03 and \\\"succeed\\\" with zero DGIs written | Rejected in `Provisioner::run()` before opening SCP03 channel |\\\n| C3 | **Audit TX entries leaked secrets** — `audit.rs` stored full STORE DATA command bytes including IRK, SPID, private key | TX entries now redact data body (header only: CLA/INS/P1/P2/Lc); only metadata retained |\\\n\\\n### WARNING — All Fixed\\\n\\\n| # | Issue | Fix |\\\n|---|---|---|\\\n| W1 | **Applet auth bypass on empty-data STORE DATA** — empty-data shortcut bypassed C-MAC verification, allowing unauthenticated lifecycle transition | Removed shortcut; general `P1 & 0x80` branch handles it after MAC verification |\\\n| W2 | **`max_block_data` dead config** — `ProvisionerConfig` had the field but `split_into_blocks` used hardcoded 245 | Threaded config value through to `split_into_blocks` |\\\n| W3 | **SPID parse fragility** — `split('-').nth(1\"],[0,\")` \"],[1,\"b\"],[0,\"re\"],[-1,\"turned `@<unix_seconds>` instead of ISO-8601.\\\n\"],[1,\"aks if FESN contains a dash | Switched to `rsplit('-').next()` for dash-in-FESN robustness |\\\n| W4 | **PersonalizationItem.data not zeroized** — plain `Vec<u8>` holding private key scalar | Accepted as known limitation; data flows from `ProvisioningSource` which owns the lifecycle |\\\n\\\n### SUGGESTION — Remaining (Low Priority)\\\n\\\n| # | Issue | Status |\\\n|---|---|---|\\\n| S1 | `handle_set_status()` + `ins::SET_STATUS` — orphaned dead code from pre-rework | Safe to delete; no callers |\\\n| S2 | `lifecycle()` / `is_factory()` accessors — unused | Wire into test assertions or remove |\\\n| S3 | `PersonalizationItem.data` not zeroized | Accept or wrap in `ZeroizingVec` later |\\\n\\\n---\\\n\\\n## Fixed in Initial Review Pass (2026-06-15 rev 1)\\\n\\\n### B1 — Audit timestamp was not ISO-8601 ✅ FIXED (HIGH)\"],[0,\"\\\n**F\"]],\"start1\":2953,\"start2\":2953,\"length1\":141,\"length2\":2029},{\"diffs\":[[0,\"second]Z`.\\\n\\\n\"],[-1,\"---\\\n\\\n\"],[0,\"### B5 — App\"]],\"start1\":5088,\"start2\":5088,\"length1\":29,\"length2\":24},{\"diffs\":[[0,\"o::ct_eq`.\\\n\\\n\"],[-1,\"---\\\n\\\n\"],[0,\"### B6 — `Pr\"]],\"start1\":5206,\"start2\":5206,\"length1\":29,\"length2\":24},{\"diffs\":[[0,\"nError>)`.\\\n\\\n\"],[-1,\"---\\\n\\\n\"],[0,\"### B7 — Hos\"]],\"start1\":5345,\"start2\":5345,\"length1\":29,\"length2\":24},{\"diffs\":[[0,\"ropy`.\\\n\\\n\"],[-1,\"---\\\n\\\n\"],[0,\"### B8–B\"]],\"start1\":5495,\"start2\":5495,\"length1\":21,\"length2\":16},{\"diffs\":[[0,\"VED\\\n\"],[-1,\"**Was:** The old code used `SET STATUS` (INS=0xF0, P1=0x0F P2=0x00) to lock the ISD. The P1/P2 values needed confirmation.\\\n\\\n**Resolution:** \"],[0,\"The \"]],\"start1\":5812,\"start2\":5812,\"length1\":148,\"length2\":8},{\"diffs\":[[0,\"**. \"],[-1,\"Instead, the l\"],[1,\"L\"],[0,\"ifec\"]],\"start1\":5875,\"start2\":5875,\"length1\":22,\"length2\":9},{\"diffs\":[[0,\"1=0x80`.\"],[-1,\" The\"],[0,\" `lock_i\"]],\"start1\":5950,\"start2\":5950,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"d()`\"],[-1,\" builder has been\"],[0,\" rep\"]],\"start1\":5967,\"start2\":5967,\"length1\":25,\"length2\":8},{\"diffs\":[[0,\")`. \"],[-1,\"The s\"],[1,\"S\"],[0,\"imul\"]],\"start1\":6010,\"start2\":6010,\"length1\":13,\"length2\":9},{\"diffs\":[[0,\"let \"],[-1,\"now \"],[0,\"uses \"],[-1,\"a \"],[0,\"`Lif\"]],\"start1\":6027,\"start2\":6027,\"length1\":19,\"length2\":13},{\"diffs\":[[0,\"ry`)\"],[-1,\" instead of a boolean `locked` flag.\\\n\\\n---\"],[1,\".\"],[0,\"\\\n\\\n##\"]],\"start1\":6079,\"start2\":6079,\"length1\":49,\"length2\":9},{\"diffs\":[[0,\"OOT\\\n\"],[-1,\"**Was:** The mock derived the decryption counter from `received_payloads.len() + 1`, which could diverge from the host on empty-body commands.\\\n\\\n**Resolution:** \"],[0,\"With\"]],\"start1\":6155,\"start2\":6155,\"length1\":168,\"length2\":8},{\"diffs\":[[0,\"nly \"],[-1,\"security level \"],[0,\"(no \"]],\"start1\":6171,\"start2\":6171,\"length1\":23,\"length2\":8},{\"diffs\":[[0,\" is \"],[-1,\"**\"],[0,\"no data \"],[1,\"d\"],[0,\"e\"],[-1,\"n\"],[0,\"cryption\"],[-1,\"**\"],[0,\" on \"]],\"start1\":6192,\"start2\":6192,\"length1\":30,\"length2\":26},{\"diffs\":[[0,\"DATA\"],[-1,\" commands. The applet no longer attempts decryption unless `c_dec_active` is true (set from EXTERNAL AUTHENTICATE P1)\"],[0,\". Th\"]],\"start1\":6224,\"start2\":6224,\"length1\":125,\"length2\":8},{\"diffs\":[[0,\"able\"],[-1,\" in the real flow\"],[0,\".\\\n\\\n-\"]],\"start1\":6256,\"start2\":6256,\"length1\":25,\"length2\":8},{\"diffs\":[[0,\"Deferred\"],[1,\" (Real-Card)\"],[0,\"\\\n\\\n### B2\"]],\"start1\":6277,\"start2\":6277,\"length1\":16,\"length2\":28},{\"diffs\":[[0,\"s`\\\n\\\n\"],[-1,\"**Issue:** For response encryp\"],[1,\"The `iv[0] |= 0x80` muta\"],[0,\"tion \"],[-1,\"(\"],[1,\"for \"],[0,\"R-ENC\"],[-1,\"), the IV is derived then mutated `iv[0] |= 0x80`. Per\"],[1,\" IVs may not match\"],[0,\" GP \"]],\"start1\":6403,\"start2\":6403,\"length1\":103,\"length2\":64},{\"diffs\":[[0,\"P SCP03 \"],[-1,\"(\"],[0,\"Amendmen\"]],\"start1\":6465,\"start2\":6465,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"§6.2\"],[-1,\"), C-DEC and R-ENC IVs are computed identically. This path is o\"],[1,\". O\"],[0,\"nly \"]],\"start1\":6485,\"start2\":6485,\"length1\":71,\"length2\":11},{\"diffs\":[[0,\"ULL`\"],[-1,\" (R-ENC)\"],[0,\", wh\"]],\"start1\":6527,\"start2\":6527,\"length1\":16,\"length2\":8},{\"diffs\":[[0,\"ault is \"],[-1,\"now \"],[0,\"`C_MAC` \"]],\"start1\":6573,\"start2\":6573,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"hen \"],[-1,\"a real-card captu\"],[1,\"hardwa\"],[0,\"re i\"]],\"start1\":6672,\"start2\":6672,\"length1\":25,\"length2\":14},{\"diffs\":[[0,\"lable.\\\n\\\n\"],[-1,\"---\\\n\\\n\"],[0,\"### B4 —\"]],\"start1\":6692,\"start2\":6692,\"length1\":21,\"length2\":16},{\"diffs\":[[0,\"s`\\\n\\\n\"],[-1,\"**Issue:** \"],[0,\"Sess\"]],\"start1\":6811,\"start2\":6811,\"length1\":19,\"length2\":8},{\"diffs\":[[0,\"rams\"],[-1,\" in `TEST_VECTOR`\"],[0,\" are\"]],\"start1\":6835,\"start2\":6835,\"length1\":25,\"length2\":8},{\"diffs\":[[0,\"orm \"],[-1,\"\\\"\"],[0,\"SCP03 \"],[-1,\"T\"],[1,\"t\"],[0,\"est \"],[-1,\"V\"],[1,\"v\"],[0,\"ectors\"],[-1,\"\\\" literals\"],[0,\" whe\"]],\"start1\":7004,\"start2\":7004,\"length1\":37,\"length2\":26},{\"diffs\":[[0,\"le. \"],[-1,\"Also d\"],[1,\"D\"],[0,\"e-ri\"]],\"start1\":7039,\"start2\":7039,\"length1\":14,\"length2\":9},{\"diffs\":[[0,\"rd with \"],[-1,\"the \"],[0,\"known st\"]],\"start1\":7086,\"start2\":7086,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"keys\"],[-1,\" from the JCShell script\"],[0,\".\\\n\\\n-\"]],\"start1\":7107,\"start2\":7107,\"length1\":32,\"length2\":8},{\"diffs\":[[0,\"ions\"],[-1,\" from JCShell Analysis\"],[0,\"\\\n\\\n1. \"],[1,\"~~\"],[0,\"**IC\"]],\"start1\":7132,\"start2\":7132,\"length1\":35,\"length2\":15},{\"diffs\":[[0,\"5)**\"],[1,\"~~\"],[0,\" — \"],[-1,\"Present in the JCShell script but NOT in the per-fob container directory. Where do these come from? Likely factory-wide constants (same for all fobs) or delivered by the DLL/SO content provider.\\\n2. **SPID derivation** — The directory name encodes `<FESN>-<SPID_HEX>`. The SPID hash is\"],[1,\"**Resolved**: factory-wide static certs, loaded from shared dir (`ica_cert.der`, `cms_root_cert.der`). Verified byte-identical across both fixtures.\\\n2. **SPID derivation** —\"],[0,\" NOT \"],[-1,\"a \"],[0,\"SHA-\"]],\"start1\":7180,\"start2\":7180,\"length1\":302,\"length2\":191},{\"diffs\":[[0,\"HA-1 of \"],[-1,\"the \"],[0,\"FESN (ve\"]],\"start1\":7368,\"start2\":7368,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"fied\"],[-1,\": `SHA1(\\\"1KM0001E\\\") ≠ \\\"59918C...\\\"`). It must c\"],[1,\"). C\"],[0,\"ome\"],[1,\"s\"],[0,\" from \"],[-1,\"an \"],[0,\"exte\"]],\"start1\":7386,\"start2\":7386,\"length1\":66,\"length2\":22},{\"diffs\":[[0,\"KLMS or \"],[-1,\"the \"],[0,\"containe\"]],\"start1\":7421,\"start2\":7421,\"length1\":20,\"length2\":16},{\"diffs\":[[0,\"very\"],[-1,\" system)\"],[1,\"). Directory name: `<FESN>-<SPID_HEX>`\"],[0,\".\\\n3.\"]],\"start1\":7443,\"start2\":7443,\"length1\":16,\"length2\":46},{\"diffs\":[[0,\"255** — \"],[-1,\"The r\"],[1,\"R\"],[0,\"eference\"]],\"start1\":7504,\"start2\":7504,\"length1\":21,\"length2\":17},{\"diffs\":[[0,\"KVN=\"],[-1,\"255 (\"],[0,\"0xFF\"],[-1,\")\"],[0,\". Pr\"]],\"start1\":7534,\"start2\":7534,\"length1\":18,\"length2\":12},{\"diffs\":[[0,\"ion \"],[-1,\"fobs may use a different value\"],[1,\"may differ\"],[0,\". Co\"]],\"start1\":7551,\"start2\":7551,\"length1\":38,\"length2\":18},{\"diffs\":[[0,\"* — \"],[-1,\"Are containers delivered p\"],[1,\"P\"],[0,\"er-f\"]],\"start1\":7620,\"start2\":7620,\"length1\":34,\"length2\":9},{\"diffs\":[[0,\" network\"],[-1,\",\"],[0,\" or batc\"]],\"start1\":7635,\"start2\":7635,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"ects\"],[-1,\" the `FileContainerSource`\"],[0,\" dir\"]],\"start1\":7671,\"start2\":7671,\"length1\":34,\"length2\":8},{\"diffs\":[[0,\"rev \"],[-1,\"2 with JCShell-driven rework findings\"],[1,\"3: second review pass complete, all CRITICAL/WARNING fixed, 64 tests green\"],[0,\".*\"]],\"start1\":7744,\"start2\":7744,\"length1\":43,\"length2\":80}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-15T15:46:56.959Z
created_time: 2026-06-15T15:46:56.959Z
type_: 13