Keyfob Station — PlantUML Diagram Sources

# Keyfob Station — PlantUML Diagram Sources

> Master copy of every PlantUML diagram for the key-fob provisioning architecture.
> **This note is the only copy** — a `docs/diagrams/` folder was never committed to the
> repo. Rendered SVG/PNG were generated ad hoc (PPT/Word embeds use the SVG).
> Companions: **Keyfob Flow 1**, **Keyfob Flow 2** (sequence detail), audit note
> (**Keyfob Station — Code Audit Open Items**).

> **Review status 2026-08-14** — verified against the code (kf-scp03 derive.rs,
> kf-provision perso.rs/helpers.rs, kf-source container.rs, nfc_reader.rs):
> - **Phase A–D KDF diagrams: correct as drawn.** Purposes 0x40/0x60/0x70, header
>   `00^11‖p‖00‖0080‖01`, K1-for-full / K2-for-partial CMAC blocks, ECB call counts
>   (7+8+5+3 = 23), Phase D CBC chaining — all match code + tests.
> - **Phase D (A003 = AES-CBC, IV=0, M2) is CONFIRMED by hardware**: the card accepts
>   CBC and rejects the KLMS's ECB blob with `6A 80`. The dev KLMS currently emits
>   ECB+M2; the station converts locally until they switch to CBC (open item).
> - Flow 2 diagrams updated: REST transport (was DLL), seed-based on-demand generation
>   (was Flow-1 cache/NoPackage), INIT UPDATE response layout fixed (UID is read via
>   GET DATA beforehand, not returned by INITIALIZE UPDATE), step 9 marked PENDING.

## Diagrams

| File | Shows |
| --- | --- |
| `kdf-overview.puml` | KDF phase overview — two-row flow (both rows start left; A003 feeds Phase D) |
| `kdf-phase-a.puml` | Phase A full (master+UID → static keys) |
| `kdf-phase-a-1.puml` | Phase A 1/2 (subkeys + S-ENC) — slide |
| `kdf-phase-a-2.puml` | Phase A 2/2 (S-MAC + S-DEK) — slide |
| `kdf-phase-b.puml` | Phase B full (session keys) |
| `kdf-phase-b-1.puml` | Phase B 1/2 (subkeys + SES-ENC) — slide |
| `kdf-phase-b-2.puml` | Phase B 2/2 (SES-MAC + SES-RMAC) — slide |
| `kdf-phase-c.puml` | Phase C — cryptograms (card gate + host cg) |
| `kdf-phase-d.puml` | Phase D — A003 AES-CBC + cleanup |
| `flow-2-provisioning.puml` | Full end-to-end (Word full-page) |
| `flow-2-provisioning-1.puml` | Flow 2a — Session establishment (slide, landscape) |
| `flow-2-provisioning-2.puml` | Flow 2b — Personalize & finalize (slide) |

## Rendering

```sh
# local (needs java + plantuml.jar)
plantuml -tpng -tsvg <file>.puml

# headless, no java — public server via deflate encoder
# https://www.plantuml.com/plantuml/{png,svg}/<encoded>
```

For PPT/Word embed the **SVG** (scalable).

* * *

## kdf-overview.puml — Phase overview (two-row flow)

```plantuml
@startuml kdf-overview
' KLMS <-> NetHSM KDF: phase overview (per fob). Two-row flow for PPT.
' Both rows start on the LEFT; end of row1 wraps to start of row2.
' A003 (OEM private key) feeds Phase D, not Phase A.

skinparam monochrome false
skinparam defaultFontName "DejaVu Sans"
skinparam ArrowColor #444444
skinparam rectangle {
  BackgroundColor #F4F8FF
  BorderColor #3B6FB6
}
skinparam card {
  BackgroundColor #FFFFFF
  BorderColor #3B6FB6
}

title KLMS <-> NetHSM KDF — Phase Overview (per fob transaction)

rectangle "**Inputs from station (from fob)**\n- UID\n- host_challenge\n- card_challenge\n- card_cryptogram" as InStation #E8F5E9
rectangle "**Inputs from OEM key feed**\n- Private Key for Key Fob (A003)" as InOEM #E8F5E9

card "**Phase A — KDF3**\nkey = master **M**\n7 x AES-ECB" as pA
card "**Phase B — Session keys**\nkey = **SE / SM**\n8 x AES-ECB" as pB
card "**Phase C — Cryptograms**\nkey = **SESMAC**\n5 x AES-ECB" as pC
card "**Phase D — A003 (private key) CBC + cleanup**\nkey = **SD**\n3 x AES-ECB" as pD
rectangle "**Outputs to station**\nSES-* keys - host_cryptogram\n- A003 ciphertext (pre-encrypted)" as Out #FFF8E1

' ---- ROW 1: inputs -> Phase A -> Phase B ----
InStation -down-> pA
pA -right-> pB : static keys\n(SE / SM / SD)
note bottom of pA
  master + UID -> S-ENC / S-MAC / S-DEK
  imported as handles SE / SM / SD
end note
note bottom of pB
  SE / SM -> SES-ENC / SES-MAC / SES-RMAC
  SES-MAC imported as handle SESMAC
end note

' ---- wrap: anchor row2 under row1's columns (C under A, D under B); arrow from end of row1 ----
pA -[hidden]down-> pC
pB -[hidden]down-> pD
pB -down-> pC : SES-MAC\n(SESMAC)

' ---- ROW 2: Phase C -> Phase D -> outputs ----
pC -right-> pD : authenticated + host_cryptogram\nsecure channel trusted -> proceed
InOEM -down-> pD : A003 private key (SD)\nIV = 0, AES-CBC
pD -right-> Out : pre-encrypted A003
note bottom of pC
  VERIFY card_cryptogram -> GATE
  then host_cryptogram
end note
note bottom of pD
  AES-CBC(S-DEK) -> A003 ciphertext
  Zeroize all static & session key material
end note

note top of pA #FFEEEE
  NetHSM exposes only single-block AES-ECB.
  Master key **M** never exposed.
  ~= 23 AES-ECB calls per transaction.
end note
note right of pD #FFEEEE
  HW-confirmed 2026-08-14: card accepts
  CBC(IV=0)+M2 and rejects ECB (6A80).
  Dev KLMS currently emits ECB+M2;
  station converts until KLMS fixes.
end note
@enduml
```

* * *

## kdf-phase-a.puml — Phase A: KDF3 (full, Word page)

```plantuml
@startuml
' Phase A - KDF3: master + UID -> S-ENC/S-MAC/S-DEK via AES-ECB (RFC 4493 CMAC).
' Constants validated against kf-scp03 derive.rs (jcshell_reference_vector test).

autonumber
title Phase A — KDF3 (key = master M) -> static keys (S-ENC/S-MAC/S-DEK)

participant KLMS
participant "NetHSM\nMaster Key M" as HSM

== Master Subkeys (RFC 4493) ==
KLMS -> HSM : ECB(M, 0^16)
HSM --> KLMS : L_M
KLMS -> KLMS : K1_M = dbl(L_M)
KLMS -> KLMS : K2_M = dbl(K1_M)
note left of KLMS : **dbl** is the doubling that CMAC uses to derive its subkeys K1 and K2.\nFor a 16‑byte block X: dbl(X) = (X << 1)  XOR  (Rb if MSB(X)=1, else 0)\nwith Rb = 00^15 87 (i.e. 15 zero bytes + 0x87).\n**In plain terms**: shift the 128‑bit value left by one bit;\nif the top bit that was shifted out was a 1, XOR 0x87 into the last byte.

== S-ENC (purpose 0x40, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 40 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-ENC**
KLMS -> HSM : ImportKey(S-ENC)
HSM --> KLMS : handle **SE**

== S-MAC (purpose 0x60, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 60 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-MAC**
KLMS -> HSM : ImportKey(S-MAC)
HSM --> KLMS : handle **SM**

== S-DEK (purpose 0x70, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 70 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-DEK**
KLMS -> HSM : ImportKey(S-DEK)
HSM --> KLMS : handle **SD**

@enduml
```

* * *

## kdf-phase-a-1.puml — Phase A (1/2): subkeys + S-ENC (slide)

```plantuml
@startuml kdf-phase-a-1
' Phase A (1/2): master subkeys + S-ENC. Continues in kdf-phase-a-2.puml.
' Splits the portrait Phase A into two landscape halves for PPT.
autonumber
title Phase A — KDF3 (1/2): master subkeys + S-ENC
participant KLMS
participant "NetHSM\nMaster Key M" as HSM

== Master Subkeys (RFC 4493) ==
KLMS -> HSM : ECB(M, 0^16)
HSM --> KLMS : L_M
KLMS -> KLMS : K1_M = dbl(L_M)
KLMS -> KLMS : K2_M = dbl(K1_M)
note left of KLMS : **dbl** is the doubling that CMAC uses to derive its subkeys K1 and K2.\nFor a 16‑byte block X: dbl(X) = (X << 1)  XOR  (Rb if MSB(X)=1, else 0)\nwith Rb = 00^15 87 (i.e. 15 zero bytes + 0x87).\n**In plain terms**: shift the 128‑bit value left by one bit;\nif the top bit that was shifted out was a 1, XOR 0x87 into the last byte.

== S-ENC (purpose 0x40, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 40 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-ENC**
KLMS -> HSM : ImportKey(S-ENC)
HSM --> KLMS : handle **SE**
@enduml
```

* * *

## kdf-phase-a-2.puml — Phase A (2/2): S-MAC + S-DEK (slide)

```plantuml
@startuml kdf-phase-a-2
' Phase A (2/2): S-MAC + S-DEK. Continues from kdf-phase-a-1.puml.
autonumber
title Phase A — KDF3 (2/2): S-MAC + S-DEK
participant KLMS
participant "NetHSM\nMaster Key M" as HSM
note left of KLMS : (continues) K1_M / K2_M already derived from L_M (see 1/2).

== S-MAC (purpose 0x60, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 60 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-MAC**
KLMS -> HSM : ImportKey(S-MAC)
HSM --> KLMS : handle **SM**

== S-DEK (purpose 0x70, 10-B UID from station) ==
KLMS -> KLMS : header = 00^11 ‖ 70 (purpose) ‖ 00 ‖ 0080 ‖ 01
KLMS -> HSM : ECB(M, header)
HSM --> KLMS : C1
KLMS -> HSM : ECB(M, C1 ⊕ pad(UID, 16-B) ⊕ K2_M)
HSM --> KLMS : C2 = **S-DEK**
KLMS -> HSM : ImportKey(S-DEK)
HSM --> KLMS : handle **SD**
@enduml
```

* * *

## kdf-phase-b.puml — Phase B: session keys (full, Word page)

```plantuml
@startuml kdf-phase-b
' Phase B - session keys: S-ENC/S-MAC -> SES-ENC/MAC/RMAC via AES-ECB CMAC.
autonumber
title Phase B — Session keys (key = SE / SM) -> SES-*
participant KLMS
participant "NetHSM" as HSM
== session subkeys (RFC 4493) ==
KLMS -> HSM : ECB(SE, 0^16)
HSM --> KLMS : L_SE
KLMS -> KLMS : K1_SE = dbl(L_SE)
KLMS -> KLMS : K2_SE = dbl(K1_SE)
KLMS -> HSM : ECB(SM, 0^16)
HSM --> KLMS : L_SM
KLMS -> KLMS : K1_SM = dbl(L_SM)
KLMS -> KLMS : K2_SM = dbl(K1_SM)
note left of KLMS : session deriv data = 32 B = block1 ‖ block2\n(usage ‖ host_challenge ‖ card_challenge ‖ …)\nboth blocks full (16 B) -> last block XOR **K1**\n(K2 is used only for a *partial* last block, as in Phase A)
== SES-ENC (usage 0x04) ==
KLMS -> HSM : ECB(SE, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SE, C1 ⊕ block2 ⊕ K1_SE)
HSM --> KLMS : C2 = **SES-ENC**  (-> station)
== SES-MAC (usage 0x06) ==
KLMS -> HSM : ECB(SM, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SM, C1 ⊕ block2 ⊕ K1_SM)
HSM --> KLMS : C2 = **SES-MAC**  (-> station)
KLMS -> HSM : ImportKey(SES-MAC)
HSM --> KLMS : handle **SESMAC**
== SES-RMAC (usage 0x07, reuse K1_SM) ==
KLMS -> HSM : ECB(SM, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SM, C1 ⊕ block2 ⊕ K1_SM)
HSM --> KLMS : C2 = **SES-RMAC**  (-> station)
@enduml
```

* * *

## kdf-phase-b-1.puml — Phase B (1/2): subkeys + SES-ENC (slide)

```plantuml
@startuml kdf-phase-b-1
' Phase B (1/2): session subkeys + SES-ENC. Continues in kdf-phase-b-2.puml.
autonumber
title Phase B — Session keys (1/2): subkeys + SES-ENC
participant KLMS
participant "NetHSM" as HSM

== session subkeys (RFC 4493) ==
KLMS -> HSM : ECB(SE, 0^16)
HSM --> KLMS : L_SE
KLMS -> KLMS : K1_SE = dbl(L_SE)
KLMS -> KLMS : K2_SE = dbl(K1_SE)
KLMS -> HSM : ECB(SM, 0^16)
HSM --> KLMS : L_SM
KLMS -> KLMS : K1_SM = dbl(L_SM)
KLMS -> KLMS : K2_SM = dbl(K1_SM)
note left of KLMS : session deriv data = 32 B = block1 ‖ block2\n(usage ‖ host_challenge ‖ card_challenge ‖ …)\nboth blocks full (16 B) -> last block XOR **K1**\n(K2 is used only for a *partial* last block, as in Phase A)

== SES-ENC (usage 0x04) ==
KLMS -> HSM : ECB(SE, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SE, C1 ⊕ block2 ⊕ K1_SE)
HSM --> KLMS : C2 = **SES-ENC**  (-> station)
@enduml
```

* * *

## kdf-phase-b-2.puml — Phase B (2/2): SES-MAC + SES-RMAC (slide)

```plantuml
@startuml kdf-phase-b-2
' Phase B (2/2): SES-MAC + SES-RMAC. Continues from kdf-phase-b-1.puml.
autonumber
title Phase B — Session keys (2/2): SES-MAC + SES-RMAC
participant KLMS
participant "NetHSM" as HSM
note left of KLMS : (continues) K1_SM / K2_SM already derived from L_SM (see 1/2).

== SES-MAC (usage 0x06) ==
KLMS -> HSM : ECB(SM, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SM, C1 ⊕ block2 ⊕ K1_SM)
HSM --> KLMS : C2 = **SES-MAC**  (-> station)
KLMS -> HSM : ImportKey(SES-MAC)
HSM --> KLMS : handle **SESMAC**

== SES-RMAC (usage 0x07, reuse K1_SM) ==
KLMS -> HSM : ECB(SM, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SM, C1 ⊕ block2 ⊕ K1_SM)
HSM --> KLMS : C2 = **SES-RMAC**  (-> station)
@enduml
```

* * *

## kdf-phase-c.puml — Phase C: cryptograms

```plantuml
@startuml kdf-phase-c
' Phase C - cryptograms (key = SESMAC): verify card_cryptogram (gate) + host_cryptogram.
autonumber
title Phase C — Cryptograms (key = SESMAC)
participant KLMS
participant "NetHSM" as HSM
== cryptogram subkeys (RFC 4493) ==
KLMS -> HSM : ECB(SESMAC, 0^16)
HSM --> KLMS : L
KLMS -> KLMS : K1 = dbl(L)
KLMS -> KLMS : K2 = dbl(K1)
note left of KLMS : cryptogram deriv data = 32 B = block1 ‖ block2\n(const ‖ host_challenge ‖ card_challenge ‖ …)\nboth blocks full (16 B) -> last block XOR **K1**\ncryptogram = CMAC tag truncated to 8 B\n(crates/kf-scp03/src/derive.rs)
== card cryptogram verify (const 0x00) — GATE ==
KLMS -> HSM : ECB(SESMAC, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SESMAC, C1 ⊕ block2 ⊕ K1)
HSM --> KLMS : C2
KLMS -> KLMS : card_crypto = C2[0:8]
KLMS -> KLMS : compare card_crypto == fob **card_cryptogram**
alt match
  note right of KLMS #DFFFD6 : authenticated = true -> proceed
else mismatch
  KLMS --> Station : auth error\n(no keys / container issued)
end
== host cryptogram (const 0x01, reuse K1/K2) ==
KLMS -> HSM : ECB(SESMAC, block1)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SESMAC, C1 ⊕ block2 ⊕ K1)
HSM --> KLMS : C2
KLMS -> KLMS : host_crypto = C2[0:8] = **host_cryptogram**  (-> station)
@enduml
```

* * *

## kdf-phase-d.puml — Phase D: A003 CBC + cleanup

```plantuml
@startuml kdf-phase-d
' Phase D - A003 AES-CBC (key = SD, IV=0, ISO 9797-1 M2 pad) + handle cleanup.
' HW-CONFIRMED 2026-08-14: card accepts CBC(IV=0)+M2; ECB is rejected (6A80).
autonumber
title Phase D — A003 AES-CBC (key = SD) + cleanup
participant KLMS
participant "NetHSM" as HSM
note left of KLMS : A003 encrypted with **S-DEK (SD)** — a static per-fob key,\nindependent of the session. IV = 0; host chains AES-CBC from\nsingle-block ECB (ISO 9797-1 Method 2 padding). SD never sent to station.\n**Dev status**: KLMS currently emits ECB+M2 instead; the station\nre-encrypts to CBC locally until the KLMS switches (open item).
== A003 encryption (AES-CBC, IV = 0) ==
KLMS -> KLMS : pad 32-B scalar -> 48 B (ISO 9797-1 Method 2)
KLMS -> HSM : ECB(SD, P1 ⊕ IV)
HSM --> KLMS : C1
KLMS -> HSM : ECB(SD, P2 ⊕ C1)
HSM --> KLMS : C2
KLMS -> HSM : ECB(SD, P3 ⊕ C2)
HSM --> KLMS : C3
KLMS -> KLMS : A003 = C1 ‖ C2 ‖ C3  (-> station, pre-encrypted)
== Cleanup — destroy ephemeral handles ==
KLMS -> HSM : Destroy(SE)
KLMS -> HSM : Destroy(SM)
KLMS -> HSM : Destroy(SD)
KLMS -> HSM : Destroy(SESMAC)
KLMS -> KLMS : zeroize all derived keys (RAM)
@enduml
```

* * *

## flow-2-provisioning.puml — full end-to-end (Word page)

```plantuml
@startuml flow-2-provisioning
' Flow 2 - Provisioning: Key Fob <-> Station <-> KLMS (-> NetHSM)
' Updated 2026-08-14: REST transport, seed-based generation, INIT UPDATE layout.
' KDF detail: kdf-phase-a/b/c/d.puml
title Flow 2 — Provisioning: Key Fob <-> Station <-> KLMS
actor       "Key Fob\n(NCJ37x)"   as Fob
participant Station               as Station
participant KLMS                  as KLMS
participant "NetHSM\n(master key)" as HSM
== 0. Identify (station <-> fob) ==
Station -> Fob : SELECT ISD\n00 A4 04 00 || A000000151000000
Station -> Fob : GET DATA (OEF ID + UID)
Fob --> Station : OEF ID (B252) + UID\n(10-B JCOP UID; 7-B chip UID fallback)
== 1. SCP03 initial handshake (station <-> fob) ==
Station -> Fob : INITIALIZE UPDATE\n80 50 <KVN> 00 08 || host_challenge[8]
Fob --> Station : key_div_data[10] ‖ key_version ‖ scp_id ‖ i ‖\ncard_challenge[8] ‖ card_cryptogram[8] ‖ seq_counter[3]\nnote right: UID is NOT in this response (read in step 0)
== 2. Container request (REST over mTLS; seed from POST /seeds at connect) ==
Station -> KLMS : POST /cryptoContainers\nform{ typeID, seedUUID, productSerial=KF-<chip UID>,\n  extra{ transaction_id, station_id,\n  fob{oef, uid, key_version},\n  scp03{host_challenge, card_challenge,\n        sequence_counter, card_cryptogram},\n  keyBundleType } }
alt HTTP 5xx / error
  KLMS --> Station : error (HTTP 500 etc.)\nstation retries once without touching the card
end
== 3. KLMS derives keys via NetHSM (AES-ECB only; detail: kdf-phase-a/b/c/d.puml) ==
note right of KLMS : ~23 AES-ECB + 4 Import + 4 Destroy\n(master subkeys cacheable across transactions)
group Phase A — KDF3 (key = M) : 7 ECB
  KLMS -> HSM : 7 x ECB(M) -> S-ENC / S-MAC / S-DEK
  KLMS -> HSM : ImportKey(S-ENC/MAC/DEK)
  HSM --> KLMS : handles SE / SM / SD
end
group Phase B — session keys (key = SE / SM) : 8 ECB
  KLMS -> HSM : 8 x ECB -> SES-ENC / SES-MAC / SES-RMAC   (-> station)
  KLMS -> HSM : ImportKey(SES-MAC)
  HSM --> KLMS : handle SESMAC
end
group Phase C — cryptograms (key = SESMAC) : 5 ECB — GATE
  KLMS -> HSM : ECB(SESMAC) -> card_cryptogram
  KLMS -> KLMS : verify == fob card_cryptogram
  KLMS -> HSM : ECB(SESMAC) -> host_cryptogram   (-> station)
end
group Phase D — A003 AES-CBC (key = SD) : 3 ECB
  KLMS -> HSM : 3 x ECB(SD) -> A003 ciphertext (CBC, IV=0, M2)
  HSM --> KLMS : A003 ciphertext (pre-encrypted)
end
== 4. Container back to station (binary Clypeum container, wrapper-key encrypted) ==
KLMS --> Station : container (13 TLV fields)\n{ UID, FESN, SPID, Authenticated,\n  S-ENC, S-MAC, S-RMAC, Host Cryptogram,\n  Private Key (A003 pre-encrypted),\n  Fob Pub Cert, IRK, ICA Cert, CMS Root Cert }
Station -> Station : parse + decrypt (wrapper key from seed)\nverify card cryptogram locally (safety gate)
== 5. Open secure channel (C-MAC) ==
Station -> Fob : EXTERNAL AUTHENTICATE\n84 82 <level> 00 || host_cryptogram || MAC[8]
Fob --> Station : 9000   (channel open)
== 6. Personalize — STORE DATA loop ==
loop over DGI blocks (<= 245 B each, each C-MAC'd)
  Station -> Fob : STORE DATA\n84 E2 <P1> <P2> || DGI payload || MAC[8]
  Fob --> Station : 9000
end
== 7. Lifecycle transition ==
Station -> Fob : STORE DATA (last)\n84 E2 80 <P2> 00 || MAC[8]
note over Fob : UNPERSONALIZED -> FACTORY
Fob --> Station : 9000
== 8. Post-personalization (raw, no secure messaging) ==
Station -> Fob : 00 DB 00 00 03 0A 01 01   (enable UICC)
Station -> Fob : 00 DB 00 00 03 0B 01 01   (enable BLE)
Fob --> Station : 9000
== 9. Result + audit — PENDING ==
Station -> KLMS : provision result (report_usage)\n{ transaction_id, fesn, result,\n  stage_reached, apdu_count, timestamp }\nnote right #FFEEEE : NOT YET WIRED - awaiting KLMS\nstatus-value semantics (open item);\nlikely also populates productSerial\nin the KLMS admin
Station -> Station : zeroize session keys
note over Station #E8FFE8
  Station holds ONLY: ephemeral session keys
  + pre-encrypted A003 (both zeroized after the transaction).
  Never: master key, static keys, S-DEK.
end note
note over HSM #FFEEEE : master key never leaves the NetHSM
@enduml
```

* * *

## flow-2-provisioning-1.puml — Flow 2a: Session establishment (slide)

```plantuml
@startuml flow-2-provisioning-1
' Flow 2a - Provisioning (1/2): session establishment (slide-fit).
' Updated 2026-08-14: REST transport, seed-based generation, INIT UPDATE layout.
' Compact KDF block here; full per-phase detail in kdf-phase-a/b/c/d.puml
' and the full-page flow-2-provisioning.puml.
' Continues in flow-2-provisioning-2.puml (personalize & finalize).
title Flow 2a — Provisioning (1/2): Session establishment
actor       "Key Fob\n(NCJ37x)"   as Fob
participant Station               as Station
participant KLMS                  as KLMS
participant "NetHSM\n(master key)" as HSM
== 0. Identify (station <-> fob) ==
Station -> Fob : SELECT ISD + GET DATA
Fob --> Station : OEF ID (B252) + UID\n(10-B JCOP UID; 7-B chip UID fallback)
== 1. SCP03 initial handshake (station <-> fob) ==
Station -> Fob : INITIALIZE UPDATE\n80 50 <KVN> 00 08 || host_challenge[8]
Fob --> Station : key_div_data[10] ‖ key_version ‖ scp_id ‖ i ‖\ncard_challenge[8] ‖ card_cryptogram[8] ‖ seq_counter[3]
== 2. Container request (REST over mTLS; seed from POST /seeds at connect) ==
Station -> KLMS : POST /cryptoContainers\nform{ typeID, seedUUID, productSerial, extra{...} }
alt HTTP 5xx / error
  KLMS --> Station : error — station retries once without touching the card
end
== 3. KLMS derives keys via NetHSM (AES-ECB only; detail: kdf-phase-a/b/c/d.puml) ==
note right of KLMS : ~23 AES-ECB + 4 Import + 4 Destroy per transaction\n(master subkeys cacheable across transactions)
KLMS -> HSM : KDF3 (M + UID) -> S-ENC / S-MAC / S-DEK   (Phase A)
KLMS -> HSM : session KDF -> SES-ENC / SES-MAC / SES-RMAC   (Phase B)
KLMS -> HSM : verify card_cryptogram (SESMAC) -> authenticated   (Phase C, GATE)
KLMS -> HSM : host_cryptogram   (Phase C)
KLMS -> HSM : AES-CBC(SD) -> A003 ciphertext (pre-encrypted)   (Phase D)
note right of HSM #FFEEEE : master key M never leaves the NetHSM
== 4. Container back to station (binary Clypeum container) ==
KLMS --> Station : container (13 TLV fields)\n{ UID, FESN, SPID, Authenticated, S-ENC, S-MAC,\n  S-RMAC, Host Cryptogram, Private Key (A003\n  pre-encrypted), Fob Pub Cert, IRK, ICA, CMS Root }
Station -> Station : parse + decrypt (wrapper key from seed)\nverify card cryptogram locally (safety gate)
note over Station #E8FFE8
  Station now holds ONLY: ephemeral session keys
  + pre-encrypted A003 (both zeroized after the transaction).
  Never: master key, static keys, S-DEK.
end note
@enduml
```

* * *

## flow-2-provisioning-2.puml — Flow 2b: Personalize & finalize (slide)

```plantuml
@startuml flow-2-provisioning-2
' Flow 2b - Provisioning (2/2): personalize & finalize.
' Continues from flow-2-provisioning-1.puml (session established).
title Flow 2b — Provisioning (2/2): Personalize & finalize
actor       "Key Fob\n(NCJ37x)"   as Fob
participant Station               as Station
participant KLMS                  as KLMS
note over Station : (continues from Flow 2a: holds session keys + pre-encrypted A003)
== 5. Open secure channel (C-MAC) ==
Station -> Fob : EXTERNAL AUTHENTICATE\n84 82 <level> 00 || host_cryptogram || MAC[8]
Fob --> Station : 9000   (channel open)
== 6. Personalize — STORE DATA loop ==
loop over DGI blocks (<= 245 B each, each C-MAC'd)
  Station -> Fob : STORE DATA\n84 E2 <P1> <P2> || DGI payload || MAC[8]
  Fob --> Station : 9000
end
== 7. Lifecycle transition ==
Station -> Fob : STORE DATA (last)\n84 E2 80 <P2> 00 || MAC[8]
note over Fob : UNPERSONALIZED -> FACTORY
Fob --> Station : 9000
== 8. Post-personalization (raw, no secure messaging) ==
Station -> Fob : 00 DB 00 00 03 0A 01 01   (enable UICC)
Station -> Fob : 00 DB 00 00 03 0B 01 01   (enable BLE)
Fob --> Station : 9000
== 9. Result + audit — PENDING ==
Station -> KLMS : provision result (report_usage)\n{ transaction_id, fesn, result,\n  stage_reached, apdu_count, timestamp }
note right #FFEEEE : NOT YET WIRED - awaiting KLMS\nstatus-value semantics (open item)
Station -> Station : zeroize session keys
note over Station #E8FFE8
  Station held ONLY: ephemeral session keys
  + pre-encrypted A003 (both zeroized after the transaction).
  Never: master key, static keys, S-DEK.
end note
@enduml
```

* * *

id: 1fbaa70f2fb94ffcaabbeca7b39482da
parent_id: 61ba8f290b7b4faf9199916387419665
created_time: 2026-06-30T11:39:29.304Z
updated_time: 2026-08-14T17:19:48.706Z
is_conflict: 0
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: 1782819569304
user_created_time: 2026-06-30T11:39:29.304Z
user_updated_time: 2026-08-14T17:19:48.706Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
is_locked: 0
extracted_resource_ids: 
type_: 1