Commit Graph

18 Commits

Author SHA1 Message Date
catarrh 8e68673509 terminal profile: spec-audited bits, fixed layout, more presets (v2.2.10)
TERMINAL PROFILE form (Phone simulator):
- The compact block no longer shows the hex value (no room); Send +
  Configure + status remain. Preset selector sits above the hex field.
- Bits are never spread horizontally: each byte is one vertical list.
  Byte blocks are placed by the fixed TP_LAYOUT groups - bytes 1-12 in
  2 columns, 13-16 in 4, 17-18 in 2, 19-21 in 3, 22-25 in 2, 26-28 in 3,
  29-30 in 2, 31+ one per row - in strict byte order.

Bit descriptions audited against the pinned specs:
- Table regenerated from ETSI TS 102 223 V18.3.0 5.2 (bytes 1-39) with
  the 3GPP-defined bits named per ETSI TS 131 111 V18.12.0 5.2; the
  "reserved by 3GPP" placeholders are gone (tests guard this).
- Fixes found in the audit: byte 7 b6 PERFORM CARD APDU (pySim had
  RESET), MO short message control by USIM, USSD string DO support in
  Call Control by USIM, the earlier "Data available" typo.
- Presets: project default + Quectel example + Samsung S21+ 5G, Samsung
  A55 5G, Xiaomi Redmi Note 10 LTE, Sony Xperia Z5c LTE, Huawei
  E5573c/M150 (LTE), Huawei E173 3G modem, Nokia 7210 2G (9 total).

Also fixes a page-load TDZ regression: the custom-files init (which
calls t()) now runs after the language init (guard test added).

SW cache otaman-v174; help EN/RU updated; tests 236 Python + 374 frontend.
2026-09-16 22:15:01 +03:00
catarrh aa33d25466 terminal profile at runtime + canonical custom-file paths (v2.2.3)
TERMINAL PROFILE:
- GET /api/terminal-profile returns the profile in effect + the CLI default;
  POST /api/terminal-profile validates ({profile}, hex, even, 1-255 bytes),
  stores it in memory and re-sends it, resetting the STK session like
  /api/rescue (the shared _resend_terminal_profile helper; rescue now
  delegates to it). __main__ keeps server.cli_terminal_profile.
- Phone tab: a TERMINAL PROFILE block next to STATUS and Polling with the
  current hex/byte count, Send (re-send) and Configure. The Configure
  dialog has a device-model preset selector, a hex field and a per-bit
  form generated from a 264-entry table for TS 102 223 V18.3.0 5.2 bytes
  1-33 (pySim's table as scaffold, later bytes/3GPP bits added from the
  spec; beyond the table generic RFU labels). Form <-> hex sync both
  ways, hex authoritative, bits preserved. Apply posts the new value.
- Presets: Xiaomi Mi A1 (project default), Quectel GSM module example.
  In-memory only, no persistence.

Custom files:
- Canonical paths rooted at MF / ADF.USIM / ADF.ISIM; entries are
  {path, name, kind}. The editor now uses root + parent-DF selector +
  4-hex FID + alias, requires the parent DF to be defined first, rejects
  duplicates, rewrites descendants when a DF's path changes and cascades
  deletes after a confirmation.
- Legacy forms are normalized on load/import (3F00/... -> MF/..., relative
  a153/4954 resolved against the custom DFs); unresolvable entries are
  dropped and reported in the list.
- Tree injection matches by exact parent path via the new pysimFsNodePath
  (same-FID DFs under different parents no longer collide);
  profilerCustomNameForPath uses the same normalization.

SW cache otaman-v167; help EN/RU + docs/api.md + AGENTS updated.
Tests: 236 Python + 371 frontend.
2026-09-16 20:46:40 +03:00
catarrh 20fbfd99a5 scp81 passthru, proactive decoders, ADM badge, file manager layout (v2.2.1)
SCP81:
- New listener mode "passthru": no local listener - the card's BIP
  channels connect straight to a configured external platform
  (host/port required), which terminates TLS and runs the dialog.
  The status API reports mode/target (_SCP81_MODE/_SCP81_TARGET) and
  clears them on stop. PWA mode select, hint, Start validation; TLS PSK
  stays the default.

Proactive command decoding (Phone tab log):
- SEND SHORT MESSAGE (0x13) is now decoded: alpha, address (TON/NPI +
  number), 3GPP-SMS TPDU (type, TP-MR, TP-DA, TP-PID 0x7F flagged as
  SIM data download, TP-DCS, TP-VP, TP-UDL), UDH concatenation IEs, and
  the TP-UD as text (GSM-7 with a septet unpacker, UCS2, 8-bit) or as a
  TS 31.115 secured packet for PID 0x7F; malformed TPDUs fall back to
  the raw hex line.
- PROVIDE LOCAL INFORMATION qualifier names completed per TS 102 223
  V18.3.0: ESN (07), MEID (0B), Supported RATs (1A); 05 relabelled
  "Reserved for GSM (Timing Advance)". Fixed in the server dict and
  both frontend tables.

Header:
- Compact ADM badge next to the card indicator: "ADM ✓" green when
  pySim's adm_verified is set, "ADM ✗" red otherwise, hidden without a
  card session or when the server is down; updated ahead of the card
  state-key early return so it never disturbs the connect/reset flow.

File manager:
- Sort pills (FID/Name), Probe all files button and progress line are
  pinned above the tree instead of scrolling with it; the tree box cap
  grows from 420px to 65vh.

SW cache otaman-v165; help EN/RU updated; tests 234 Python + 361 frontend.
2026-09-16 15:29:01 +03:00
catarrh 8c90958718 scp81: PSK by identity, scripts page, exact snapshots, SCP80 LOAD fit (v2.2.0)
Cards / SCP81:
- Cards is a top-level tab; presets gain PSK identity + key, HTTP-OTA
  column, Edit/Update and a live PSK-map push into a running listener.
- SCP81 has Listener/Scripts pills; scripts are named local APDU lists
  (Empty / Explore / Install from .cap / Delete templates), sent to the
  server explicitly at start. The listener takes mode/host/port/script
  only; PSK inputs and the .cap row are gone.
- Multi-PSK TLS listener: identity -> key lookup from the card presets
  (POST /api/scp81/psk-map updates a running listener), unknown
  identities log tls-psk-unknown and fail; handshake logs carry psk_match.
- Script engine: execution tracking (next/done/pending/results), a
  resumed dialog sends only the unexecuted tail (unreported APDU is
  resent), a fresh dialog restarts, listing continuation pages are
  tracked separately (pending.pages/complete). Restart script button.
- POST /api/scp81/gen-install replaces the SCP81 ram-install queueing
  (generation only; the .cap is never stored).

Profiler / snapshots:
- Snapshot comparison is always exact (mask checkboxes removed; the
  first-4-bytes mask remains a profile-creation option).
- "matching records" line shows count + #record numbers.
- New Clone action: copy named "Copy of <profile>", opened in the editor.
- Matched-record count/numbers fix ("1 из 8 (#8)").

SCP80:
- Configurable / auto-fitted LOAD block size: each LOAD APDU encodes into
  one SMS (pySim rejects secured packets above 140 octets, so a 240-byte
  block could never be sent). Response reports the effective size and
  clamps; encode failures are reported per step with the pySim message.

SW cache otaman-v161; docs/api.md, scp81-findings and help EN/RU updated.
Tests: 226 Python + 356 frontend.
2026-09-16 13:32:26 +03:00
catarrh 175ca934d8 scp81: PSK TLS server and command scripting (phases B/C) + BIP fix (v2.1.5)
- scp81.py: PSK TLS listener (stdlib ssl PSK callbacks) speaking the GP
  HTTP administration dialog; configurable framing (chunked/Content-Length,
  TLS record split, Apache-style/compact headers, Connection header,
  keep-alive, Next-URI template with %d, TLS version/cipher, answer delay,
  keylog for capture decryption)
- server.py: script responder + Response Scripting parsing (AF/AB, 80/23
  TLVs), memory decoder, SCP81 start options, terminal-side timer
  management, background-mode BIP events, permissive OPEN CHANNEL
- BIP fix: the RECEIVE DATA channel-data TLV length is BER long form
  (36 81 <len>) above 127 bytes; a raw length byte is mis-parsed on the
  card, so the large TLS records never reached its stack (a live card
  fetched the script response and silently never processed it - endless
  resume). The card now executes scripts and returns R-APDUs: memory
  (13 applets, 50646 B NV free, 2402 B volatile), ISD, stored HTTP OTA
  parameters, ELF and application registries
- frontend: SCP81 tab (listener, script selection, HTTP OTA log), phone
  event forms, i18n; service worker v141
- docs: api.md, scp81-findings.md (attempt matrix + root cause analysis);
  tools/scp81_decrypt.py decrypts listener captures via the keylog
- tests: 187 python + 337 frontend
2026-09-16 01:50:29 +03:00
catarrh 2213e25385 ui: generate a profile from a card snapshot
- new 'Profile from snapshot' button in the Profiles toolbar: reuses the
  snapshot picker (profilerSnapshotPickListHtml refactor) and then opens
  the same scan form as Profile from card (profilerScanOpenForm), with
  the profile name prefilled from the snapshot
- new profiler-build target 'profile-snapshot': the form's ignore list,
  mask options and FCP/FCI mode apply to profilerScanSnapshot(), which
  walks the snapshot's captured files instead of the card; uncaptured
  contents produce rules without a content check, record files map to
  exact record content, transparent files honor the masks
- profilerScanStart() pushes the generated profile and opens the editor
  like the live scan; _scanSnapshot is cleared in the finally block
- RU entry 'Профиль из снимка'; tests for the snapshot rule builder
  (exact/mask/ignore/uncaptured/record), the snapshot scan progress, the
  picker wiring and the form title, plus a structural button check;
  help EN/RU and READMEs updated; SW cache v130 -> v131.
2026-09-13 11:32:18 +03:00
catarrh 2a559371ae ui: keep the selected file in state, drop the File: title line
The File: EF.DIR [LF] line doubled as the selection store: pysimFsClickFile
wrote a formatted display string there and pysimFsRead/Save parsed it back
(stripping '✗ ' and ' [TYPE]'). The pane now keeps the name in
pysimFsSelected, the redundant title is gone (FID/type/size/FCI remain in
the info block), and the dead pysimFsSelect() plus the unused 'File:' RU
entry are removed. Read/Save still re-select on the server first: pySim's
current selection is a shared cursor that tree expansion, scans and
commands move, so it cannot identify the file shown in the pane.

SW cache v121 -> v122; structural test updated.
2026-09-12 22:12:25 +03:00
catarrh 2ec7a2d6c0 ui: show FID, layout and decoded FCI in the file manager detail pane
- selecting an EF now renders an info block between the File: title and
  the contents: FID, file type, size, record length/count (nulls skipped)
  followed by the decoded FCI via the existing profilerFciPreviewItems()
  decoder (no raw hex), matching the snapshot detail view
- pysimFsInfoHtml() is pure and reused by pysimFsClickFile(); the [LF]
  shorthand stays in the title; custom files get the same metadata from
  the temporary select probe
- tests: pysimFsInfoHtml in profiler.test.js (full response, missing
  fci_hex, skipped nulls) and a structural check for #pysim-fs-info;
  help EN/RU and READMEs updated; SW cache v119 -> v120.
2026-09-12 21:54:06 +03:00
catarrh 92271d6726 ui: probe-all-files walk and honest file tree coloring
- pysimFsLoadChildren now treats {success:false,error} (and any error
  payload) as a failed listing: absent DFs/ADFs render as a red cross
  without an expand arrow instead of silently opening empty; the
  /api/tree 500 body also carries exists:false for shape consistency
  with /api/select
- an expanded directory with zero children shows a gray (empty)
  placeholder
- new Probe all files button (data-needs=card) in the tree header:
  walks the whole model tree from MF, verifies every DF/ADF via
  /api/tree and every file (incl. custom entries) via /api/select,
  skips subtrees of absent dirs, shows N/total progress, toggles to
  Stop, and reports present/absent counts with elapsed time; results
  colour the current tree only
- tests: fs_load (error/retry/empty), fs_render (red cross, (empty)),
  fs_probe (flags, absent-dir skip, custom files, stop, summary);
  html structural check; help EN/RU, READMEs, AGENTS updated;
  SW cache v115 -> v116.
2026-09-12 21:14:09 +03:00
catarrh 4598a70db2 ui: edit/delete buttons for custom files
- each row now shows Edit and Delete buttons (same styling as the
  profiler list buttons) instead of the red X glyph
- Edit reloads the entry into the top form: the Add button becomes
  Save (data-l10n updated too) and a Cancel button appears; submit
  updates the entry in place, recomputing fid/parentFid and excluding
  the edited row from the duplicate-path check
- Delete removes without confirmation; deleting the row being edited
  cancels the edit and an earlier deletion shifts the edit index
- pysimCustomAdd renamed to pysimCustomSubmit; tests for the whole
  flow; SW cache v114 -> v115.
2026-09-12 20:14:24 +03:00
catarrh 82fb2c8800 ui: move the Profiler tab next to SCP80
- top-level tab order is now Remote APDU, SCP80, Profiler, Card reader,
  Phone simulator; the profiler view moved with its button so the DOM
  order matches the tab strip
- docs (help EN/RU, READMEs) and the tab-order test updated; SW cache
  v113 -> v114.
2026-09-12 20:09:47 +03:00
catarrh 5485d61390 ui: sort the file manager tree by FID or symbolic name
- pysimFsSortChildren() groups DFs above EFs, then sorts each group by
  FID (default) or case-insensitive symbolic name; missing names fall
  back to the FID as the sort key; deterministic tie-break; the input
  array is not mutated
- render uses the sorted copy, so lazily loaded directories and custom
  file injections are sorted too
- FID / Name pills above the tree (pysimFsSetSort) persist the choice in
  localStorage (otaman_fs_sort) and re-render without refetching
- tests for the comparator (DF priority, both keys, fallbacks, custom
  entries, no mutation) and the pill wiring; help/README updated;
  SW cache v108 -> v109.
2026-09-12 19:41:49 +03:00
catarrh 5b9e821d6d ui: gate controls by server/card state; move Custom files to Profiler
Availability model: server-down / server-up-no-card / card-equipped.
- header shows a symbolic indicator dot (red/yellow/green, tooltip)
- a 5s probe tracks /api/version+/api/status while not fully ready; the
  2s status poll takes over once connected
- controls declare data-needs='server' or 'card'; disabled + tooltip when
  the state does not satisfy them: equip (server), status/reset, file
  manager read/save/edit, raw APDU, profile-from-card, new snapshot,
  profiler list Check card, event Send, RAM install/explore delete,
  STK menu, Send STATUS, Verify vs pySim (server), pySim execute (server),
  PLI save/poll toggle (server)
- pysimConnect no longer conflates server and card: it records both and
  Connect stays usable without a card
- ramSendOta/ramInstallCap now use pysimFetch instead of raw fetch
  (relative /api URLs broke custom server URLs)

Custom files moved from the Card reader sub-tabs to the Profiler list
tabs (Profiles / Card snapshots / Custom files); help/README/AGENTS
updated. Tests: availability gating, indicator structure, moved tab.
SW cache v103 -> v104.
2026-09-12 17:58:30 +03:00
catarrh 6d30e7095e snapshots: measure SELECT / READ commands and show timing stats
Server measures every classified APDU (A4 select, B0 read binary, B2 read
record) from command to response: _collect_apdu_times() enables the tracer
(reattaching it if pySim nulled it) and /api/select + /api/read return
'apdu_times': [{type, ms}]. Collection is safe: handlers hold _CARD_LOCK.

Snapshots store per-file {select_ms, read_ms}, a ms value per record and
snapshot-level stats {select, read_binary, read_record}: {min, max, avg,
count} plus total_ms (wall time of the scan). The snapshot view gets a
summary under the title (files/records counts, scan time, min/avg/max per
command type) and shows select/read per file and read time per record.
Timings are display-only: checks, snapshots comparison and imports ignore
them (old snapshots simply show 'No timing data').

Tests: Python classifier/collection (tests/test_apdu_timing.py) and
frontend stats/accumulator/format/build-file/summary. SW cache v99 ->
v100; help, README and docs/api.md updated.
2026-09-12 14:56:59 +03:00
catarrh a261675aad ui: focus scan name input and start scanning on Enter
'Profile from card' and 'New snapshot' now focus the name field when the
dialog opens, and Enter in that field starts the scan
(profilerScanNameKeydown, ignored while the Scan button is disabled).
Tests for the key handler and the input wiring; SW cache v98 -> v99.
2026-09-12 14:41:35 +03:00
catarrh 9363f209ee ui: split Phone simulator into Phone and TR Config pills
- Phone pill: STK menu, STATUS and polling and the subscribed-events list
  in one row, proactive command log full-width below (room for more
  elements)
- TR Config pill: response data injected into TERMINAL RESPONSEs;
  currently the PROVIDE LOCAL INFORMATION dictionary, structured as
  heading + body blocks for future proactive-command responses
- phoneSwitchSubtab() mirrors the other sub-tab switchers and sets help
  anchors stk-menu / pli-dict; switchTab('phone') always opens Phone
- refreshDynamicI18n renders only the visible phone panel
- help EN/RU section 6 regrouped (6.4 STATUS polling under Phone, 6.5
  TR Config response data), READMEs and AGENTS.md updated
- structural and behavioral tests (html.test.js, phone_tabs.test.js);
  SW cache v93 -> v94
2026-09-12 14:17:48 +03:00
catarrh 33443d4f28 ui: regroup tabs — Remote APDU, Card reader, Profiler, Phone simulator
- C-APDU tab renamed Remote APDU (label untranslated EN/RU); Response
  parser moved from a top-level tab to a pill under it
- Profiler moved from the Card reader sub-tabs to a top-level tab
- Proactive UICC moved to a top-level tab and renamed Phone simulator
- Card reader keeps File manager, Custom files, pySim command line,
  Raw APDU
- modals (event send, profiler scan/snapshot, STK menu) moved outside
  the tab containers so they can open from any tab
- STK overlay disables the top-level tabs while waiting for user input
- OTA PoR jump now goes to Remote APDU > Response parser
- help EN/RU restructured (2.8 Response parser, 5 Profiler, 6 Phone
  simulator, subsequent sections renumbered, anchors kept); READMEs and
  AGENTS.md updated
- html.test.js asserts the new tab/pill structure; SW cache v92 -> v93
2026-09-12 14:01:28 +03:00
catarrh d2c292e5b8 Merge pysim-otaman-server into otaman (monorepo, v1.7.0)
- Move PWA into frontend/; server package at pysim_otaman_server/
- Server now serves the PWA (same origin -> no CORS/PNA): static file handler
  + --web-dir flag + injects window.PYSIM_EMBEDDED marker into index.html
- Frontend defaults to relative API base when embedded (pysimBase = '')
- Version 1.7.0 aligned: server.py VERSION, pyproject.toml, PWA header
- SW cache bump otaman-v7 -> otaman-v8
- Docs: combined README + docs/api.md endpoint reference; update links
- Fix stale package.json repository URL
2026-08-14 15:48:38 +03:00