Rules now persist the pySim symbolic name captured during 'Profile from
card' (profilerBuildFileRule stores c.name || sel.name), and the profile
edit view renders it next to the Path label using the same lookup order
as the check view: profilerCustomNameForPath(path) || rule.name.
Path edits call the new profilerUpdateRulePath, which drops the stale
scan-time name and refreshes the label from the custom-files dictionary
live, so the displayed name always corresponds to the current path.
Manually added rules get name: null (resolved via custom-file lookup).
Old profiles without rule.name still work.
4 new tests (rule.name from child/select/null, custom name lookup, path
edit clears name + refreshes label). SW cache v51 -> v52.
The 'Profile from card' ignore checkboxes matched only by FID, but the
list had EF.KcGPRS at 4F52 (TS 31.102 DF.GSM-ACCESS) while the live card
exposes it at 6F52 (TS 51.011 DF.GSM, verified in TS 51.011 v4.15.0
10.3.32 and the DF.GSM allocation table). The miss made profilerBuildFileRule
capture full contents (Exact) for a file the user had checked to ignore.
- Correct EF.KcGPRS FID to 6F52 (spec-verified)
- Match ignores by FID OR by pySim name: each checkbox now carries
data-ignore-name, profilerScanStart builds an ignoreNames set, and
profilerBuildFileRule checks both. Covers FID variants (6F52 vs 4F52)
and future constant typos; ignoreNames is optional for back-compat.
- 6 new tests: ignore-list FID/name sanity (incl. KcGPRS=6F52), duplicates,
ignored-by-FID, ignored-by-name-only regression case, non-ignored control,
back-compat when ignoreNames is omitted.
SW cache v50 -> v51.
- C-APDU chain rows: delete button moved after the hex field (matches
Expanded Script layout)
- Use ✕ uniformly across delete icons (was × in chain rows)
SW cache v49.
- Regular chain add buttons: faint blue tint -> solid blue (bg-blue-600)
- '+ GET RESPONSE' (SIM/USIM/RAM): gray/blue -> emerald to stand out
(also fixes RAM GET RESPONSE which was styled inconsistently)
SW cache v48.
- Seed one empty record row when record content is created or reset
(filetype change or Contents None->Exact/Mask) via profilerEmptyRecordContent
- Add per-record delete and '+ Record' add controls so the record view is
always editable; delete is hidden on the last remaining row
SW cache v47.
- Do not store fileSize on record-file rules during 'Profile from card'
- Do not check/report fileSize mismatch for record-file rules; only
recordLen and numRecords are reported for LF/CY files
SW cache v46.
- Show Size only for transparent/ber_tlv; Record length/count only for
linear_fixed/cyclic; hide all size/record fields for df (unknown shows all)
- Hide the Contents section entirely for df (existence-only check)
- Changing file type clears now-inapplicable fields and resets content
(keeps mode) when its kind switches between record and transparent
- profilerSetContent defaults content kind from the selected file type
Add profilerFileFields + profilerContentKindForFileType helpers + tests.
SW cache v45.
When generating a ruleset from the card, capture EF.IMSI (6F07) and
EF.ICCID (2FE2) contents as a mask matching only the first 4 bytes
(e.g. 08290591??????????), the rest are '?' wildcards.
Add profilerMaskPrefix4 helper + PROFILER_MASK_PREFIX4_FIDS map + tests.
SW cache v44.
When a declared fileSize/recordLen/numRecords attribute mismatches, compare
file contents only over the overlapping (shorter) portion, so a length
difference alone doesn't fail the content check when the common bytes match.
When sizes match, contents are compared fully as before.
Add profilerMatchMin helper + tests. SW cache v43.
Redefine .text-xs to render at 0.875rem/1.25rem (text-sm), covering the PWA
and both help pages via the shared stylesheet. Explicit leading-* overrides
still apply.
SW cache v41.
- Light theme: darken gray-400/500/600 muted text one step
- Dark theme: lighten slate-400/500 muted text one step
- Dark theme: lighten red-500/600/700 text (was too dark on dark bg)
SW cache v40.
- Capture the pySim file name (sel.name) during each check
- Resolve custom-file aliases from pysimCustomFiles (frontend dict)
- Render 'EF.ADN (MF/7F10/6F3A)' in the results report
SW cache v39.
New 'Profiler' sub-tab in card-reader view:
- Named rulesets (profiles) persisted in localStorage; each has an ordered
list of filesystem rules (type 'file', extensible to OTA/TAR checks later)
- List page: New profile / Profile from card / Import profile + per-profile
Edit / Check / Export / Delete
- Editor page: inline rule fields (path, file type, size, record length,
record count, contents with exact/mask/'?'-wildcard modes)
- Results page: sequential rule checks with live progress and a pass/fail
report (existence + FCI attributes + content match)
- 'Profile from card' scans the equipped card; creates a rule only for files
that exist (FCI present). A scan-options dialog lets the user skip contents
of frequently-overwritten dynamic files (LOCI/PSLOCI/EPSLOCI/5GS3GPPLOCI/
Keys/KeysPS/SMS/Kc/KcGPRS/LOCIGPRS/CBMID/SMSS), checked by default
- ADF-rooted paths use the AID; paths resolve via a new server _select_path
Server:
- _select_path() resolves MF/ADF-AID-rooted paths (pySim can't select ADF by AID)
- /api/select and /api/read accept 'path'; /api/select returns file_size,
record_len, num_of_rec
Also fix: silent SELECT (P2=0x0C) no longer emits a trailing Le byte
(chain builder) - matches ETSI TS 102 221 silent-select behavior.
Tests: +profiler.test.js; frontend 117 pass, Python 61 pass.
SW cache v37.
- Removed STK pill from pysim sub-tab bar
- Added 'STK menu' block atop 'STATUS and Polling' in Proactive UICC view
- Shows emerald button when card issued SET UP MENU, 'no menu' text otherwise
- stkCheckMenu toggles button vs no-menu text; refreshed on proactive tab open
- Dropped dead stk-menu-btn exemption in stkSetPillsDisabled
- i18n: Меню STK / Меню не задано картой
- SW cache v36
- Added get-response to CHAIN_CMDS_RAM
- Added + GET RESPONSE button to RAM/GP sub-tab
- Added Le input field template in chainRamFieldsHtml
- Added CLA=80 GET RESPONSE handler in chainRamBuildRowHex
- Le stripping for Case 4 + GET RESPONSE already handled by chainBuildHex
__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.
The Secured Packet and Cards views moved under a single top-level SCP80
tab switched by two pills. Update README and help (EN+RU) section
titles/descriptions to match; bump service-worker cache so the precached
help pages are re-fetched.
_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
- PARSE_INS CB: rename 'RETRIEVE DATA (GET DATA)' -> 'RETRIEVE DATA'
(CA = GET DATA, CB = RETRIEVE DATA are separate commands per TS 102 221)
- Add trailing newlines to sim.test.js + response_map.test.js
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
- sim.png regenerated from sim.svg with true transparency (was flattened
onto opaque white; favicon showed a white box)
- new favicon.svg: same glyph, fill moved to presentation attribute +
embedded @media (prefers-color-scheme: dark) -> white glyph on dark
browser themes (interface sim.svg untouched, keeps dark:invert)
- index.html: dual icon links (svg for modern browsers, png Safari fallback)
- sw.js: precache favicon.svg, cache bump otaman-v11 -> otaman-v12
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