__main__.py:
- Probe smartcard.System.readers() before init_reader when no reader
was explicitly specified
- Retry 3 times with 2s delay to handle late pcscd startup and
USB enumeration delays
start.sh:
- Also match pcscd.bin process name (some distros)
- Wait 1s for pcscd if not running yet
- Moved Execute button to the right of the Operation dropdown (same row)
- Removed standalone Execute button that was on its own row
- Added LANG_RU translations: Card preset, Operation, Explore Card, Install Package
Docs:
- SCP80 tab is now a parent section (3) with subsections 3.1/3.2/3.3
- Renumbered sections: Response parser→4, Card reader→5, Server→6, Version compat→7
- Applied to help.html, help-ru.html, README.md, README_RUS.md
C-APDU/RAM form labels (GP + JavaCard terminology):
- AID → Application / Instance AID
- ELF AID → Load File AID / Package AID
- Module AID → Executable Module AID / Applet Class AID
SCP80/RAM explorer labels (GP + JavaCard terminology):
- ISD → ISD (Issuer Security Domain)
- Applications → Applications / Applet Instances
- Executable Load Files → Executable Load Files (ELFs) / Packages
- AID → Application/Instance AID or Load File AID/Package AID (context-dependent)
- Module AIDs → Executable Module AIDs / Applet Class AIDs
UX fixes:
- Delete All button now matches Delete button style (red)
- Explorer results cleared when switching away from Explore Card operation
- KIc/KID dropdowns (index + algorithm) now update when selecting saved card
- Added LANG_RU translations for new labels
- README/README_RUS: RAM tab now describes Explore Card + Install Package operations,
explorer view with inline Delete/Delete All buttons for Applications and ELFs
- docs/api.md: add full /api/ram-install endpoint documentation
- index.html: remove Delete from dropdown, add ramDeleteFromExplorer() with confirm dialog,
add Delete buttons in explorer for Apps and ELFs (ELFs get cascade option)
Incoming: _parse_sms_concat parses UDH for IEI 0x00 (8-bit ref) and
IEI 0x08 (16-bit ref). PoRSubmitHandler accumulates segments, sorts by
num, and reassembles complete payload when all parts arrive.
Outgoing: MAX_ENVELOPE_SEGMENTS=5 limits the secured packet to 5
ENVELOPE chunks (650B max at 130B/chunk).
10 new tests for concat parsing and reassembly.
Key fix: re-add chained C0000000 (GET RESPONSE Le=00) in paginate() so the
full GP APDU inside SCP80 is 80F2<p1>p2024F0000C0000000, matching the
working tool. The card's SCP80 layer executes both commands internally
(GET STATUS → 61XX → GET RESPONSE) and puts the final 9000 + data in
the PoR.
Also: server logging improvements (RAM RESPONSE-PACKET label, no truncation
of FETCH/PoR hex), docs for /api/ram-install endpoint, minor test fix.
_send_status sent F2 00 00 -> the card echoes the FCP of the currently
selected DF on every poll/one-shot STATUS. Phones use P2=0C (no
response expected) to avoid that overhead. Keep P1=00 and the SIM/UICC
P3 distinction (SIM=0x23, UICC=0x00):
CLA F2 00 0C P3
Affects one-time /api/status-poll and the automatic polling timer.
Version 1.9.11 -> 1.9.12 everywhere; SW cache otaman-v22 -> otaman-v23
cardsRender() was mutation-only (add/import/remove/counter-sync), so
after a reload the SCP80 -> Cards table showed 'No cards defined.'
even while localStorage held presets and the SP dropdown listed them
correctly. Now:
- cardsLoad() renders the table right after rebuilding the select
- entering the Cards pill re-renders (mirrors pysim-pill pattern)
Version 1.9.10 -> 1.9.11 everywhere; SW cache otaman-v21 -> otaman-v22
- SPI1/SPI2 side by side in one row
- KIc/KID byte selectors side by side in one row
- KIc/KID key textareas moved directly under the KIc/KID selectors
- Card preset selector moved to the top of the form, full width
- Padding byte joins TAR + Counter in one three-column row
- Pure HTML reflow: no id/handler changes, all behavior preserved
(invalidation, presets sync, por_ok counter bump)
Version 1.9.9 -> 1.9.10 everywhere; SW cache otaman-v20 -> otaman-v21
Secured Packet and Cards pages move into a new SCP80 top-level tab
(positioned next to C-APDU), switched by rounded-full pills - both
features are SCP80 concerns and more SCP80 operations are planned.
- nav: 'sp'/'cards' buttons replaced by single data-tab=scp80 button
- new #tab-scp80 with scp80-subtab pills and re-parented page wrappers
(#scp80-sub-sp / #scp80-sub-cards); all element IDs preserved so
genSp/verify/send-to-card/CNTR-sync/preset flows are untouched
- switchTab(): sp/cards branches replaced by scp80 -> help anchor
- new scp80SwitchSubtab(): pill styling, wrapper visibility, per-pill
help anchor (secured-packet | cards)
- packToSp() hands off via switchTab('scp80') + Secured packet pill
Version 1.9.8 -> 1.9.9 everywhere; SW cache otaman-v19 -> otaman-v20
When a secured packet is accepted (por_ok) and a card is selected in
the SP-page card dropdown, the new normalized counter is written back
into that preset (cards[].cntr), persisted via cardsSave() and
reflected in the presets table via cardsRender(). Placeholder option
and unselected state are skipped. Form bump + preset sync happen in
the same okPor block, so form and stored preset always climb together.
Version 1.9.7 -> 1.9.8 everywhere; SW cache otaman-v18 -> otaman-v19
pysimSendOta wrote its progress/verdict into #pysim-status - the shared
header line owned by connect/equip/poll logic - clobbering the card
status shown under 'Equip card'. OTA outcomes now live exclusively on
the Secured Packet page:
- send start: small gray 'Sending OTA...' in #sp-send-result
- verdicts: big #sp-por-status + details line, as before
- header #pysim-status untouched by OTA flows
Version housekeeping: v1.9.6 -> 1.9.7 everywhere; SW cache
otaman-v17 -> otaman-v18. Also realigns pyproject.toml which the
multi-amend rounds had left stale at 1.9.5.
OTA inner-APDU SWs from SIM-domain applications surfaced as 'Unknown
status word' (e.g. 9404 on a failed SELECT inside an authenticated
B00000 packet). SW_MAP.generic gains the complete TS 51.011 §9.4
families, so they decode in every resp-cmd context:
- 9200/9240 memory management (retry / memory problem)
- 9400/9402/9404/9408 referencing management (no EF selected,
out of range, file ID/pattern not found, file inconsistent)
- 9802/9804/9808/9810/9840/9850 security management (CHV/access-
condition/invalidation contradictions and blocked states; 9850
also added to generic alongside its gp/uicc copy)
- 9EXX/9FXX data-download error length / response length wildcards
Exact keys safely coexist with the 92XX proactive wildcard (exact
match wins). TS 102 221 sweep found no further stragglers: 63C0-C9
PIN retry counters are covered by the existing 63CX wildcard.
Secured packet page: enforce fresh packet per send
- any change to APDU/TAR/CNTR/keys/padding/SPI now clears the secured
packet textarea via spInvalidate(), so Send cannot reuse a stale
packet ('No secured packet to send' guards the path)
- on por_ok the CNTR field auto-increments (10-digit hex normalized,
wraps modulo 2^40) and the textarea clears; security-error verdicts
leave both untouched for retry after fixing the cause
SIM/USIM SELECT: P1/P2 per spec + live RFM idioms
- USIM FID selects requested no response data (P2=0C) - that coding is
reserved for the select-MF-by-empty-data special case; ordinary FID
selects now request the FCP template (P2=04) with Le='00' per
TS 102 221 Table 11.2 / pySim sel_ctrl convention; preset tables
fixed in both genSimUsim and updateP1P2Display
- path method gains base selector: from MF (P1=08) / from current DF
(P1=09) - reproduces the dominant live RFM idiom 09/0C
- 'silent' checkbox on fid/path/chain emits P2=0C without Le for hops
where the FCI is not needed
- chain syntax gains GET RESPONSE hops: 'C0' emits CLA C0 00 00 00 and
'C0:NN' sets explicit Le, enabling classic SELECT -> 9FXX ->
GET RESPONSE pairs in a single secured packet so the PoR carries the
actual FCI/response bytes instead of a bare length SW
- SW dictionary: 9FXX reworded to point at GET RESPONSE
- placeholders show the new chain syntax
- findings & backport decisions written to ~/WSL/RFM_notes.md
response_map.test.js: +8 assertions across the new families.
sim.test.js: USIM FID expectation updated; silent/base/chain cases.
apdu_parse.test.js: live capture lines 1-3 as regression fixtures.
Version 1.9.5 -> 1.9.6 everywhere; SW cache otaman-v16 -> otaman-v17
pySim encode_cmd transmits the 2-byte CPL only when ciphering is
applied; unciphered packets start at CHL. genSp always emitted it,
so every SPI1=0x00 packet diverged from the pySim reference at byte 0
(sp-verify MISMATCH) and carried a length octet pair real cards need
not expect.
- genSp output: bytesToHex(ciphering ? packet : packet.subarray(2));
MAC input unchanged (still covers the virtual-CPL frame, matching
pySim's sign-then-strip convention)
- Secured packet page: PoR verdict now rendered prominently in a
dedicated text-base semibold line ('PoR: por_ok' green / other
statuses red) above the small detail line; hidden when no PoR was
requested or the ENVELOPE failed
- sp.test.js: three unciphered expectations updated to CHL-first form;
ciphered vectors untouched (byte-identical)
- version 1.9.4 -> 1.9.5 everywhere; SW cache otaman-v15 -> otaman-v16
- pysimSendOta: Response Parser filled strictly from decoded PoR
(por.decoded) for both SPI2 variants; envelope SW/failures no longer
leak into it — they render in a new inline result line (#sp-send-result)
next to Send to Card, always including SW on failure
- Default-level stderr tracing per send (no flags needed):
'OTA SEND: SPI .. KIc .. KID .. TAR .. CNTR .. LEN ..B CHUNKS N',
'OTA SEND FAILED: chunk N SW xxxx' and
'OTA PoR[envelope|sms-submit]: status=.. TAR=.. CNTR=.. PCNTR=..
RPL=.. RHL=..' (+ compact summary / undecodable raw / none fallbacks)
- _decode_por: surface every parsed PoR field verbatim (cntr, rpl, rhl,
cc_rc, raw) instead of status/tar/pcntr only
- genSp: CNTR normalization padEnd -> padStart so short input like '1'
becomes 0000000001, not 1000000000 (counter is big-endian 5 bytes)
- tests: TestDecodePor completeness + cntr_low field-report cases
Version 1.9.3 -> 1.9.4 everywhere; SW cache otaman-v14 -> otaman-v15
Both sections (card presets, custom files) gain two import paths and a
file download alongside the existing clipboard import:
- Import from file: hidden <input type=file accept=.json> + FileReader
feeding the unchanged dedupe/merge logic (works on plain-HTTP LAN
origins where navigator.clipboard is unavailable)
- Paste & import: reuses the IO textarea; guard requires '[' prefix so
status messages are never mis-imported; empty state reveals + focuses
the field with a hint placeholder
- Export to file: Blob download (otaman-cards.json /
otaman-custom-files.json) via shared downloadJson() helper
- Import functions take optional text arg; clipboard stays the fallback
- data-l10n on all five buttons per section; LANG_RU entries for the
three new labels
Version 1.9.2 -> 1.9.3 everywhere; SW cache otaman-v13 -> otaman-v14
SIM/USIM encoder (registry §2.3):
- New PIN commands: DISABLE '26' / ENABLE '28' (single FF-padded PIN,
Lc=08) and UNBLOCK '2C' (unblock+new, Lc=10); dynamic field labels
- ACTIVATE/DEACTIVATE FILE target selection: Current EF (case 1),
by FID (P1=00), path from MF (P1=08), path from current DF (P1=09)
with Lc+FID/path data form
- LANG_RU entries for new labels
Tests:
- sim.test.js: new stub-DOM genSimUsim harness; exact-hex for all PIN
ops, ACTIVATE/DEACTIVATE forms, SELECT Le rules, RECORD P1=00 rule
- response_map.test.js: lookupSw wildcards (91XX / 63CX), LIFECYCLE_MAP
per GPC v2.3, PRIVILEGE_NAMES vs Tables 11-7/11-8/11-9
GET STATUS E3 template decoding verified already present
(TLV_TAG_NAMES + decodeTlvValue cover 4F/9F70/C5/CF/C4/CC/CE/84).
Version 1.9.1 -> 1.9.2, SW cache otaman-v12 -> otaman-v13
Parser labels (§8.3, verified against GPC v2.3 / TS 102 221 PDFs):
- describeP1P2: SELECT P1/P2 (FID/DF-name/path-MF/path-DF; FCP/no-data),
READ/UPDATE RECORD modes (next/previous/absolute + SFI, P1-ignored note),
GET STATUS P1 (ISD/Apps/ELF/ELF+Modules) and P2 formats,
INSTALL P1 bit-aware roles ('for install + for make selectable'),
SET STATUS P1 (80 ISD / 40 App-or-SSD / 60 SD+associated) with
card states vs lock/unlock P2, VERIFY/CHANGE PIN ref
Parser structure (§8.4):
- describeInstallDataLv: exact-sum LV walker for all 5 INSTALL layouts
with privilege bit names and params tag nesting (C9, EF->CA, EA->80);
falls back to legacy TLV view when lengths do not sum exactly
- GET DATA: case-2 (P3=Le) vs case-4 (Lc + tag list + Le)
- SET STATUS data: 'ignored for ISD' / raw AID / legacy 4F-TLV labeled
- Trailing single byte consumed as Le at end of compact chain
- ACTIVATE/DEACTIVATE: case-1 (4 bytes), legacy empty-Lc, FID/path forms
- Expanded script C-APDU rows now decode into structured APDU nodes
- Compact matcher accepts CLA 84-87 (GP secure messaging)
Version 1.9.0 -> 1.9.1, SW cache otaman-v10 -> otaman-v11
Server:
- Track all TERMINAL RESPONSEs (chain, TP, pySim auto-handler, menu-respond
link to paused entry) with shared _build_tr/_record_tr
- Log entry ids + cmd_num; server-side decode of fetched commands and TRs
(compact PLI port); Result CTLV extracted as tr_result/tr_result_name
- _LoggingApduTracer captures TR SW for pySim auto-handler responses
- _DefaultProactiveHandler answers PLI with editor data (no re-request)
- VERSION 1.8.0
Frontend:
- Expandable proactive log rows (click to expand, state survives re-render)
- Response section: Result name + hex, decoded fields, width-restrained
readonly hex inputs; no SW display
- Rename Expanded Script tab/pill/button/docs; proactive UICC view font
sizing increase; card reader pills text-sm