SET UP MENU / SELECT ITEM items carry an optional Items Next Action Indicator
(TS 102 223 8.24, tag '18'): one byte per item, in list order, coded with the
Table 9.4 values marked "Used for Next Action Indicator". The decoder ignored
it, so neither the proactive log nor the STK menu showed what the card would
do when an item is selected.
- server: NAI_TYPES whitelist (the ToC-only values - e.g. '26', '27', '47' -
are reserved there and are ignored, as are '00' and unlisted values),
_nai_name(), _attach_nai() (a short NAI list leaves the tail without an
indicator, extra bytes are ignored); _parse_select_item() and
_parse_setup_menu_items() plus the TERMINAL PROFILE SET UP MENU walk attach
nai/nai_name to the items; _decode_cmd() renders '1. Menu -> SET UP MENU'.
- PWA: stkMenuNaiSuffix() adds a small gray '<name>' suffix to STK menu items
that carry an NAI; the proactive log picks the decode up via cmd_decoded.
- tests: SET UP MENU / SELECT ITEM vectors with NAIs, a reserved value that is
ignored, a short NAI list, and the stkMenuNaiSuffix unit test.
- docs: UICC_SPECS 6.5 gained the '18' Items next action indicator row
(8.24/9.4); help EN/RU mention the menu suffix; AGENTS files updated.
548 frontend / 413 Python green; version 3.5.3; sw cache simple-v255.
Three fixes found while filling the ETSI/3GPP registry gaps against the
SIMalliance Stepping Stones R7:
- SCP80 builder/verification parity (TS 31.115 Table 1 NOTE / 4.2 / 4.3):
the CPL is now transmitted whenever the packet is ciphered or carries
RC/CC/DS - it is part of their input - and whenever the packet needs SMS
concatenation; a single unprotected SM keeps pySim's CHL-first form.
Before, the JS dropped the CPL for every unciphered packet while the
server reference re-added it, so "Verify vs pySim" reported a false
MISMATCH for every unciphered RC/CC packet (SPI1 01/02/0A/12/1A...).
All ten offered SPI1 values now match the server reference byte-for-byte.
- RC (SPI1 b2b1 = 01) was offered but not built: the JS now computes CRC-32
(TS 102 225 5.1.3.2, pySim zlib.crc32 parity) over the same CPL frame as
the CC; the packet no longer silently omits the 4-byte RC field.
- Server: _build_secured_packet/_ota_reference add the CPL to a concatenated
unprotected packet too (Table 1 NOTE / 4.3).
- fcpLifeCycle: unlisted values with b8 clear are RFU, b8 set is proprietary
(Table 11.7b); previously all unmatched values were labelled proprietary.
- EF.ARR decoder now decodes the expanded format (AM_DO/SC_DO per ISO 7816-4
5.4.3.2 + TS 102 221 9.2.7): operation bit masks, INCREASE/RESIZE AM_DO
0x84, OR/AND/NOT templates, PIN key references with usage qualifiers.
- FCP 'C6' PS template DO decoded (PS_DO bitmap + key references + usage
qualifiers, TS 102 221 11.1.1.4.10/9.5.2) with a shared key-reference map.
Tests: sp.test.js (CPL/RC vectors + crc32 known answer), ef_decode.test.js
(expanded-format ARR vectors), profiler.test.js (LCSI RFU/proprietary, C6),
test_ota_helpers.py (RC reference, unprotected single-SM vs concatenated).
Help EN/RU and READMEs: CPL size 2 (SMS), RC/CC/DS 4-8, RC bullet, CPL rule.
547 frontend / 397 Python green; version 3.5.1; sw cache simple-v253.
Packets longer than one SMS now go out as concatenated SMS-PP downloads
per TS 31.115 4.3 and the UI shows how many SMS a packet needs.
Server:
- `_split_secured_packet` cuts the command packet at the exact SMS
user-data capacities (first SM 132 octets: concat IE 5 + CPI IE 2;
following ones 134; a single-SM packet may be 137 with the CPI IE) and
`_build_sms_tpdu` tags every segment with the fixed concatenation
reference 01; `_build_sms_tpdu` also enforces the 140-octet budget.
- `_send_secured_packet` (shared by /api/send-ota and /api/ram-install)
sends one ENVELOPE per segment in order, refuses more than
MAX_ENVELOPE_SEGMENTS (5, the card's concatenation buffer) and reports
`bytes`/`segments` in the response (RAM install per step as well).
- `_build_secured_packet`/`_encode_cmd_unlimited`: our own TS 102 225
5.1.1 encoder on pySim's keyset/header constructors, byte-identical to
pySim for packets <= 140 octets (tests) and not limited to one SMS
(pySim refuses the longer ones, which is why they never went out).
- RAM LOAD blocks are no longer clamped to one SMS: 1-240 bytes of
payload with the default 240 (the GP maximum); `load_block_size_auto`
replaces `load_block_size_clamped`.
PWA:
- `scp80SegmentInfo` / `spSizeInfoText` show "N bytes . M SMS
(concatenated)" under the packet field, turn red past 5 SMS and report
size/SMS in the send result and the RAM step log; LOAD block hints and
placeholders updated; EN/RU.
Tests/docs: Python +2 cases incl. segment order/capacities and byte
identity with pySim; Node drift guard against the server constants and
UI text tests; README/README_RUS, help EN/RU, docs/api.md, AGENTS.
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.
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.
- 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
- 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
Incoming: _parse_sms_concat parses UDH for IEI 0x00 (8-bit ref) and
IEI 0x08 (16-bit ref). PoRSubmitHandler accumulates segments, sorts by
num, and reassembles complete payload when all parts arrive.
Outgoing: MAX_ENVELOPE_SEGMENTS=5 limits the secured packet to 5
ENVELOPE chunks (650B max at 130B/chunk).
10 new tests for concat parsing and reassembly.
- pysimSendOta: Response Parser filled strictly from decoded PoR
(por.decoded) for both SPI2 variants; envelope SW/failures no longer
leak into it — they render in a new inline result line (#sp-send-result)
next to Send to Card, always including SW on failure
- Default-level stderr tracing per send (no flags needed):
'OTA SEND: SPI .. KIc .. KID .. TAR .. CNTR .. LEN ..B CHUNKS N',
'OTA SEND FAILED: chunk N SW xxxx' and
'OTA PoR[envelope|sms-submit]: status=.. TAR=.. CNTR=.. PCNTR=..
RPL=.. RHL=..' (+ compact summary / undecodable raw / none fallbacks)
- _decode_por: surface every parsed PoR field verbatim (cntr, rpl, rhl,
cc_rc, raw) instead of status/tar/pcntr only
- genSp: CNTR normalization padEnd -> padStart so short input like '1'
becomes 0000000001, not 1000000000 (counter is big-endian 5 bytes)
- tests: TestDecodePor completeness + cntr_low field-report cases
Version 1.9.3 -> 1.9.4 everywhere; SW cache otaman-v14 -> otaman-v15
Server:
- Track all TERMINAL RESPONSEs (chain, TP, pySim auto-handler, menu-respond
link to paused entry) with shared _build_tr/_record_tr
- Log entry ids + cmd_num; server-side decode of fetched commands and TRs
(compact PLI port); Result CTLV extracted as tr_result/tr_result_name
- _LoggingApduTracer captures TR SW for pySim auto-handler responses
- _DefaultProactiveHandler answers PLI with editor data (no re-request)
- VERSION 1.8.0
Frontend:
- Expandable proactive log rows (click to expand, state survives re-render)
- Response section: Result name + hex, decoded fields, width-restrained
readonly hex inputs; no SW display
- Rename Expanded Script tab/pill/button/docs; proactive UICC view font
sizing increase; card reader pills text-sm