- 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
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
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
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
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
- 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
- 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
- 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)
- 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)
- 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
- 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
- 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
- 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
Live-testing against a UICC without the EPS files (no USIM service 85)
and without the Kc files showed the runner aborting when EPSLOCI/Kc were
absent. write_binary/write_record now take an 'optional' flag: a missing
candidate file is logged as a skip step instead of failing the scenario
(store_epsnsc, real/dummy locations, churn and Kc writes). Also allow
POST /api/write to select by 'path' like /api/select and /api/read (the
netsim live test needed it to restore the captured file values).
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
- 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)
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)
- 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
- 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.
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.
- 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.
- 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.
- __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.
- 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.
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).
- 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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
explore with the fixed response parser ran 18 commands / 10 continuation
pages (real 63 10/9000 statuses) and shows the installed applet:
AA1902BC22580101 (life=07 selectable, elf=AA1902BC225801) in the
applications listing, AA1902BC225801 (life=01) in the ELF registry and with
its module in the ELF+modules listing.
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
- 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
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
INSTALL [for load] -> LOAD x6 -> INSTALL [for install] (SW 9000) against the
live card. Records the parameter-less 6A80 finding and the working
INSTALL [for install] command built from the Remote APDU -> RAM form and
queued via "Queue in SCP81".
- 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