v3.11.0: Cards tab redesign (list + editor), preset PIN/PUK codes

The Cards tab now shows the preset list by default; "Add preset" / "Edit"
open a separate editor view (Save stores and returns to the list, Cancel
discards; ramGoToPreset opens it too).  The editor keeps the name/ICCID
header (with From card) and groups its fields into bordered blocks:
- PIN/ADM: the new optional card codes PIN1/PUK1/PIN2/PUK2 (plain text,
  4-8 decimal digits) plus ADM, moved out of the top row; nothing consumes
  the PIN/PUK codes yet.
- TAR: one uniform table (TAR, MSL, description); the first three rows are
  the fixed role entries with the immutable English descriptions "ISD
  compact format" / "UICC shared FS RFM compact format" / "ADF FS RFM
  compact format"; further rows are deletable with an optional description.
- SCP80 (keyset editor) and SCP81 (PSK pair) as before.

Store: PRESET_FIELDS gains pin1/puk1/pin2/puk2 (optional; whitespace
stripped; 4-8 decimal digits validated on save and import), SCHEMA_VERSION
4, docs/help/AGENTS updated.  TAR rows use unified ids (cards-tr-N-*), the
editor switches via cardsShowList/cardsShowEditor.

Tests: 665 Python + 783 frontend green; the whole inline PWA script
syntax-checked.  sw cache simple-v361.
This commit is contained in:
2026-10-02 09:32:45 +03:00
parent 5dd056beb8
commit 65c565e058
12 changed files with 451 additions and 188 deletions
+7 -7
View File
@@ -345,25 +345,25 @@
<p class="mb-2">Each packet the card accepts <strong>consumes a counter</strong>, which is saved into the preset's keyset &mdash; the probe is refused when the resolved SPI1 has no counter check or the named preset does not define the keyset number. Observed on a live card: <code class="font-mono text-sm">000000</code>, <code class="font-mono text-sm">B00000</code> and <code class="font-mono text-sm">B00001</code> registered (compact-format applications), the expanded-format TARs, USAT, multiplexing and CASD refused with <code class="font-mono text-sm">6200</code>.</p>
<h2 id="cards" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">4. Cards tab</h2>
<p class="mb-2">Stores card presets on the server (<code class="font-mono text-sm">~/.pysim-simple-server/card_presets.json</code>; <code class="font-mono text-sm">--card-presets</code> overrides it) so the Secured Packet view can auto-fill keys and parameters, the SCP81 HTTP OTA listener can look up PSK keys, and the SCP80 counters an operation consumed are persisted with the operation itself &mdash; a closed tab or a second browser window can no longer lose an increment. A preset carries <strong>several keysets</strong> &mdash; a keyset's number is the <code class="font-mono text-sm">b8..b5</code> nibble of its KIc/KID (TS 102 225 &sect;5.1.2/A.2): KIc and KID of a keyset must carry the same number (01&ndash;0F; A.2 rejects a mismatch), and each keyset has its own counter on the card (Annex A.1), so counters are tracked per keyset. In the form the keysets are edited as rows &mdash; one per number, with <strong>Add keyset</strong> / <strong>Remove keyset</strong>; each row shows the number derived from its KIc/KID byte, and <code class="font-mono text-sm">00</code> (no security) is not accepted as a keyset. The list is loaded when the PWA connects and can be exported/imported as JSON (the import also takes presets exported by older builds, flat or keyset-shaped). Cards is a top-level tab. When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in all three SCP80 views; when the equipped card matches <strong>no</strong> preset &mdash; or the selected preset belongs to another card &mdash; all three SCP80 views show an amber warning above the preset selector. The list highlights the equipped card's preset. The form groups the fields into two bordered blocks: <strong>SCP80 (GSM 03.48, ETSI TS 102 225)</strong> (the keysets, the TAR table &mdash; the three mandatory role entries ISD / UICC RFM / ADF RFM, each with its MSL, plus further TARs with an optional description &mdash; and the counters) and <strong>SCP81 (HTTP OTA)</strong> (the PSK identity/key pair with the key-selection note). The optional <strong>ADM</strong> field sits in the top row and is stored for later use by the file manager. The header shows gray <strong>SCP80</strong>/<strong>SCP81</strong> markers and a key glyph on the ADM badge when the preset matching the equipped card's ICCID has those settings filled in.</p>
<p class="mb-2">Stores card presets on the server (<code class="font-mono text-sm">~/.pysim-simple-server/card_presets.json</code>; <code class="font-mono text-sm">--card-presets</code> overrides it) so the Secured Packet view can auto-fill keys and parameters, the SCP81 HTTP OTA listener can look up PSK keys, and the SCP80 counters an operation consumed are persisted with the operation itself &mdash; a closed tab or a second browser window can no longer lose an increment. A preset carries <strong>several keysets</strong> &mdash; a keyset's number is the <code class="font-mono text-sm">b8..b5</code> nibble of its KIc/KID (TS 102 225 &sect;5.1.2/A.2): KIc and KID of a keyset must carry the same number (01&ndash;0F; A.2 rejects a mismatch), and each keyset has its own counter on the card (Annex A.1), so counters are tracked per keyset. In the form the keysets are edited as rows &mdash; one per number, with <strong>Add keyset</strong> / <strong>Remove keyset</strong>; each row shows the number derived from its KIc/KID byte, and <code class="font-mono text-sm">00</code> (no security) is not accepted as a keyset. The list is loaded when the PWA connects and can be exported/imported as JSON (the import also takes presets exported by older builds, flat or keyset-shaped). Cards is a top-level tab: it shows the <strong>preset list</strong> by default, and <strong>Add preset</strong> / <strong>Edit</strong> open the <strong>editor</strong> (a separate view) &mdash; <strong>Save</strong> stores the preset and returns to the list, <strong>Cancel</strong> discards the edits. When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in all three SCP80 views; when the equipped card matches <strong>no</strong> preset &mdash; or the selected preset belongs to another card &mdash; all three SCP80 views show an amber warning above the preset selector. The list highlights the equipped card's preset. The editor groups the fields into bordered blocks: <strong>PIN/ADM</strong> (the optional card codes &mdash; PIN1/PUK1/PIN2/PUK2 as 4&ndash;8 decimal digits and the ADM key &mdash; stored for later use), <strong>TAR</strong> (the TAR table), <strong>SCP80 (GSM 03.48, ETSI TS 102 225)</strong> (the keysets and their counters) and <strong>SCP81 (HTTP OTA)</strong> (the PSK identity/key pair with the key-selection note). The header shows gray <strong>SCP80</strong>/<strong>SCP81</strong> markers and a key glyph on the ADM badge when the preset matching the equipped card's ICCID has those settings filled in.</p>
<table class="w-full text-sm mb-3 border-collapse">
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Field</th><th class="text-left py-1 px-2">Description</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Name</td><td class="py-1 px-2">Human-readable label (required)</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ICCID</td><td class="py-1 px-2">Optional card identifier. <strong>From card</strong> fills it with the equipped card's EF.ICCID (enabled only while a readable ICCID is known); saving refuses an ICCID that already exists in the list (compared ignoring spaces and the raw-hex form, and the conflicting preset is named)</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ADM</td><td class="py-1 px-2">Optional administrator PIN (hex, or up to 8 ASCII digits), stored in the preset for later use by the file manager; not used by the SCP80/SCP81 views</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">PIN1 / PUK1 / PIN2 / PUK2 / ADM</td><td class="py-1 px-2">Optional card codes, all stored for later use (nothing consumes them yet). PIN/PUK must be 4&ndash;8 decimal digits (saving refuses anything else); ADM is the administrator key (hex, or up to 8 ASCII digits), used by the file manager. None is used by the SCP80/SCP81 views</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">KIc / KID</td><td class="py-1 px-2">Key and algorithm indicators (e.g. 15 = index 1, 3DES-CBC2; x2 = AES), required together with the keys</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">TARs / MSL</td><td class="py-1 px-2">The TAR table: the three role entries (ISD / UICC RFM / ADF RFM &mdash; mandatory, pre-settled) plus further TARs, each with its MSL (Minimum SPI1, TS 102 226 &sect;8.2.1.3.2.4). A packet's SPI1 is derived from its TAR's MSL; free entries carry an optional description</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ISD TAR</td><td class="py-1 px-2">Issuer Security Domain TAR (TS 101 220 Annex D); used for RAM/GP operations (SCP80 RAM view, RAM/GP chains); spec default <code class="font-mono text-sm">000000</code>, prefilled when empty</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">UICC RFM TAR</td><td class="py-1 px-2">UICC Shared File System RFM TAR (TS 102 226 &sect;7.2, TS 101 220 Annex D): the MF is implicitly selected and only files under the MF are accessible (no ADFs); used by the SIM RFM view; spec default <code class="font-mono text-sm">B00000</code></td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ADF RFM TAR</td><td class="py-1 px-2">ADF RFM TAR (TS 102 226 &sect;7.3, TS 101 220 Annex D): the ADF is implicitly selected and the TAR is linked to the application's AID &mdash; ADF.USIM in the USIM RFM view; spec default <code class="font-mono text-sm">B00001</code></td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">TAR / MSL / Description</td><td class="py-1 px-2">The TAR table: one uniform row per TAR with its MSL (Minimum SPI1, TS 102 226 &sect;8.2.1.3.2.4). The first three rows are the fixed role entries (pre-settled, not removable) with their fixed descriptions; further TARs are added with <strong>Add TAR</strong>, carry an optional description and can be removed. A packet's SPI1 is derived from its TAR's MSL</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ISD compact format</td><td class="py-1 px-2">Issuer Security Domain TAR (TS 101 220 Annex D); used for RAM/GP operations (SCP80 RAM view, RAM/GP chains); spec default <code class="font-mono text-sm">000000</code>, prefilled when empty</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">UICC shared FS RFM compact format</td><td class="py-1 px-2">UICC Shared File System RFM TAR (TS 102 226 &sect;7.2, TS 101 220 Annex D): the MF is implicitly selected and only files under the MF are accessible (no ADFs); used by the SIM RFM view; spec default <code class="font-mono text-sm">B00000</code></td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ADF FS RFM compact format</td><td class="py-1 px-2">ADF RFM TAR (TS 102 226 &sect;7.3, TS 101 220 Annex D): the ADF is implicitly selected and the TAR is linked to the application's AID &mdash; ADF.USIM in the USIM RFM view; spec default <code class="font-mono text-sm">B00001</code></td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Counter</td><td class="py-1 px-2">Replay counter (5 bytes); one per keyset, advanced and saved by the server after each operation that consumes it</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">KIc key / KID key</td><td class="py-1 px-2">16/24/32 hex chars (8/16/24-byte 3DES) or 32/48/64 hex chars (16/24/32-byte AES) keys</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">PSK identity</td><td class="py-1 px-2">SCP81 HTTP OTA: the identity the card sends in the TLS handshake (e.g. <code class="font-mono text-sm">89390100000129506903</code>)</td></tr>
<tr><td class="py-1 px-2">PSK key</td><td class="py-1 px-2">SCP81 HTTP OTA: 32 hex chars (16 bytes) for the PSK TLS listener, picked by the identity the card presents</td></tr>
</tbody>
</table>
<p class="text-sm mb-3">The <strong>SCP81</strong> column shows whether the preset supplies a usable PSK pair: <strong>&#10003;</strong> (green) — identity and key set; <strong>&#9888;</strong> (amber) — only one of the two, so the listener ignores the preset; <strong>&mdash;</strong> — no PSK. PSK identity and key must be set together. <strong>Edit</strong> loads a preset into the form (the Add button becomes <strong>Save</strong>; <strong>Cancel</strong> clears it) so fields can be changed without re-entering the card; <strong>Remove</strong> deletes the row. Presets can be shared with <strong>Export to file</strong> and restored with <strong>Import from file</strong>. The selected card preset auto-fills the Secured Packet form and sets the TAR for the operation (RAM/GP &rarr; ISD TAR, SIM RFM &rarr; UICC RFM TAR, USIM RFM &rarr; ADF RFM TAR); a TAR typed into the Secured Packet form by hand is kept when sending - the counter, keys and the TAR's MSL are still re-read from the preset; the SPI1 is seeded from the TAR's MSL and a lower value warns (the server returns it as a <code class="font-mono text-sm">warning</code> too); edits are pushed into a running SCP81 listener automatically.</p>
<p class="text-sm mb-3">The <strong>SCP81</strong> column shows whether the preset supplies a usable PSK pair: <strong>&#10003;</strong> (green) — identity and key set; <strong>&#9888;</strong> (amber) — only one of the two, so the listener ignores the preset; <strong>&mdash;</strong> — no PSK. PSK identity and key must be set together. <strong>Add preset</strong> opens the editor with a fresh preset; <strong>Edit</strong> loads the row's preset into it so fields can be changed without re-entering the card; <strong>Save</strong> stores it and returns to the list, <strong>Cancel</strong> discards the edits; <strong>Remove</strong> deletes the row (list only). Presets can be shared with <strong>Export to file</strong> and restored with <strong>Import from file</strong>. The selected card preset auto-fills the Secured Packet form and sets the TAR for the operation (RAM/GP &rarr; ISD TAR, SIM RFM &rarr; UICC RFM TAR, USIM RFM &rarr; ADF RFM TAR); a TAR typed into the Secured Packet form by hand is kept when sending - the counter, keys and the TAR's MSL are still re-read from the preset; the SPI1 is seeded from the TAR's MSL and a lower value warns (the server returns it as a <code class="font-mono text-sm">warning</code> too); edits are pushed into a running SCP81 listener automatically.</p>
</section>
<section class="mb-10">