Commit Graph

206 Commits

Author SHA1 Message Date
catarrh ef9faabdf8 ui: TP bit labels — byte 30 b5-b8 and byte 34 b8 per TS 102 223 (v2.7.4)
- byte 30 b5-b8 were marked RFU; now Proactive UICC: ACTIVATE,
  GEOGRAPHICAL LOCATION REQUEST, PROVIDE LOCAL INFORMATION (Broadcast
  Network Information) and "Steering of Roaming for I-WLAN" REFRESH
  support (TS 102 223 cl. 5.2; TS 31.111 cl. 5.2 names)
- byte 34 b8 "Reserved for future use" -> "Deprecated, bit = 0"
  (TS 102 223 cl. 5.2)
- mirrors the UICC_SPECS.md audit fixes (byte 30/34 tables, ESN/MEID
  bit positions b5/b6, Data Connection Status Change in extra events)
- SW cache simple-v207; 454 frontend / 298 Python tests green
2026-09-20 13:44:44 +03:00
catarrh cd802d965e ui: network-monitor status on its own line + Refresh translation (v2.7.3)
The location line in the Phone-tab network monitor grew too long once the
status was appended (e.g. "262-01 · Germany · Telekom · Guest (roaming)
 PLMN not allowed").  netStateRender now keeps the identity
(mcc-mnc · country · operator) on the first line and renders the status
elements (roaming tag + forbidden badge) in a block span on a second line;
a location without a status still renders on one line.

Also add the missing Russian translation for the monitor's Refresh button
(t() fell back to English): 'Refresh': 'Обновить'.

- frontend/index.html: two-line location render, LANG_RU Refresh, 2.7.3
- frontend/tests/netstate.test.js: pins the block-span layout and the
  one-line no-status case
- sw.js cache simple-v206; version trio 2.7.3
2026-09-20 13:34:21 +03:00
catarrh 6bea10bb5b net: align the EPSLOCI dummy with the corrected trace study (v2.7.2)
The UICC_NAA.md C3/C3a recipes were re-checked against the corpus: the
EPSLOCI dummy tail is FF*13 + `FF FE` + status - the TAC is always FFFE
(never FF) and the status byte is 01 (not updated), not FF; the old
`<FF*16>` reading was a misparse of that tail.  The rejection form
(status 02) is the C3a spec model: the only observed rejection trace never
writes 6FE3 at all.

- build_epsloci_dummy(status=ST_NOT_UPDATED, keep_plmn=None): corrected
  tail; keep_plmn selects the NMR style that preserves the last visited
  TAI PLMN (the guest style wipes GUTI and TAI PLMN) - both are observed
- write_dummy_locations(): drop the all-FF special case (service loss now
  ends `FF FE 01`) and thread keep_plmn through
- tests: exact guest/NMR/rejection vectors, 18-byte length, runner
  assertions for service_lost and roaming_denied; netstate fixture updated
- UICC_NAA.md C3a tail `<FF*15> 02` corrected to `<FF*13> FF FE 02`;
  the PWA help/param text no longer claims the EPSLOCI dummy keeps the
  PLMN; SW cache simple-v205
2026-09-20 13:30:26 +03:00
catarrh bb13ed45f8 net: initialize the monitor in the startup equip path (v2.7.1)
The startup init (server booted with the card already in the reader) equips
the card and reads the ICCID itself, but never created server.net_state -
only /api/command equip and the auto-equip worker did.  With a card present
at boot the monitor stayed empty, net-sim responses carried net_state: null
and the panel only started updating after pressing Refresh (which lazily
created the state).  Fix:

- factor _netstate_read(app) / _netstate_install() / _netstate_init() in
  server.py; _apply_equipped_card uses _netstate_init()
- __main__ startup: read the monitored EFs in the same CAT-free window as
  the ICCID and install the state once the server object exists
- _netstate_ensure() lazily initializes the state in the net-sim/event hooks
  so any equip path predating the monitor cannot leave it dead
- netSimRun falls back to netStateFetch() when a response has no net_state
- tests: tests/test_netstate_server.py (candidate order, record vs
  transparent, absent files, install, ensure); SW cache simple-v204
2026-09-20 13:12:00 +03:00
catarrh b1539d7cd5 net: network-state monitor and permanent-rejection FPLMN (v2.7.0)
server:
- netsim: LOCI/PSLOCI dummy builders take a status; the EPSLOCI dummy is
  wiped to 0B F6 + FF per UICC_NAA.md C3; roaming_denied now emulates the
  permanent 'PLMN not allowed' rejection (C3a): status 010, EPSNSC dropped,
  EF.FPLMN append with TS 31.102 4.2.16 shift semantics and a home-PLMN
  guard (HPLMNwAcT/EHPLMN, IMSI fallback), optional 'Rejection: write FPLMN'
  toggle; write steps carry the logical key; SCENARIO_SERVICE map;
  insert_fplmn/fplmn_entries/parse_imsi helpers
- netstate.py: cached per-session monitor state for the 12 network EFs,
  step-based patch, simulated service state, derived location with country/
  operator (optional MCC/MNC list) and roaming class
- server: monitor read at equip right after a readable ICCID (skipped
  otherwise), cleared on card removal; GET /api/net-state and POST
  /api/net-state-refresh; net-sim patches the cache from the written bytes
  and re-reads EF.IMSI; Location-status events set the service state and
  re-read EF.IMSI (multi-IMSI applets)

frontend:
- Phone tab: 'Network state' panel next to Network simulation with the
  simulated service badge (Undefined until simulated; normal/limited/no
  service + rejection marker), location/roaming line, compact per-file
  summaries with full-decode tooltips and a Refresh button; no card polling
- EF decoders: EF.FPLMN (FFFFFF gaps are not terminators) and EF.EHPLMN
- i18n EN/RU, help updated; SW cache simple-v203

tests: 295 Python / 453 frontend
2026-09-20 12:26:17 +03:00
catarrh 81b50199cc ui: bordered header badges and ICCID-labelled card actions (v2.6.2)
- ADM/SCP80/SCP81 header indicators become bordered chips (neutral gray
  border, rounded, px-1.5 py-0.5); SCP80/SCP81 get an even ml-1 gap, so no
  separator glyph is needed
- profiler/snapshot card actions carry the equipped card's ICCID:
  'Profile from card <ICCID>', 'Check card <ICCID>' (row button, arrow
  dropped), 'New snapshot: <ICCID>'; pysimUpdateCardActionLabels() fills
  the spans on every status poll and after a language switch
- these buttons switch to data-needs='card-iccid': disabled without a
  readable ICCID ('Card equipped but its ICCID is not readable' hint)
- profiler rows wrap so the longer button cannot break the layout;
  help EN/RU updated; SW cache simple-v202
2026-09-20 01:40:39 +03:00
catarrh 97bc8f929d ui: header preset indicators — ADM key glyph, SCP80/SCP81 completeness (v2.6.1)
- the ADM badge gains a trailing key glyph (ADM ✓ ⚿ / ADM ✗ ⚿) when the
  preset matching the equipped card's ICCID carries an ADM key; the four
  tooltip states cover key/no-key x verified/not-verified
- new gray SCP80/SCP81 markers after ADM, shown only when that preset has
  all SCP80 fields (KIc/KID, SPI1/SPI2, counter, KIc/KID keys) or the SCP81
  PSK pair filled in; hidden without a card session, a matching preset or
  the fields, never color-coded
- cardsScp80Complete/cardsScp81Complete/cardsAdmPresent/cardsMatchedPreset
  helpers; cardsRender reuses the SCP81 predicate
- tests: ADM four-state badge, marker show/hide, predicate whitespace
  handling, header markup order; help EN/RU updated; SW cache simple-v201
2026-09-20 01:08:44 +03:00
catarrh cee59bd611 cards: label the preset TARs (ISD / UICC RFM / ADF RFM) after Counter
- the three TAR inputs move to the right of the Counter field and get
  visible labels: ISD TAR, UICC RFM TAR, ADF RFM TAR (placeholders removed)
- names verified against the pinned specs: TS 101 220 Annex D ('00 00 00'
  Issuer Security Domain, 'B0 00 00' UICC Shared File System, 'B0 00 01'
  ADF) and TS 102 226 §7.2/§7.3 (MF implicitly selected, no ADF access vs
  ADF implicitly selected, TAR linked to the ADF AID); help EN/RU updated
  with the references
- card list TAR header/tooltip use the new names
- html test guards the labels and the Counter -> TAR field order
- SW cache simple-v200 (version stays 2.6.0, relabel/layout only)
2026-09-20 00:18:02 +03:00
catarrh 15121cff20 cards: per-target TARs (ISD/UICC/USIM) and an ADM field in presets (v2.6.0)
- the SCP80 fieldset gains ISD TAR (000000), UICC TAR (B00000) and USIM TAR
  (B00001), prefilled with the spec defaults; empty stored values mean the
  default for that target (cardsTarValue)
- ADM field between "From card" and "Add", stored in the preset for later
  use by the file manager (placeholder ADM (optional), spaces stripped)
- packing a chain selects the preset TAR for its target: RAM/GP -> ISD,
  SIM RFM -> UICC, USIM RFM -> USIM (spec default without a preset);
  _spTarKey keeps the target when the card preset is picked afterwards,
  RAM card selection forces the ISD TAR
- USIM RFM 'from current DF' labelled (ADF.USIM); the list TAR column shows
  all three values; help EN/RU updated
- SW cache simple-v199; new cards_form.test.js + html markup guards
  (440 frontend / 281 Python tests green)
2026-09-20 00:05:39 +03:00
catarrh 8aa9e8eaef ui: separate the header ICCID from the ADM badge
- the ADM badge gets ml-2 (was ml-0.5), so the ICCID digits and the ADM
  indicator no longer run together in the header
- SW cache simple-v198
2026-09-19 23:41:17 +03:00
catarrh ccabef883e ui: print the equipped card's ICCID in the header indicator (v2.5.2)
- new #state-indicator-iccid span between the card image and the ADM badge;
  shows the EF.ICCID digits of the equipped card (hidden without a card
  session or when the ICCID is unreadable, cleared when the server is lost)
- pysimUpdateIccidIndicator() mirrors the ADM badge pattern (no rewrite when
  the value is unchanged); wired into pysimCardStateUpdate() and
  pysimSetServerAvailable()
- indicator/card_state tests for show/hide and markup order;
  SW cache simple-v197
2026-09-19 23:40:08 +03:00
catarrh 163a859658 ui: drop the clipboard/paste JSON buttons from Cards and custom files (v2.5.1)
- Cards and Custom files now expose only Export to file (JSON download) and
  Import from file; the Export-as-JSON / Paste & import / Import-JSON-from-
  clipboard buttons and their paste textareas are gone
- import feedback moves to a transient status line (new ioStatus helper);
  cardsImport/pysimCustomImport no longer read the clipboard
- Network simulation parameters: note under the operator form listing the
  scenarios that use the MCC/MNC (EPS/2G attach and SMS received write real
  LAI/RAI/TAI; cold boot, service lost, limited service and roaming denied
  write dummy files keeping the PLMN; churn/CB/AUTHENTICATE do not use it)
- i18n: removed-button entries dropped, note added EN/RU; help EN/RU updated
- guard tests for the button set and the note; SW cache simple-v196
2026-09-19 23:26:21 +03:00
catarrh 8848df1c1e ui: green Send buttons, TP status below the buttons
- TERMINAL PROFILE: the status line moves out of the button row (its own
  line under Send/Configure; tpStatusMsg keeps the top margin on every
  update) and the Send button turns emerald
- STATUS and Polling: Send STATUS turns emerald as well
- html guard test for the green buttons and the status placement;
  SW cache simple-v195
2026-09-19 23:10:41 +03:00
catarrh bc5d46eccb ui: move Network simulation above the proactive log; drop the doc link from its text
- the Network simulation fieldset now sits directly under the four Phone
  containers (Fetched proactive commands moves below it, taking the top
  margin)
- the fieldset description no longer points at projects/UICC_NAA.md (the
  research note stays referenced in the help pages and README)
- SW cache simple-v194
2026-09-19 22:56:48 +03:00
catarrh 0d92ea8f24 net-sim: network-condition simulation in the Phone tab (v2.5.0)
Server (pysim_simple_server/netsim.py + POST /api/net-sim):
- pure builders for the observed phone write vocabulary (UICC_NAA.md §13):
  EPSNSC (A0 TLV with KSI/KASME/counts/algo, padded to the card's FCP record
  size; invalidate-wipe and invalidate-keep-key), LOCI/PSLOCI/EPSLOCI real
  and dummy forms (PLMN kept, LAC FFFE, status 01/02), Kc/KcGPRS (9-byte
  USIM and 33-byte GSM forms, 07 invalidates), EF.SMSstatus counter bump
  (read-modify-write), CBMI/CBMIR, the Location status event (9B 01 <st>,
  with the optional 13 07 location info for normal service) and the
  AUTHENTICATE APDU (TS 31.102 7.1.2.1) with DB/DC response parsing
- scenario runner executes the recipes under _CARD_LOCK, picks the first
  existing candidate path (ADF.USIM vs DF.GSM/DF.TELECOM), reads FCP/current
  records where the format is card-specific, skips the event unless the card
  subscribed to Location status, honours the scenario toggles and returns a
  per-step log; only D6/DC, ENVELOPE and AUTHENTICATE are ever sent
- GET /api/mcc-mnc serves the optional worldwide operator list
  (--mcc-mnc-list, default <workspace>/samples/mcc-mnc-list.json): ?q=
  search and ?random=1&exclude= for roaming; the list stays out of the repo

PWA:
- Phone pill gains a 'Network simulation' fieldset: one button per scenario
  (cold boot, EPS/2G attach, service lost, limited service, roaming denied,
  churn, SMS received, CB reconfig, AUTHENTICATE), a collapsed Parameters
  block (operator search + random roaming, LAC/Cell ID/TAC/RAC, optional
  identities, toggles, churn count/delay) and a step log with SWs
- i18n EN/RU, help/README/api.md/AGENTS.md updated; version 2.5.0,
  SW cache simple-v193

Tests: tests/test_netsim.py (19), frontend/tests/netsim.test.js (3) and
html guards - 423 frontend / 280 Python, all passing
2026-09-19 22:24:57 +03:00
catarrh 158f1c676f ui: embedded labels (fieldset/legend) for the phone simulator and SCP81 containers
- Phone simulator: STK menu, STATUS and Polling, TERMINAL PROFILE and
  SET UP EVENT LIST become fieldsets with legends; 'Fetched proactive
  commands:' gains a bordered container
- SCP81 listener: the HTTP OTA listener label moves into the legend (the
  live state span stays next to it), Script results (R-APDUs) and HTTP OTA
  log become fieldsets (the log's Clear button moves to the body top-right)
- Options (applied at Start) stays a details/summary but its summary is
  styled as an embedded label (page-background patch over the top border,
  native triangle kept) so it expands/collapses from the legend
- html.test.js: card-fieldset test no longer assumes exactly two fieldsets;
  new guard test for the legends and the Options summary
- Tailwind rebuild of style.css; SW cache simple-v192 (version stays 2.4.0)
2026-09-19 21:51:03 +03:00
catarrh eb83238ae7 EF content decoders: decoded fields in the file manager, snapshots, checks and comparisons (v2.4.0)
Client-side, offline decoder layer (ported from simtrace-analyser, verified
against the pinned specs and pySim test vectors):
- file manager Decoded pill: decoded field table instead of the raw pySim
  JSON dump (per-record fields for record files); the server's
  read_*_decoded JSON stays behind a 'pySim JSON (server)' disclosure;
  switching Raw/Decoded re-reads the open file
- snapshot view: decoded field block for transparent files and a per-record
  decoded summary for record files
- profile check / snapshot comparison report: content mismatches of files
  with a decoder render as a per-field decoded diff (only the differing
  fields, expected | actual); the raw expected/actual pair is kept when the
  decoded values match
- coverage: ICCID, IMSI/IST, UST/SST, LI/PL, SPN, PLMNsel/PLMNwAcT/
  OPLMNwAcT/HPLMNwAcT, OPL, SPDI, PNN, LOCI/PSLOCI/EPSLOCI/EPSNSC/LOCIGPRS,
  Kc/KcGPRS, ACC, PHASE, CBMI/CBMID/CBMIR, ECC, AD, ACL, SMSP, SMS, ARR, DIR,
  IMPI/DOMAIN/IMPU, ADN/FDN/SDN/MSISDN/LND/BDN, EXT1-5, SUME (TS 51.011
  §10.3.34); unknown files fall back to raw
- new frontend/tests/ef_decode.test.js (14 tests); help EN/RU + READMEs
  document the decoded views; version 2.4.0, SW cache simple-v191
- also fix the README/RUS snapshot-comparison text: comparison is always
  exact (no content masking)
2026-09-19 21:07:30 +03:00
catarrh 4f6e48f0e0 docs: fix version compatibility (PWA vs server majors), broaden descriptions, drop the header slogan (v2.3.1)
- version check in pysimConnect() now compares the server major against
  SIMPLE_VERSION (2.3.1) instead of the hardcoded 1.x assumption; the header
  version is rendered from the same constant
- removed the 'SIM OTA with a Human Face' slogan (span, i18n key, translate
  hook) to free header space
- broaden the PWA description in manifest.json/package.json and the help
  overview (EN/RU): APDU workbench, OTA lab, CAT/STK simulator, card profiler
- version compatibility tables in README/RUS, help EN/RU and docs/api.md now
  describe the major-version rule
- README/RUS: Cards promoted to a top-level section; EN duplicate
  Theme/Localisation sections removed
- docs: /api/cardinfo documented; SCP81 'expose framing options' next-work
  item dropped (done); local AGENTS.md not-implemented note corrected
- SW cache simple-v190
2026-09-19 08:54:33 +03:00
catarrh ac18c3e2cf rename OTAMan → SIMple (v2.3.0)
- package pysim_otaman_server → pysim_simple_server; distribution and
  console script pysim-otaman-server → pysim-simple-server
- PWA branding SIMple: title, header, manifest, help EN/RU, GitHub links
- localStorage keys otaman_* → simple_* (clean break, no migration)
- export filenames simple-cards.json / simple-custom-files.json
- service worker cache simple-v189
- README/docs/setup/start scripts updated; no 'otaman' left in the tree
2026-09-19 08:25:20 +03:00
catarrh 9c64da86a3 terminal profile: numeric value fields (soft keys, channels, screen, ND/NK, frames), doc corrections (v2.2.21)
- TP_BITS: fill the value-field rows that were all-RFU (byte 11 soft keys,
  byte 13 b6-b8 channels, byte 14 height + ND/NK/sizing, byte 15 width +
  variable fonts, byte 16 effects + width reduction, byte 19 TIA version,
  byte 24 max frames), per TS 102 223 5.2.
- TP_VALUE_FIELDS + tpGetValue/tpSetValue (little-endian bit ranges) and
  number inputs in the Configure dialog; the byte 11 'FF' reserved note
  stays visible in the hint; flags remain checkboxes.
- Tests: new label spot checks, value round-trips/clamping, render guard.
- docs/TERMINAL_PROFILE.md: byte 22 b1 not universal (S25 Ultra/edge 60
  clear, WISMO228 sets), b2/b3 counts fixed, byte 21 markup present in
  Pixel 3/4 + OnePlus 12, ESN is a capability bit not a CDMA marker,
  0xFE sentinel unexplained by the spec, channel count 5 added, new
  Appendix A with the value-field ranges.
- Version 2.2.21, SW cache otaman-v188.
2026-09-19 07:45:12 +03:00
catarrh 0fe51491d8 terminal profile: fix the per-byte bit order (b1 = LSB), add the inference notes (v2.2.20)
The spec tables (TS 102 223 5.2) list b1 - the least significant bit - first:
verified from the PDF connector geometry (label row 1 connects to the
rightmost cell) and from Wireshark's packet-gsm_sim.c masks ('Profile
download' = 0x01, 'E-UTRAN bearer' = 0x40, 'HSDPA bearer' = 0x80). The
Configure dialog had it mirrored: TP_BITS[n] was matched with 0x80 >> (n%8)
and displayed as b8..b1, so every label toggled the wrong physical bit
(our own BIP-capable default proved it: byte 12 = 0x1F read as DECLARE
SERVICE.. instead of OPEN/CLOSE/RECEIVE/SEND/GET CHANNEL STATUS).

- tpGetBit/tpSetBit now use 1 << (n%8); the form lists b1 first, matching
  the spec; tpBitLabel names the RFU fallback bit accordingly.
- Tests pin the new mapping and the corrected spot-check indices, and the
  project default is asserted to decode as BIP-capable (OPEN/SEND DATA,
  E-UTRAN, HSDPA).
- docs/TERMINAL_PROFILE.md (in projects/docs): everything gathered about
  inferring device characteristics from a TP - bit order, profile-length era
  bands, headless/modem markers (Annex S ND/NK plus the byte 11/screen-size
  heuristics), radio generation (byte 17 b7/b8 with the 'capability, not
  attach' caveat), standards family (ESN/IMEISV/TIA/CCAT), BIP capability,
  UI paradigm, and a suggested inference model. Corpus: samples/tp_db.csv
  (185 profiles, CC-BY-SA, kept out of the repo).
- Help EN/RU note the bit order and point at the new document; AGENTS
  records the mapping rule and the doc location.
2026-09-19 01:21:06 +03:00
catarrh 2a03c74941 cards: group the form into SCP80/SCP81 fieldsets, close the RU i18n gaps (v2.2.19)
- The card create/edit form now surrounds the SCP80 parameters (KIc/KID,
  SPI1/SPI2, TAR, counter, keys) with a bordered <fieldset> labelled
  'SCP80 (GSM 03.48, ETSI TS 102 225)' and the PSK pair (with the listener
  explanation) with one labelled 'SCP81 (HTTP OTA)'; the name/ICCID row and
  the list stay outside the groups.
- i18n: the SCP81 PSK note is translated now, plus the keys an audit found
  missing from the RU dictionary (Start, Clear, Loading..., Working...,
  APDU hex, CAP file (max 48 kB), Install from .cap, Card reader server is
  not connected, KIc/KID required messages, Memory (GET DATA FF21), the two
  no-secured-packet alerts, Select, event notification to the card?).
- Tests: html.test.js guards both fieldsets, their legends and that the PSK
  note and fields live inside the SCP81 group (401 frontend tests).
- Docs: help EN+RU Cards intro. SW cache otaman-v186.
2026-09-19 00:15:18 +03:00
catarrh ce7d91cfe0 cards: From card button + normalized duplicate ICCID refusal (v2.2.18)
- The card create/edit form gets a From card button next to the ICCID
  field: it fills the field with the equipped card's EF.ICCID. It uses the
  availability model through a new 'card-iccid' requirement - enabled only
  while /api/status reports a connected card with a readable iccid; with a
  card but no readable ICCID the tooltip says so, otherwise the usual
  'insert and equip' hint applies. The last status ICCID is kept in
  _pysimCardIccid (cleared on disconnect) so the click needs no round-trip.
- Duplicate ICCID refusal now compares normalized values via
  cardsNormIccid/cardsFindDuplicateIccid (digits, spaced digits and raw EF
  hex all identify the same card), skips the row being edited, ignores an
  empty field, and names the conflicting preset in the alert (translated).
- i18n RU: From card -> 'С карты', duplicate/unreadable alerts.
- Tests: cardsFindDuplicateIccid across formats and edit-skip,
  cardsIccidFromCard fill + alert path, form wiring guards, _pysimCardIccid
  tracking in the card-state harness, card-iccid availability gating in the
  indicator harness. 261 Python + 400 frontend tests green.
- Docs: help EN+RU Cards field table, READMEs, AGENTS. SW otaman-v185.
2026-09-18 23:57:05 +03:00
catarrh 235388a5d0 startup: cardless start is a normal state, not an init failure (v2.2.17)
- __main__ catches NoCardError from the startup init (fast and stock paths,
  and the stock fallback) and reports it with one line - 'INIT: no card in
  the reader - server ready; insert a card or press Equip' - without a
  traceback and without the stock-pysim fallback. Any other init failure
  still falls back with its traceback.
- The cardless PysimApp construction no longer prints pySim's 'Waiting for
  card...' or pySim-shell's 'pySim-shell not equipped!': a new _LineFilter
  (server.py) drops whole matching lines while proxying encoding/isatty/
  fileno (the cmd2 Rich console probes them on its stdout).
- Removed the now-duplicate 'INIT: card not initialized' tail line and the
  cat_cla assignment when no card was detected (scc exists but is cardless).
- Tests: tests/test_startup_cardless.py (line filter semantics incl. partial
  lines, one-line cardless report, no fallback call, real failures keep the
  fallback + traceback, muted PysimApp construction). Live-verified against
  an empty SCR 3310: two init lines, no traceback, API serves 2.2.17.
- Docs: READMEs quick start, help EN+RU (Card reader), AGENTS (equip flow).
  SW cache otaman-v184.
2026-09-18 23:41:12 +03:00
catarrh 6d5d23487a cards: read EF.ICCID at equip and auto-select the matching preset (v2.2.16)
- Server: _decode_iccid (nibble-swapped E.118 digits, trailing-F pad) and
  _read_iccid (best-effort MF/2FE2 read through the parent-scoped select
  helper; the previous selection is restored, the read never raises). The
  equip path records the digit string before the TERMINAL PROFILE, i.e.
  before any CAT session is active; a startup with a card does the same.
  The value is cleared on card removal and exposed as /api/status 'iccid'
  (only while connected).
- PWA: when a connected status update reports a *new* ICCID, the matching
  card preset is selected in both SCP80 views - Secured Packet (sp-card-sel
  + form fill) and RAM (ram-card-sel + _ramCardIdx). Matching normalizes
  digits and accepts the raw EF hex form, leading zeros ignored; a manual
  choice for the same card is kept until the next equip, and a card removal
  re-arms the auto-selection. The status line shows the ICCID.
- Tests: tests/test_iccid.py (decode variants, model + probe read, equip
  recording, disconnect clearing) and frontend/tests/cards_iccid.test.js
  (normalize, find, select, no-override, card swap); the card_state test
  harness stubs the new hook and covers the guard reset.
- Docs: api.md /api/status fields, help EN+RU (SCP80 intro + Cards tab),
  READMEs, AGENTS. SW cache otaman-v183.
2026-09-18 21:05:25 +03:00
catarrh ab91fcff54 i18n: reword the Next-URI hint (RU: 'Выкл - команда без ответа')
The dictionary hint, the RU help paragraph and README_RUS use the short
'выкл - команда без ответа' phrasing for the unchecked Next-URI case.
SW cache otaman-v182 (index.html/help-ru.html changed).
2026-09-17 09:29:24 +03:00
catarrh a59e374ff9 scp81: always reuse the connection, collapsible options, label polish (v2.2.15)
- Keep-alive is no longer a setting: the TLS connection stays open between
  POSTs for the whole dialog (the card, as HTTP client, may still dial a new
  one at any time - GP Am. B 4.3.1) and only the 204 ends it, with a clean
  close_notify while the response is still buffered, then FIN. Dropped
  keep_alive/on_before_close/_scp81_wait_drained and the Connection-header
  'close' value (the API, status and UI no longer carry a keep-alive knob).
- The Listener Options block is a collapsed <details> with a 'custom' badge
  when anything differs from the reference defaults; the Reset button moved
  into the body so it cannot toggle the panel.
- X-Admin-Targeted-Application is opt-in (checkbox + //aid/... field, field
  disabled while off); X-Admin-Next-URI has a checkbox + hint explaining the
  one-shot rule of GP Am. B 4.4.2; both dependent fields grey out when
  unchecked.
- Labels/i18n: 'Chunked body (Transfer-Encoding: chunked)' stays English,
  'Show link events (...)', 'Теги comprehension-required'; the compact-header
  wording now spells out that it omits the optional space after ':' (legal
  per RFC 7230 3.2 OWS; saves one byte per header).
- Tests: a 200 keeps the socket for the next POST; the 204 closes with a
  mutual close_notify exchange; options helper/badge unit tests; removed the
  close-per-response and drain-wait tests. SW cache otaman-v181.
2026-09-17 09:25:23 +03:00
catarrh f4462c14e7 scp81: UI framing options, permissive TLS, drop the Apache-header mimicry (v2.2.14)
Listener Options block (applied at Start, persisted in localStorage, Reset to
defaults): HTTP framing - chunked body, chunk size (0 = one TLS record),
keep-alive, Connection header, compact headers, Next-URI (unchecked = omit);
script framing - indefinite/definite Command Scripting template, CR tag,
targeted app; link events (now parsed on the common start path, so every mode
honors them). scp81OptionsFromForm() is unit-tested.

TLS is automatic: 'auto' (min 1.0, max 1.2 + :@SECLEVEL=0) is the new
default, all six PSK suites are offered and OpenSSL negotiates the highest;
the negotiated version/cipher is logged (tls-handshake) and reported as
version_seen/cipher_seen in /api/scp81/status, and a handshake failing for a
TLS/cipher reason logs tls-handshake-failed (post-handshake record errors
stay tls-error). tls_version/cipher/keylog/answer_delay stay as API-only pins.

Dropped the Apache-style header mimicry completely: no Date/Server/
X-Powered-By, no Content-Length-before-Content-Type ordering, no Content-Type
on 204 - the minimal response set is X-Admin-Protocol (+ X-Admin-Next-URI /
Targeted-Application), Content-Type on 200s, and Transfer-Encoding or
Content-Length per the chunked flag. Docs, help (EN/RU), READMEs and the
AGENTS notes updated; SW cache otaman-v180.
2026-09-17 08:50:40 +03:00
catarrh 404fdf5a1c scp81: rename passthru to redirect, add a true passthru mode (v2.2.13)
The former 'passthru' mode is now 'redirect': it pins the configured target
and every BIP channel is connected there (the card's requested address is
only logged; host/port required). The name 'passthru' now means the new mode:
no listener and no target - each channel dials the destination the card
requests in OPEN CHANNEL (Other address + Transport level port, TCP client,
remote, 0x02 only). The specs define no default port (TS 102 223 8.59), so an
incomplete or non-TCP request fails the channel with result 3A and an
open-fail log reason.

BipTerminal gains a mode (enable(host, port, mode=...), open(..., proto=...)),
reports it in status(), and the control API/status expose redirect (target)
and passthru (per-channel targets). The PWA mode selector shows four modes
with per-mode notes and disables Host/Port in passthru; the status line shows
each channel's actual target. Docs, help (EN/RU), READMEs and the AGENTS notes
updated; SW cache otaman-v179.

Breaking API change: mode:'passthru' has the new semantics - use
mode:'redirect' for the previous behavior (no alias).
2026-09-17 08:17:28 +03:00
catarrh b02f28f6ce docs: help restored to the UI (Cards/SCP81 sections), READMEs updated
The help pages had no SCP81 section and documented Cards as a 3.2 subsection
of the SCP80 tab. They now mirror the UI: 3 SCP80 (3.1 Secured Packet, 3.2
RAM), 4 Cards tab, 5 SCP81 tab (5.1 Listener, 5.2 Scripts), with the
following sections renumbered (6 Card reader, 7 Profiler, 8 Phone simulator,
9 Server, 10 Compatibility) and the TERMINAL PROFILE range corrected to bytes
1-39 (TS 102 223 5.2 + TS 31.111 5.2). The SCP81 tab and its sub-pills set
their own help anchors; a new test asserts every help anchor used by the UI
exists in help.html. READMEs: seven top-level tabs, SCP80 two pills, Cards
fields/buttons/SCP81 column, SCP81 modes (PSK TLS / pass-through / capture),
PSK pairs from presets, Scripts page and resume semantics. SW cache
otaman-v178.
2026-09-17 00:33:39 +03:00
catarrh eaa2453853 http ota: spec-correct retry timer, TLV and APDU edges (v2.2.12)
Audit against GP v2.2 Am.B 4.7 / TS 102 226 / GP Card Spec 11.11 found the
retry waiting delay encoded as plain hex instead of the TP-SCTS semi-octet
order required by TS 102 223 8.38 -> TS 23.040 9.1.2.3 (1 min must be 10,
20 s must be 02); the builder now encodes semi-octets, clamps 0-59 / 0-99 and
pads the 2-byte counter. Odd-length hex is padded instead of producing
fractional BER lengths; empty 83/84/89 sub-TLVs are omitted (lengths are 1-n
per Tables 4-3/4-5/4-8..10) and an empty trigger becomes 81 00; a store
payload above a short APDU is chained as P1.b8=0 STORE DATA blocks (P2 =
block number). Connection presets fixed: device identities 82, alpha 05,
command details 81, bearer 35/03; the A5 store tag and the 'B0,00=unlimited'
counter hint are marked unverified (not in the pinned spec). Docs:
UICC_SPECS.md 9.6.5 example annotated 2 s, findings 10-minute timer corrected
to 1 minute; help updated. SW cache otaman-v177.
2026-09-17 00:23:56 +03:00
catarrh dd7eecb279 custom files: update the section comment to the new parent rules 2026-09-16 23:48:44 +03:00
catarrh b0b8d02fe7 custom files: any DF parent path at any depth (v2.2.11)
The parent field was a select over custom DFs only, so a standard DF (e.g.
MF/7F20) could not be a parent and canonical entries under one were dropped
on import. It is now a free-text path with suggestions from the root, custom
DFs and DFs of the loaded tree (any depth); a bare FID chain is completed
from the root selector. Validation accepts standard or not-yet-seen parents
(flagged in the list), rejects a parent known to be an EF, and import keeps
such entries. Custom-file edits re-inject the loaded tree: new nodes appear
at once, renamed model nodes are restored and injected nodes removed when
their entry goes away. Help updated (the form lives in Profiler, not Card
reader). SW cache otaman-v176.
2026-09-16 23:47:30 +03:00
catarrh 0698c9e90c terminal profile: move Cancel/Apply to the top of the dialog
The actions sat below the note line and the (long, 45vh) bits grid, so they
needed scrolling to reach. They now sit to the right of the preset selector
and hex field, aligned with the preset row; the bit grid is unchanged.
2026-09-16 23:26:51 +03:00
catarrh c734b46731 file manager: tree height fits the free viewport space (sw otaman-v175)
The tree box was capped at a fixed 65vh, which ignored the chrome above it
(header, tabs, status, sub-tabs, sort/probe row), so the page itself grew a
scrollbar once the tree hit its cap. pysimFsFitTree() now caps it to
innerHeight - top - 12px at runtime (the inline 65vh stays as the no-JS
fallback), re-run on resize, on render, on sub-tab/tab entry and when the
probe status line appears.
2026-09-16 23:26:47 +03:00
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 ef8b651f28 scp80: keep the card preset selected after a send (v2.1.18)
cardsSave() calls cardsRebuildSelect(), and the innerHTML rebuild reset the
dropdown to its first option ("- Select card -"). Since the counter sync
saves after every send, the selection was lost each time and had to be
re-picked manually. The rebuild now restores the previous selection
(guarded by the preset still existing). Service worker v155.
2026-09-16 08:44:44 +03:00
catarrh e957757fda scp81: BER length for the Response Scripting template TLVs (v2.1.17)
The R-APDU TLV length was read as a raw byte, so a listing page above 127
bytes (AF 80 23 81 FC <252 bytes> 00 00) was cut to its first 127 bytes with
a bogus status word (the data's last two bytes: CAFE/0001/9F70 instead of
the real 63 10 "more data"). The bogus SW also stopped the SW CAFE/6310
pagination, which is why later registry entries - e.g. the installed package
AA1902BC225801 - never showed up. Uses httpota.ber_len_read now.

Tests: 250-byte long-form page and short-form regression (204 python);
findings updated; service worker v154
2026-09-16 08:29:00 +03:00
catarrh c341571300 scp81: correct GET STATUS pagination and registry P1s (v2.1.16)
- continuation repeats the SAME GET STATUS command with P2.b1 set (the
  pagination state lives in the card); changing the 4F criterion is a match
  filter, not a position - P2=03 with the last AID is rejected with 6A80 and
  P2=02 with it returns that single match (the earlier duplicate)
- handle the standard "more data available" warning SW 63 10 (Table 11-38)
  in addition to the live card's proprietary CA FE
- explore script: P1=40 is applications+SDs, P1=20 the ELF registry, P1=10
  ELF+modules (Table 11-33) - the ELF-only registry was never queried, which
  hid the installed package; labels and the results decoder show C4 (ELF AID)
  and CC (SD AID) too
- UICC_SPECS.md: GET STATUS P1/P2 tables made explicit with the pagination
  rule, plus BER length coding notes for the scripting templates and the
  TS 102 223 channel data TLV (the two >127-byte traps)

201 python + 346 frontend; service worker v153
2026-09-16 08:22:59 +03:00
catarrh 3403abd6b9 scp81: continuation pages use GET STATUS P2=03 (next occurrence) (v2.1.15)
P2=02 means "get first or all occurrence(s)" (Table 11-34), so every
continuation re-returned the first listing (the criterion's single match),
pagination stopped after one extra page and the installed package
AA1902BC225801 never showed up in the ELF registry. P2=03 = "get next
occurrence(s)" is the correct value for the SW CAFE continuation.

Tests updated with the new continuation bytes; findings documented;
service worker v152
2026-09-16 08:12:36 +03:00
catarrh b811906751 scp81: queue explicit commands, expanded-format output for the RAM chain (v2.1.14)
- POST /api/scp81/queue takes an explicit APDU list (or single APDU) and
  queues it as the SCP81 script; entries that already are Command Scripting
  templates (AA.../AE80..., the expanded format) are sent verbatim instead of
  being wrapped again
- Remote APDU -> RAM chain: "To expanded" builds each command in the
  TS 102 226 expanded form (AA definite / AE80 indefinite selector) and
  "Queue in SCP81" queues the built commands for the next card POST, so the
  full-featured RAM/INSTALL [for install] form (AIDs, privileges, TK/STK
  parameters) can drive the HTTP OTA install
- RU strings; api.md; service worker v151
2026-09-16 08:05:57 +03:00
catarrh e32a6e17e4 scp81: expose install/STK parameters for the RAM install (v2.1.13)
The SCP81 install row now carries the same fields as the SCP80/RAM form:
install parameters (default C900), STK parameters (CA TLV) and make
selectable. An UICC applet install can require STK parameters; without them
the card answers INSTALL [for install] with SW 6A80 (incorrect parameters in
data field). RU strings added; service worker v150.
2026-09-16 07:57:59 +03:00
catarrh ff7a1ad5d3 scp81: fix RAM LOAD block splitting (overlapping 1-byte shifts) (v2.1.12)
The block slicer used the block number as a character offset
(loadfile_tlv[i * 2:(i + 240) * 2]), so every LOAD block after the first was
a 1-byte-shifted copy of the previous one - the cap header repeated every
239 bytes on the wire. A live install accepted three blocks, failed block 4
with SW 6400, then 6985, and INSTALL [for install] answered 6A88. The same
slicing was inherited by the SCP81 helper from the SCP80 path, so
multi-block caps could not install there either; both are fixed.

Tests: blocks are consecutive and reassemble the C4 TLV byte-for-byte
(200 python); findings updated; service worker v149.
2026-09-16 07:51:59 +03:00
catarrh 92f646df08 scp81: BER-encode the Command Scripting template lengths (v2.1.11)
The C-APDU TLV length was written as a raw byte: a 245-byte LOAD command
produced 'AE 80 22 F5 ...', which BER reads as a long-form marker, so the
card mis-parsed every script command over 127 bytes. Small commands worked,
which made a RAM install look alive: the card answered the LOAD steps with a
degenerate 'AF 80' body (no R-APDU), and the final INSTALL [for install]
failed with 6A88 because the package never loaded.

Both the indefinite ('22' TLV) and definite ('AA' outer) lengths are now
BER-encoded (same rule as the BIP channel data TLV fix). Tests: 245-byte
LOAD body, short form, definite variant (200 python, 346 frontend);
findings doc updated; service worker v148.
2026-09-16 07:45:14 +03:00
catarrh f411f12c56 scp81: results panel polish - labels, dedupe, 0085 decode (v2.1.10)
- each result group shows the command label (GET DATA FF21, GET STATUS
  P1=80/40/10, GET DATA 0085, INSTALL/LOAD)
- GET STATUS pages deduplicate by AID: the continuation page re-includes its
  search criterion, which made the last entry of every listing appear twice
- the GET DATA 0085 answer is decoded (host/agent/uri, PSK identity +
  KVN/KID from the unframed [14][id][02 KVN/KID] security TLV, retry counter
  and timer, connection block with APN and destination address) instead of a
  truncated hex dump; undecodable results show the full hex now
- tests: admin-params decode with the live sample, command labels
  (346 frontend, 196 python); service worker v147
2026-09-16 07:35:11 +03:00
catarrh 4dd09740b7 scp80: advance the counter after every send and persist it to the preset (v2.1.9)
The counter only advanced when a PoR came back with status por_ok, so
PoR-less sends kept reusing the same counter (the card rejects a repeated
counter for replay protection). It now advances on every successful send
(and shows "CNTR -> ..." in the result line), and the new value is written
into the selected card preset via spCntrSyncPreset(). Manually edited
counters are persisted too (the field's onchange). New spNextCntr() helper
with carry tests; service worker v146.
2026-09-16 07:30:21 +03:00
catarrh 3463a68292 scp81: RAM install over HTTP OTA (v2.1.8)
- shared _cap_apdu_sequence helper (INSTALL [for load] -> 240-byte LOAD
  blocks -> INSTALL [for install]); the SCP80 /api/ram-install path now uses
  it too (one source of truth; byte-level tests pin the APDUs)
- POST /api/scp81/ram-install: parses the .cap server-side and queues the
  APDU sequence as the SCP81 command script (one C-APDU per POST, runs on the
  card's next push); refused while a script is mid-run unless force
- /api/scp81/script reports the script kind (explore/none/custom/ram-install)
- tab: RAM install row (CAP file + SD AID + Queue button); RU strings
- docs: api.md, findings, AGENTS; service worker v145
2026-09-16 02:05:20 +03:00