daaddf21cbcd63500b34fc88f07766a9ca9c6d1b
415 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
daaddf21cb |
fix: install-form grant hygiene + correct 82 access coding (v3.6.12)
Live findings (2026-09-28): a browser-restored Receipt Generation privilege bit (third byte, ISD-only per GP Table 6-2) was silently sent and produced 6985, and the '82' access entries were missing the mandatory "Length of Access Domain DAP" byte, so the card rejected the ADF.USIM entry with 6A80. - ramResetGrants() clears the privilege / toolkit-enable / file-access checkboxes and refreshes the aggregates on load; ramInstallCap re-derives the privileges from the checkboxes at send time - no stale or browser-restored grant can be sent. - the privileges aggregate emits 1 or 3 bytes, never the invalid 2-byte form (GP Table 11-43), in both updateRcPriv and the chain's computePriv. - '82' entries carry the DAP-length byte: '00 01 00 00' (shared FS) and '<len> <ADF AID> 01 00 00' (ADF); the ADF AID is editable (rc-tk-adfaid, default A0000000871002 = ADF.USIM, 5..16 bytes enforced). - tests: ram_grants.test.js (aggregate forms, the send-time derivation, the load-time reset) and the updated access-parameter shapes in stk_params.test.js. 638 frontend / 496 Python green; version 3.6.12; sw simple-v285. |
||
|
|
05c14b42dd |
feat: UICC file-access parameters in the install form (v3.6.11)
SIM toolkit applets installed while UICC-library applets failed at INSTALL [for install] with 6F00 - including with the reference tool's exact install parameters. The asymmetry: the SIM (CA) path grants file access via the Access Domain field (default 00 = full access), while the UICC (EA) path sent no '82' (UICC Access Application specific parameters) at all, and the reference's '82 00' is empty. An applet importing uicc.access (the failing CAP has 2 refs) can then fail inside its install(). - EA mode gains two checkboxes (RAM install form + the chain rows' toolkit block): "File system access (full)" appends '82 03 00 01 00' (shared file system + Access Domain Parameter 00 = full access, TS 102 226 8.2.1.3.2.2.2/8.2.1.3.2.5); "ADF.USIM access (full)" adds '07 A0000000871002 01 00' to it. Both default off. - tests: the access TLV shapes (with/without the ADF entry), the form -> hex path with the checkbox, and the toolkit-field wiring count (16 fields). 635 frontend / 496 Python green; version 3.6.11; sw simple-v284. |
||
|
|
a0704a8fd0 |
fix: Explore Apps listing uses the SMS-submit PoR transport (v3.6.10)
The Apps query (P1=40) was sent with SPI2=0x01 while only the ELF queries
(P1=20/10) used SPI2=0x21. The Apps listing can exceed the ENVELOPE
response, and the card then answers the envelope PoR with
`actual_response_sms_submit` - the actual response would follow as an
SMS-SUBMIT, which is never sent unless the PoR is requested in submit mode.
The paginate only accepted `por_ok`, so the Explore reported the raw status
as an error ("Partial - Apps: actual_response_sms_submit") and the Apps list
never rendered (live 2026-09-28).
- `ramListingSpi2(p1)`: 40/20/10 -> '21', else '01'; the paginate uses it.
- the paginate accepts `actual_response_sms_submit` via `spPorAccepted` and,
when no data arrived, notes "response via SMS not captured" instead of
reporting the card status as a failure.
- the Explore delete verdict follows the same acceptance rule (an accepted
SMS-submit delete no longer shows "Failed" while its counter advanced) and
no longer prints a dangling " -> " without a SW.
- tests: ramListingSpi2 + the delete-accepted-via-sms-submit flow.
633 frontend / 496 Python green; version 3.6.10; sw simple-v283.
|
||
|
|
9fb2ad5d2b |
fix: send the STK install parameters as entered (v3.6.9)
The RAM install form sent the `STK parameters (hex)` field, which was only regenerated when the toolkit checkbox or the mode select changed - editing the TAR (or any other toolkit field) afterwards was silently dropped. The live install therefore carried the form defaults (TAR B00001, textLen 0, menus 0, MSL 16, channels 0) instead of the entered AF4D01 / MSL 12 / channels 1, and the card rejected the install parameters with 6A80 (TS 102 226 8.2.1.3.2.7: a TAR already assigned on the card). - every `rc-tk-*` field regenerates the hex on input/change; the install recomputes it at send time unless the user hand-edited the hex (`dataset.manual`), so the edit order can no longer drop values; - one pure `stkParamsBuild` serves the RAM form (`buildRcToolkitParams`) and the RAM/GP chain rows (`buildTkParams`) - no drift between them (the chain row also gains the menu-ID <= 7F check); - applet TAR field: empty by default, no placeholder - B0 00 01 is the allocated ADF RFM TAR (TS 101 220 Annex D), not an applet TAR; an empty field is coded as TAR length 00 (the card may take a TAR from the AID only when the AID carries one, PIX hex digits 15-20); - the chain builder's INSTALL/LOAD rows drop the trailing Le (the v3.6.8 server form; a trailing Le made the card execute a phantom command); - tests: stk_params.test.js (the decrypted live parameters, empty-TAR coding, CA wrapper, long-form lengths, rejection cases, form wiring, and the form -> hex path with the real builders) and ram_counter_flow.test.js (the Explore delete persists the consumed counter through the real ramSaveCntr); STK fixtures no longer use B00001. 631 frontend / 496 Python green; version 3.6.9; sw simple-v282. |
||
|
|
29864617eb |
fix: CAP install APDUs match the reference terminal form (v3.6.8)
INSTALL [for load]/LOAD/INSTALL [for install] carried a trailing Le and an explicit ISD AID in the Security Domain field; the card executed the extra byte as a second (phantom) command, so the compact response reported count=2 with SW 6700 - which the v3.6.2 remote-SW check correctly treated as a failure, aborting at step 1. Decrypted from the live trace (KIc/KID 25/25, 3DES): ours was 80E6020014 07F0414C46416101 08A000000003000000 00 00 00 00 the reference tool sends 80E602000C 07<aid> 00 00 00 00 (empty SD, no Le) and its response is count=1 / 9000 while ours was count=2 / 6700. - `_cap_apdu_sequence`: the SD AID is only sent when a custom one was supplied (empty -> '00', the card defaults to the ISD; GP 11.5.2.3.1 Table 11-42) and all three RAM install APDUs are case 3 (no Le). - tests: the expected INSTALL bytes updated, a no-Le assertion for every APDU in the sequence, and a custom-SD-AID case. 615 frontend / 496 Python green; version 3.6.8; sw simple-v281. |
||
|
|
50fa8744fd |
fix: keep the preset counter intact through the Explore delete flow (v3.6.7)
Delete All worked but reset the preset counter: the handler never re-read the preset (unlike ramExecute), saved N+1, then re-ran the Explore with the OLD sp (counter N). Every page answered cntr_low, nothing was accepted, and ramExplore's final ramSaveCntr wrote N back over the preset. - ramDeleteFromExplorer re-reads the preset first (spRefreshFromPreset 'ram-card-sel'), advances and saves the counter only for a packet the card accepted (spPorAccepted) - a rejected packet leaves it untouched - and continues the follow-up Explore from the consumed counter instead of replaying it. - the delete result also checks the remote command's SW via a new `ramRemoteSwOk` (9000 / 61xx / 62xx / 63xx / CAFE, mirroring the server's `_ram_remote_sw_ok`): a por_ok packet whose DELETE was refused (e.g. 6A88) no longer shows "OK" (the counter still advances, the card consumed it). - ramExplore only saves the counter when it advanced, so a stale caller can never lower a preset counter again. - tests: flow tests for accepted / rejected / refused-remote-SW deletes (refresh order, saved counter, the counter passed to the re-explore) and the ramRemoteSwOk set. 615 frontend / 495 Python green; version 3.6.7; sw simple-v280. |
||
|
|
c39250f1b4 |
fix: make the Explore Delete buttons send the GP DELETE APDU (v3.6.6)
ramDeleteFromExplorer() called an undefined `_ber_len()` helper, so clicking Delete / Delete All raised a ReferenceError right after the confirm and no APDU was ever sent. The inline builder also omitted the mandatory Le byte. - new pure `ramDeleteApdu(aid, withCascade)` = GP Card Spec v2.3.1 Table 11-20/23 form: `80 E4 00 <p2> <Lc> 4F <len> <AID> 00` (P2 00 = object, 80 = object and related objects), used by the Explore handler. - `scp81DeleteApdus()` (the "Delete AID" script template) now shares the same builder: it used to send the raw AID without the mandatory '4F' TLV. - tests: exact bytes for 7/16-byte AIDs and both P2 modes, the SCP81 script expectations updated to the TLV form, and a wiring check that the undefined helper call does not come back (`_ber_len(`). 611 frontend / 495 Python green; version 3.6.6; sw simple-v279. |
||
|
|
40f20d539e |
fix: decode the Response Scripting template and the compact listings (v3.6.5)
Explore still missed the F0414C46416101 package on a card whose responses wrap the R-APDU in the TS 102 226 5.2.2 Response Scripting template (`AB <len> 80 <count> 23 <len> <R-APDU>`): `_decode_por` parsed that as a compact response, so the frontend got `last_status_word` 81d0/7680 and data starting `80 01 01 23 ...` instead of the listing. - server: `_parse_response_scripting()` (AB definite / AF 80 ... 00 00 indefinite) extracts the executed-command count and the last R-APDU's SW and data; `_decode_por` exposes it as `response_type: scripting` in the same `decoded` shape as compact, so the RAM explore paging and the RAM install `por_sw` see the real 9000/6310. - frontend: the compact listing walk is deterministic on the AID length (`len AID life ver`; P1=10 adds `module_count (len module_AID)*`). `_parseRawAppEntry` no longer guesses `rawLen-1` for >8-byte AIDs (16-byte A113 applet AIDs were truncated), and `_parseRawElfEntry` no longer scans for `0x10` (a length/AID byte equal to 0x10 derailed the walk: a live page parsed to 1 entry with no F0414C46416101). - tests: exact trace fixtures - the ELF page lists F0414C46416101 (11 entries), the P1=10 page attaches its F0414C4641610101 module, the app page keeps the 16-byte A113 AIDs; Python covers the scripting template (definite/indefinite) against the live `AB 12` ISD and listing vectors. 610 frontend / 495 Python green; version 3.6.5; sw simple-v278. |
||
|
|
9e85f522f6 |
fix: recover the SMS-SUBMIT listings in RAM Explore (v3.6.4)
Explore returned partial data (no ELF/module entries, the installed package
missing) and then failed with cntr_low. Two causes, both in the
actual-response SMS-SUBMIT path the card uses for big listings:
- `_find_sms_tpdu` read TLV lengths as a single byte; the FETCH carries
`8B 81 97 ...` (BER long form) for the big pages, so it returned a
corrupted, truncated TPDU.
- `_calc_ud_offset` treated the SMS-SUBMIT relative validity period (VPF=10)
as 7 bytes instead of 1, shifting the UD offset: `_parse_sms_concat` then
read a bogus UDH and reported no concatenation, so the segments never
assembled and the PoR/data was dropped ("RAM RESPONSE-PACKET: empty").
The step was marked failed, the counter did not advance, the same counter
was retried and the card answered `cntr_low`, which also blocked P1=10
(modules) where the installed package appears.
- the captured segments now always store the assembled UD (`submit_ud_hex`);
`_sms_submit_por()` rebuilds the DELIVER-style packet (`02 71 00` + UD) for
`_decode_por`. Verified against the live capture: por_ok, remote SW 6310,
438 hex chars of listing data (the exact bytes of the P1=20 page).
- `spPorAccepted` counts `actual_response_sms_submit` (0x0B) as accepted, so
the counter advances when the data follows via SMS-SUBMIT.
Explore queries follow GP Card Spec v2.3.1 11.4.2.2: compact listings
(P2.b2=0) with the chained GET RESPONSE (`ramGetStatusApdu`, paging P2=00 ->
P2=01), the malformed P2=02 attempt is gone, and the ISD-only query (P1=80)
never pages with next-occurrence (the card shall reject it).
Tests: Python SmsSubmitCaptureTest with the live FETCH bytes (TPDU length,
UD offset, concat reassembly, 027100+UD decode); ram.test.js checks the APDU
builder and the missing P2=02 attempt; cards_counter covers 0x0B.
608 frontend / 492 Python green; version 3.6.4; sw simple-v277.
|
||
|
|
39c82f26f3 |
fix: SCP80/RAM re-read the preset before every operation; no counter bump on a rejected send (v3.6.3)
The SCP80/RAM forms hold a copy of the preset (counter, keys, TAR, SPI). Editing the preset on the Cards tab saved correctly, but the form kept the old copy: the operation sent the stale counter (the card answers cntr_low) and the post-send sync wrote the stale value back over the preset - the saved counter silently reverted. Reproduced in a real DOM: after select in SCP80: preset=0000000001 sp=0000000001 after Cards edit+save: preset=00000000AA sp=0000000001 after a sync: preset=0000000001 (edit lost) - `cardsApply()` split into `cardsApplyFields()` (field copy, no packet) and `cardsApply()` = fields + genSp; new `spRefreshFromPreset(selId)` re-reads the selected preset from `sp-card-sel` / `ram-card-sel` and re-applies it. - `pysimSendOta()` and `ramExecute()` call it before starting, so every SCP80/RAM operation uses the preset as it is now. - A rejected send no longer advances the counter: new `spPorAccepted(por)` gates the advance+write-back in `pysimSendOta`, the Explore pagination and its GET DATA step, and the server's RAM install (`_ram_next_cntr`: advance only for `por_ok`/`no_por` steps). A failed install still returns `final_cntr` (the accepted prefix) and the PWA persists it, so a retry never replays a counter the card already consumed. - tests: cards_counter.test.js (the stale-form regression, RAM selector, fields-without-genSp, spPorAccepted) and `_ram_next_cntr` cases; the cards_form/ram harnesses updated for the split. - docs/api.md counter semantics; AGENTS preset-source-of-truth rule. 607 frontend / 488 Python green; version 3.6.3; sw simple-v276. |
||
|
|
36d2f71bc7 |
fix: report the real PoR/remote-SW result of every RAM install step (v3.6.2)
The RAM installer read `por['decoded']['response_status']`, but the compact
response decoder's `decoded` only carries {number_of_commands,
last_status_word, last_response_data} - so the suffix was always empty and
every step showed the useless `por_error_`, even when the PoR was por_ok.
The PoR verdict is the top-level `response_status`.
With that fixed, the decoded details also show that the remote command's own
status word was the real verdict all along: a captured live install returned
por_ok with `last_status_word` 6700/6F00 (the card rejected every RAM APDU)
while the installer reported success.
- new `_ram_step_result()`/`_por_remote_sw()`/`_ram_remote_sw_ok()`: a step
fails on a non-por_ok PoR, on a remote SW outside the success set (9000,
61xx more data, 62xx/63xx warnings, CAFE GP "more data"), or on an
undecodable PoR (a 9000 transport SW with no PoR at all is `no_por`, not a
failure). A decoded 61xx is explicitly success, per GP/ISO.
- step records gain por_sw/por_type/por_cntr/por_data/por_raw and
`por_error`; the response carries `failed_step` and a detailed error such
as `LOAD (1/9): remote SW 6700`; the log line now prints
`status=por_ok remote_sw=6700`.
- PWA: new pure `ramStepLine()` renders e.g.
`❌ Шаг 2: LOAD (1/9) — PoR ok · remote SW 6700 (Wrong length in Lc) · 274 B / 3 SMS`
(SW meaning via the existing lookupSw decoder) and the transport SW only
when it is not 9000.
- tests: tests/test_ota_helpers.py RamPorStepTest (success set incl. 61xx,
the captured 6700 failure, no-PoR/undecodable, expanded responses);
ram.test.js ramStepLine cases.
- docs/api.md RAM install step fields + failure semantics; AGENTS updated.
603 frontend / 487 Python green; version 3.6.2; sw simple-v275.
|
||
|
|
b92a47cd44 |
fix: close the eSIM panel so the Test script pill shows its panel (v3.6.1)
#phone-sub-esim was missing its closing </div> before #phone-sub-test, so the browser parsed the test panel (and everything after) as a child of the eSIM panel. Clicking Test script unhid the panel itself, but its hidden ancestor kept it invisible - the pill has never shown a panel in a real browser since it was added (v3.5.11). The eSIM LPA pill conversely showed the whole test editor below its own fields. The old whole-file div-count test stayed green because the inline script's template strings happen to contain one extra </div>; the real markup was 409 opens vs 408 closes. - add the missing </div>. - html.test.js: count divs on the markup only (scripts excluded) and add a structural walk (stray end tags, cross-nesting, unclosed elements, HTML optional end tags honoured) over index.html, help.html and help-ru.html, plus a Simulator-panel sibling-depth check. Run this for every HTML edit. - the walk immediately found the same class of bug in both help pages: six unclosed <section> chapters each; closed them. - AGENTS: the HTML structure test is now part of the working conventions. 602 frontend / 482 Python green; version 3.6.1; sw simple-v274. |
||
|
|
18a36a8f27 |
feat: generic BIP terminal control (Simulator BIP pill) (v3.6.0)
BIP was only reachable through the SCP81 listener; cards that use TCP for
other purposes (an applet's OPEN CHANNEL, a push trigger without HTTP OTA)
now get a generic control surface, independent of SCP81.
Server:
- /api/bip/control starts a plain BIP session in three modes: sink
(default; a local TcpDumpServer that accepts the card's channels and only
logs conn/sink-rx/conn-close, never answering; port 0 = ephemeral, the
bound port is reported), passthru (dial the OPEN CHANNEL destination, TCP
client only) and redirect (fixed host:port).
- /api/bip/status returns {owner, bip, listener}; /api/bip/log and
/api/bip/log-clear share the BIP event log with /api/scp81/log.
- The session state is shared with the SCP81 listener and only one session
runs at a time: _bip_session_stop() stops whichever control started it
and the control responses report it as `replaced` (both directions,
SCP81 <-> BIP). State renamed _SCP81_MODE/_TARGET/_LISTENER/_LINK_EVENTS
-> _BIP_* plus _BIP_OWNER.
- TcpDumpServer logs conn-close and counts accepted connections; stop()
joins the accept thread so the port is really free on restart.
- The new control endpoints are blocked during test-script runs.
PWA:
- New Simulator pill BIP (after TR Config): mode select with notes,
Host/Port rules, Start/Stop with a "replaced" notice, status line (mode,
bound address, owner, channels), a Channels box and the shared BIP log
(reuses the SCP81 log renderer, now showing `peer`).
- The SCP81 status line marks a session owned by the BIP pill.
Help EN/RU 8.10, docs/api.md, AGENTS; CAT_TP/UDP recorded as not
implemented.
Tests: tests/test_bip.py (9) and frontend/tests/bip.test.js (6).
600 frontend / 482 Python green; version 3.6.0; sw simple-v273.
|
||
|
|
73ef3a67ca |
ui: put the CAP report in a bordered "CAP details" container (v3.5.19)
The CAP analysis now lives in the simulator-style fieldset with the title
embedded in the border:
<fieldset id="ram-cap-info" class="hidden mt-2 border ... p-3 text-xs ...">
<legend ... data-l10n="CAP details">CAP details</legend>
<div id="ram-cap-body"></div>
</fieldset>
- Both CAP boxes (RAM installer, SCP81 Scripts) use the same bordered
fieldset + legend pattern as the simulator/eSIM/card fieldsets.
- capRenderAnalysis toggles the fieldset and writes into the body div, so
the border and title stay visible for analyzing / failed (Retry) / done
states alike.
- capMemHtml is pure content now; the inner "CAP requirements (estimate)"
heading is gone - the border legend titles the box.
- i18n: 'CAP details' -> "Детали CAP" (static legend, data-l10n).
- refreshDynamicI18n() rebuilds both CAP bodies on a language switch (the
dynamic report previously kept the old language).
Tests: the wiring test requires both legends and body divs, a new
capRenderAnalysis test covers idle/analyzing/error/ok, and the renderer
tests no longer expect the inner heading.
593 frontend / 473 Python green; version 3.5.19; sw simple-v272.
|
||
|
|
b43e3118cf |
ui: give the Requires table its own AID column (v3.5.18)
The imported-library AIDs are now shown in a dedicated column between the library name and the minimum version (Library | AID | Version | Family | Refs), monospace and break-all so the 32-char UICC/3GPP AIDs wrap cleanly on narrow panels. When a library name cannot be resolved (vendor AID) the name cell shows a dash instead of repeating the AID now carried by the column. Tests updated: the Requires slice asserts the AID header and every row's AID, and the unresolved-name case expects the dash plus the AID column. Help EN/RU reworded; the AID key is a no-op in RU. 592 frontend / 473 Python green; version 3.5.18; sw simple-v271. |
||
|
|
d37a29b389 |
ui: regroup the CAP report into Package / Requires / Memory tables (v3.5.17)
The CAP analysis box was a flat list mixing memory, package and library
data, with run-on "table-like" lines of differing lengths. Rebuild it as
a labelled report (always visible except the bulky parts, which stay in
nested disclosures):
- Package: AID (plus the optional JC 2.2 package_name), version, header
flags (applet/exports/int), applet AIDs and the compiled-against hint
(javacard.framework -> Java Card SDK release + CAP format).
- Requires (n): one row per imported library with the minimum version
(">=", same major and minor >= the recorded export version per JC VM
4.5.2), the API family and the distinct constant-pool reference count.
The AID is not repeated when the name resolves (redundant); unresolved
AIDs are shown as the name.
- Memory: the NVRAM requirement stated once, with indented children (code
image + persistent data parts), RAM (volatile) and the suggested
C6/C7/C8 quotas - the old duplicated totals are gone.
- Components: collapsed table in load-file order with size and share,
ending in the load-file total row; Notes holds the GP caveat and the
estimate disclaimer.
- Numbers line up right-aligned in monospace columns; section labels use
uppercase letter-spacing inline, so no Tailwind rebuild is needed.
- i18n: 27 new EN/RU keys; help EN/RU and AGENTS updated.
Frontend tests reworked for the new markup (group labels, aligned values,
load-file order, total row, and the no-imports / unknown-AID /
older-response / missing-components cases).
592 frontend / 473 Python green; version 3.5.17; sw simple-v270.
|
||
|
|
0255a956e2 |
feat: show the CAP's required libraries, package identity and components (v3.5.16)
The Import component (JC VM spec 6.6) is now exposed by /api/cap-info and rendered in the CAP analysis box: - imports: the libraries the CAP is linked against with the export-file versions and the number of distinct constant-pool references (6.7), displayed as "name >= version" (a card resolves an import only with the same major and a minor >= the recorded one, 4.5.2). Standard names come from the AID table; each gets a family label (Oracle JavaCard / ETSI SIM 2G / ETSI UICC / 3GPP USIM-ISIM / GlobalPlatform) and, for javacard.framework, a Java Card SDK release hint derived from the local Oracle SDK kit corpus (jc211..jc305u4 exports; unknown versions stay unhinted). Vendor/applet AIDs stay bare. - Header package flags (Table 6-4: int / exports / applet package) and the optional JC 2.2 package_name (absent in all CAP 2.1 files). - components: every archive entry in load-file order with its size and share of the load file (including the Directory/Export entries capmem does not parse); the sizes sum to load_file_bytes. - The PWA box leads with "Requires: ..." when imports exist, keeps the compiled-against line (Java Card hint + CAP format), the package/applet identity, the import details (family, refs, AID) and the component breakdown in Details; a memory-only response still renders. Tests: Python +2 (imports/flags/name/components; header flags + package name) with the synthetic CAP builder extended; frontend +2 renderer cases (unknown AIDs, no-import responses) plus jcAidNorm/jcAidFamily tests; the jcAidNorm extraction added to the ram/scp81 harnesses. Help EN/RU, docs/api.md, AGENTS. 591 frontend / 473 Python green; version 3.5.16; sw simple-v269. |
||
|
|
18e0db75a9 |
feat: approximate NVRAM requirement from the CAP analysis (v3.5.15)
GlobalPlatform models this explicitly (GP Card Spec v2.3.1 Table 11-48, load parameters): C6 = non-volatile code, C7 = volatile data, C8 = non-volatile data, and 11.5.2.3.7 - when the card makes no code/data distinction the required minimum is C6 + C8. The analysis now reports it: - capmem.memory_json() accepts the load file size (all CAP components - the package image the card stores) and returns code.load_file, nvram.requirement = load_file + persistent data, and sets the suggested C6 to the load file (the bytecode-only Method.cap figure stays in code.method_component); _cap_info_body passes the size it already computed. - The CAP estimate box (both the RAM installer and the SCP81 Install-from-.cap form) leads with "NVRAM requirement ≈ code image + data" plus the RAM estimate, keeps the bytecode/other-component split and the full data breakdown in Details, and carries the GP citation with the caveats (card memory management, allocation rounding and the registry entry are not included; the static field image appears in both parts). - An older server response without code.load_file still renders (the bytecode size is used as the fallback). Tests: Python +1 (load-file semantics) with extended cap-info assertions, frontend +1 (renderer + fallback). Help EN/RU, docs/api.md, AGENTS. 587 frontend / 472 Python green; version 3.5.15; sw simple-v268. |
||
|
|
2d2a40a73c |
fix: follow-up test-script review findings (v3.5.14)
- The SCP80 counter write-back resolves the preset by NAME first (the run
snapshot prefers the name) and only falls back to an ICCID-looking
value: presets with digits in their names are found after a page reload
(the previous fallback ran cardsNormIccid on the name and gave up).
- testScriptProblem validates the *selected* SCP80 source (like the form
and the server), so source=apdu with only sp filled is caught locally
instead of failing with a server 400.
- _test_run_start cleans up (_TEST_RUNNING=False, run state error) when
the worker thread cannot be created/started, and the endpoint answers
500 with a clear error instead of leaving the card blocked behind a run
that never started.
- The 5 s poll writes the counter back whenever a run is finished
(idempotent), so a reloaded page saves it without visiting the pill.
- Mask wildcards ('?') are stripped from the hex fields that cannot carry
a mask (APDU, secured packet, event/file data, TAR/SPI overrides, DCS,
extra TLVs); check values keep them.
Tests: frontend +3 (write-back by name / by ICCID, mask-free fields,
source-aware script check), Python +1 (failed thread start unblocks).
586 frontend / 471 Python green; version 3.5.14; sw simple-v267.
|
||
|
|
d1eb88d4d7 |
fix: test-script review findings (v3.5.13)
PWA: - The SCP80 "Source" switch sticks: the choice is stored in `params.source`, switching keeps both values (the server honors the explicit source when both are present, and the form validates only the selected one). - The SW check field is empty by default (placeholder "default: 9000 (91?? when polling)"), so the server defaults apply - previously the pre-filled 9000 defeated the polling STATUS default and a card that announced a command made the step fail with "expected 9000". - Item text checks offer contains/exact only (the mask mode was rejected by the server validation). - The SCP80 counter is written back to the preset even when the run is observed after a page reload or was started elsewhere (resolved by ICCID or preset name from the run snapshot). Server: - Step-entry mutations happen under `_TEST_LOCK` (`_test_entry_update`/`_test_finish_entry`): the status endpoint serializes the state with json.dumps, so entries must not change while it iterates them. - The pending-command drains (unexpected command, error, stop) hold `_CARD_LOCK` like every other card conversation. - `_int` accepts plain decimals with leading zeros and 0x hex. - The scripted TERMINAL RESPONSE text string uses the CR-set tag `8D` (consistent with the other TR TLVs; both are legal). - A script-driven menu selection mirrors `server.menu_active`. Tests: frontend +4 (source switch/round-trip, status SW default, render checks, item modes), Python +3 (integer parsing, menu_active, source selection). Help/docs unaffected beyond api.md's `source` note. 583 frontend / 470 Python green. |
||
|
|
d1c6a9c27a |
ui: rename the Phone simulator tab to Simulator and the eSIM pill to eSIM LPA (v3.5.12)
- Top-level tab label "Phone simulator" -> "Simulator" (RU "Симулятор"); the DOM ids stay tab-phone / phone-sub-*. - The eSIM pill inside the Simulator tab is now "eSIM LPA" (EN/RU), matching the section's ES10/LPA scope; the panel id stays phone-sub-esim. - Documentation updated: help EN/RU (section 8 Simulator, list of the four pills including Test script, tab lists and cross-references), README/README_RUS (tab lists, Simulator section, eSIM LPA), AGENTS repo/root. - Test titles updated; version 3.5.12; sw cache simple-v265. 579 frontend / 467 Python green. |
||
|
|
722e237ea4 |
feat: Test script pill in the Phone simulator (v3.5.11)
- Phone simulator tab: a fourth pill **Test script** - script list (localStorage `simple_tests`) with New/Clone/Delete/Export/Import, a form-based step editor (modal) and a run panel. - Actions: ENVELOPE (Event Download), Menu Selection, UPDATE/READ file by path, raw APDU, SCP80 (C-APDU or a pre-built packet, per-step TAR/SPI1/ SPI2 overrides; the preset is matched by the equipped card's ICCID and the final counter is written back) and STATUS with an optional poll. Every action has SW/data checks (exact or ?-mask) and a PoR check for SCP80, with an error (stops) / warning (continues) fail level. - Expectations: FETCH the command announced by the previous step, verify type/qualifier/text/item/raw and answer with the scripted TERMINAL RESPONSE (result, item id, text, extra TLVs); templates for STK menu browsing and an applet via SCP80. - Run panel: progress banner (plus a marker on the pill), per-step OK/Warning/Error badges with expected vs actual values, sent/received hex, decoded commands and the SCP80 counter; 500 ms polling while a run is active, and a running script disables other card controls (data-needs gate; the server answers 409 to other card endpoints). - Help EN/RU (8.9 Test script), AGENTS; version 3.5.11; sw simple-v264. 579 frontend / 467 Python green. |
||
|
|
3a4a098f1a |
feat: test script engine and server-side runner (phase 1)
A test script drives a deterministic dialogue with the card: action steps (ENVELOPE event / Menu Selection, raw APDU, SCP80 secured packet with a card-preset, file update/read, STATUS) with SW/data/PoR checks, and proactive-command expectations that fetch, check (command type, qualifier, text/item/raw) and answer with a scripted TERMINAL RESPONSE. - pysim_simple_server/testscript.py: pure engine (validation, exact/mask matchers with '?' nibble wildcards, item/text checks, TERMINAL RESPONSE building). - server.py: worker thread + state, /api/test/run|status|stop|clear, the card-endpoint guard (409 while running), background STATUS polling suspended, 'error terminates / warning continues', a pending command is drained with a cancel TR on stop/error, no-drain modes for the ENVELOPE and SCP80 senders (the pending command belongs to the next expectation). - Expectations never poll: a command must be pending (91XX) from the previous step, otherwise it is an error (TS 102 221 7.4.2.1 / TS 102 223 6.3); scripts add an explicit `status` action (attempts/interval) when the card delivers on poll. - SCP80 steps require a complete card preset, may override TAR/SPI1/SPI2 only, and the counter is advanced per send and reported (`scp80_counter`) for the PWA to write back. - tests/test_testscript.py (26 tests: engine, matchers, TR building, the STK menu dialogue, error/warning termination, status polling, unexpected-command drain, SCP80 preset/counter, file actions, guards). - docs/api.md endpoint reference. 467 Python / 573 frontend green. |
||
|
|
4c3fa64c95 |
feat: polling on by default and an effective-state toggle (v3.5.10)
- Background STATUS polling is enabled by default, per the spec's idle
polling rule (TS 102 221 14.6.2): `_POLL_ENABLED = True`, __main__ runs
`_poll_enable()` regardless of card presence (--poll-interval 0 still
disables) and `_do_status_poll` keeps ticking while enabled even without
a session, so a cardless start resumes as soon as a card appears.
- A new card session clears a POLLING OFF from the previous card in
`_apply_equipped_card` before re-enabling polling.
- The Phone-tab toggle now shows the *effective* state: OFF in amber
("card disabled polling (POLLING OFF)") while the card suspended
proactive polling, back to ON on the next POLL INTERVAL. The 5 s
background poll now passes `card_disabled` through (it was dropped, so
an autonomously received POLLING OFF never reached the UI).
- Tests: poll_ui updated (button OFF while suspended, warning path),
test_poll +2 (cardless ticking, module default via a subprocess check).
- Help EN/RU, docs/api.md, AGENTS; version 3.5.10; sw cache simple-v263.
573 frontend / 441 Python green.
|
||
|
|
25f001a778 |
feat: card-driven STATUS polling and the Poll Interval Negotiation event (v3.5.9)
- EVENT_NAMES (server + PWA) corrected against TS 102 223 v18.3.0 8.25: 0x14 is "Access technology change (multiple)" (not Change of UICC Access), 0x19 Profile container, 0x1A Void, 0x1B Secured profile container, 0x1C Poll interval negotiation, 0x20-0x22 reserved. Values the CAT spec leaves "Reserved for 3GPP" now carry the concrete TS 31.111 event name + clause (0x11 (I-)WLAN access status, 0x12 Network rejection, 0x15 CSG cell selection, 0x17 IMS registration, 0x18 Incoming IMS data, 0x1D Data connection status change, 0x1E CAG cell selection, 0x1F Slices status change). - Poll Interval Negotiation (0x1C) in the Phone tab: the form proposes a Duration (unit + interval) and the UICC's answer (TS 102 223 8.97 accepted / rejected / modified + optional Duration) is decoded and shown; a "modified" duration becomes the background poll interval. The form appears when the card subscribed to the event, like every other. - Polling emulation is card-driven: a POLL INTERVAL adopts its Duration (minutes/seconds/tenths -> 1..255 s, logged) for the background poll, is echoed in the TERMINAL RESPONSE (6.8.4) and clears a POLLING OFF suspension; POLLING OFF (6.4.14) suspends proactive polling until a new POLL INTERVAL - the manual Send STATUS button and presence detection are unaffected. /api/poll-status and /api/poll-toggle report card_disabled (+ a warning when enabling while suspended); the PWA shows it in amber. - Tests: tests/test_poll.py +7, test_proactive_names.py +3, frontend event_forms +2 and poll_ui (4). Help EN/RU, docs/api.md, AGENTS. 573 frontend / 439 Python green; version 3.5.9; sw cache simple-v262. |
||
|
|
d9e6c6c9dd |
feat: CAP memory estimation with a confirm step before install (v3.5.8)
Selecting a .cap in the RAM installer or the SCP81 "Install from .cap" template now runs a read-only analysis (POST /api/cap-info) before any APDU is built: the archive is validated structurally (a corrupt or wrong-format file fails here) and the bundled capmem analyzer estimates the code size and the persistent (NVRAM) / volatile (RAM) requirements, with the tool's suggested C6/C7/C8 quotas shown as information. The form's action button (Execute / Generate) stays disabled until the analysis succeeds - pressing it is the user's confirmation to continue. - pysim_simple_server/capmem.py: bundled analyzer (component parsers + JCVM opcode table + method-bytecode allocation scan), adapted to take the CAP archive as bytes and return report/memory dicts; output verified byte-identical to the workspace tool on 21 real CAPs. - _cap_info_body + POST /api/cap-info (read-only; the install endpoints stay unchanged and self-sufficient). - PWA: shared capAnalyzeFile/capMemHtml/capGateOk helpers, estimate box under both CAP inputs (reusing the idle #ram-cap-info div, new #scripts-cap-info), data-cap-gate gating in pysimApplyAvailability, stale-response guard, Retry, EN/RU strings. - Tests: tests/test_cap_memory.py (synthetic CAPs: new/newarray/ makeTransientByteArray/static fields/unknown-opcode warnings/corrupt input), frontend capmem.test.js (renderer, gate, analyze flow). - help EN/RU, docs/api.md, AGENTS; version 3.5.8; sw cache simple-v261. 567 frontend / 429 Python green. |
||
|
|
b88f04fc69 |
feat: name standard package AIDs in the Explore / SCP81 / R-APDU views (v3.5.7)
The RAM Explore view, the SCP81 GET STATUS script results and the R-APDU parser tree showed package AIDs as bare hex. A shared resolver now annotates the known standard JavaCard / ETSI / 3GPP / GlobalPlatform package AIDs with their library name (workspace export-file study `docs/JAVACARD.md`, 2026-09-26, plus the GP default ISD AID from GP Card Spec v2.3.1 H.1.3); a RID table gives a weak owner hint for otherwise unknown AIDs, and vendor/applet AIDs stay bare. - JC_AID_NAMES / JC_AID_RIDS + jcAidName / jcAidSuffix / jcAidHtml. - Wired into ramRenderExploreHtml (ISD, applications, ELFs, module and SD AIDs), scp81ResultLines GET STATUS listings and decodeTlvValue (4F/84/C4/CC - the R-APDU parser tree). - aid_names.test.js (families, normalisation, RID hints, malformed input, table sanity) plus render assertions in ram.test.js and scp81.test.js. - Help EN/RU note the annotation; table is hand-maintained from the note. 561 frontend / 421 Python green; version 3.5.7; sw cache simple-v260. |
||
|
|
8064625d56 |
feat: next action suffix in the pending SELECT ITEM panel (v3.5.6)
The STK menu overlay's cached top menu showed the card-provided Items Next Action Indicator (TS 102 223 8.24) as a gray suffix, but the pending SELECT ITEM list the card returns mid-dialogue did not: the server already attaches nai_name (via _parse_select_item), the panel just had its own row markup without the suffix. Both lists now render through stkMenuItemsHtml(items, handler) - identical rows (id, text, optional gray suffix) with the per-list click callback (stkMenuItemClick / stkSubItemClick). stk_menu.test.js: the shared renderer (suffix only with nai_name, handler in the row, null list) plus a call-site guard for both lists. 554 frontend / 421 Python green; version 3.5.6; sw cache simple-v259. |
||
|
|
31ece16ada |
fix: animated card icon while the card is being initialized (v3.5.5)
The Card reader view showed the static nosim.svg while the status line said
"Card inserted - initializing..." (the header indicator was correct): the
2 s /api/status poll called pysimSetConnected(false) unconditionally in its
not-connected branch, ignoring the equipping / auto-equip-pending state.
- pysimCardStateUpdate(): one `initializing` flag (equipping || card_present
&& auto_equip) now drives both the status text and the icon
(pysimSetConnected('spin') -> sim_anim.svg), matching the header indicator.
- pysimRefresh() ("Check status"): a reachable server is not an equipped card;
the icon now follows connected / initializing / none instead of always
showing the equipped icon.
- card_state.test.js: initializing states -> 'spin', cardless -> false, and
the four Check-status combinations.
552 frontend / 421 Python green; version 3.5.5; sw cache simple-v258.
|
||
|
|
719956baee |
docs: note the A4 CRT single-key-reference decoding; sw cache bump
Small code-review leftover: efArrSc() keeps only the last '83' key reference of a malformed multi-reference AT control reference template (compliant CRTs carry exactly one, ISO 7816-4 6.3.1) - now said so in the code. The scp81-findings log needs no new rows: no new live SCP81 tests were run (it is current through 2026-09-16h; the "load/store over SCP81" item in its Next-tests list is already verified). Comment-only index.html change -> sw cache simple-v257 (version stays 3.5.4). 549 frontend / 421 Python green. |
||
|
|
bed66c8ff8 |
fix: robust equip-state detection and review follow-ups (v3.5.4)
Code-review follow-ups for v3.5.2/v3.5.1, plus a test flake found while re-running the suites: - F1: a half-initialized equip is no longer reported as success. cmd2 swallows exceptions raised inside the equip command (and prints no traceback by default), while PysimApp.equip() assigns card/rs before it registers the command sets - so app.card alone once let the "CommandSet ... is already installed" abort pass as done while /api/tree stayed broken. The auto-equip attempt now captures the output with cmd2 debug on (a swallowed error prints a traceback), requires the new profile's command-set instances to be installed (_app_equip_complete), and the manual /api/command equip branch applies the post-equip refresh only when that check passes (and reports it in the output when it does not). - F3: SCARD_E_SHARING_VIOLATION is recoverable (a rebuild cannot free another process's claim) instead of transport-fatal. - F4: SPI1 b2b1 = 11 (Digital Signature) is refused instead of building an unsigned packet; help notes RC is CRC-32 only (KID CRC-16 not offered). - F5: _clear_app_card_state removes the muted stdout again when the app object had no stdout attribute. - F7: the watchdog re-arm keeps its rate-limit window when the trigger is busy/disabled instead of consuming it. - F2: stale docstring in _auto_equip_attempt. - tests: equip-state units, half-equip and captured-traceback failures, unequip-on-failure, busy trigger, sharing violation, DS refusal. - bonus: the MCC/MNC random-pick test could fail because a dict keyed by (mcc, mnc) keeps one of two entries (the bundled list carries both a real and an MVNO entry for 234/18 and 234/28); the picker was correct - the assertion now checks the pair against the non-MVNO pairs. 549 frontend / 421 Python green; version 3.5.4; sw cache simple-v256. |
||
|
|
337e5df770 |
feat: decode the Items Next Action Indicator in proactive commands (v3.5.3)
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. |
||
|
|
0f8c6dea50 |
fix: recover from a card swap without a server restart (v3.5.2)
Equip was broken after a card swap: auto-equip (and manual Equip) failed with "Failed to transmit with protocol T0. Card was removed. (0x80100069)" until the server was restarted. Chain (v3.1.2 regression): - _handle_card_disconnect() cleared server.card/scc but not pySim's app.card/app.rs/app.lchan, so the removed card - and with it the old PC/SC link and its exclusive card handle - stayed referenced; handlers using app.rs (e.g. the PWA's /api/tree poll) kept transmitting over the dead card. - those errors carry hresult=0x80100069 (SCARD_W_REMOVED_CARD), but _is_pcsc_error() treated any PC/SC error as a dead service and set _TRANSPORT_STALE; the next auto-equip then called _ensure_transport(), which built a second PcscSimLink while the old one was still connected - the new link inherited the removed card's handle and failed on its first APDU. No retry existed, so every later equip repeated the failure. Fixes: - _is_transport_fatal(): only service/context hresults rebuild the transport (E_NO_SERVICE, E_SERVICE_STOPPED, E_NO_READERS_AVAILABLE, E_INVALID_HANDLE, ...); card-level states (W_REMOVED_CARD, E_NO_SMARTCARD, W_RESET_CARD, W_UNRESPONSIVE_CARD, W_UNPOWERED_CARD) reconnect on the existing link. All 8 disconnect call sites pass the new verdict. - _clear_app_card_state(): unequip through pySim's own equip(None, None) before clearing app.card/app.rs/app.lchan. Nulling them alone would make the next equip abort with "CommandSet ... is already installed" (PysimApp.equip() unregisters the previous profile's command sets from self.rs), which left /api/tree broken after a swap. A failed auto-equip attempt unequips the half-initialized shell as well. Handlers now answer "no card" instead of transmitting over the dead card, and the old link becomes collectable. - _ensure_transport(): disconnects the old link before building the new one (restoring it if the factory fails) - the rebuild path is now safe for the real pcscd-restart case. - auto-equip: _auto_equip_attempt()/_auto_equip_attempts() retry up to 3 times, 1 s apart (fatal failures mark the transport so the retry rebuilds); the watchdog re-arms it (_auto_equip_rearm) every 5 s while a card is present and the session is down, with exponential backoff to 60 s for a card that cannot be initialized at all. - fastinit.init_card_fast(): also retries once after a PC/SC link error (CardConnectionException/NoCardException), not only after SW mismatches. Tests: transport-fatal classification, app-state clearing, shell unequip, old-link release, auto-equip retry/backoff/re-arm, fastinit link retry. 547 frontend / 410 Python green; version 3.5.2; sw cache simple-v254. |
||
|
|
73ff6a60bd |
fix: SCP80 RC/CPL parity, file-life-cycle labels, EF.ARR and PS template decoders (v3.5.1)
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. |
||
|
|
7803d8520d |
docs: sync help/README with the Push commands, Parser and picker UI (v3.5.0)
- README/RUS: the Remote APDU tab has six sub-tabs, not seven; the GP connection-parameter table listed the wrong TLV tags (02/80/01) - it now matches the UI presets (82 Device Identities, 05 Alpha, 81 Command details, 35 Bearer, 03 = default bearer). The Push commands intro names the mode-specific rows (SD params tag in Store, Command Scripting template in Trigger) and the RU text uses the RU sub-pill labels; both READMEs gain the PoR advice bullet (Stepping Stones R7 18.6.3: no PoR on success, on failure SMS-SUBMIT + SPI2 0x20). - help RU: the C-APDU parser section names the Parser pill sub-pills like the EN one; the Push commands intro describes what the mode actually switches and uses the RU sub-pill labels. - sw cache -> simple-v252 (help pages are precached). Docs only; no functional change, version stays 3.5.0. |
||
|
|
82f45d488f |
ui: guided OPEN CHANNEL parameters for the BIP opening request (v3.5.0)
The 01 (BIP channel opening) variant was raw hex while 03 (TCP) had a detailed form. The asymmetry is real - 01 accepts any OPEN CHANNEL COMPREHENSION-TLVs (TS 102 226 9.2.1) while 03's set is fixed by 9.2.3 - so 01 now offers a Fields/Hex switch instead of a rigid form: - Fields (default) composes the most-used OPEN CHANNEL parameters per SIMalliance Stepping Stones R7 18.6.1: bearer description (default 35 01 03), transport protocol (UDP 01 / TCP 02, UICC client, remote - the only values TS 102 223 6.6.27.4 allows when the transport level is present) with the destination port, destination address, NAA/APN, buffer size, alpha (transparent checkbox -> 05 00 = no user confirmation) and an extra-TLVs field for login/password and anything else. The local address is never emitted (9.2.1 and the guide forbid it). - Hex keeps the raw COMPREHENSION-TLVs field; pushOpenChannelTlvs() is a pure, vector-tested composer feeding the same encoder as before. Help 2.7 EN/RU and the READMEs explain the two modes; sw cache -> simple-v251; version stays 3.5.0. |
||
|
|
052d90c03e |
ui: HTTP OTA pill labels, CAT_TP paired with the BIP opening (v3.5.0)
- The first two Push commands sub-pills carry the HTTP OTA prefix
("HTTP OTA Trigger (Push SMS)", "HTTP OTA Store (SD admin params)"), so the
form headings drop it and read "Administrative session trigger (Push SMS)"
/ "Administrative session store data (SD admin params)".
- BIP channel opening (01) gains the same-message CAT_TP follow-up: an "Also
request the CAT_TP link establishment (02) in the same message" checkbox
with the shared CAT_TP fields (destination port mandatory, optional max SDU
/ identification data / extra TLVs). The preview and Pack then carry the
pair 80EC0101...80EC0102... as one command script (concatenated SMS when
over 140 octets) and -> Expanded Script appends both 22 Command TLVs.
Rationale (SIMalliance Stepping Stones R7 18.6): 01 carries the channel
parameters, 02 only the CAT_TP port, and the guide recommends both in the
same SMS; standalone 02 stays for cards with provisioned defaults.
- Help 2.7 EN/RU, READMEs and AGENTS document the pairing, the fact that 03 is
self-contained (it carries the mandatory OPEN CHANNEL TCP set, so no
companion 01) and the Stepping Stones PoR advice (no PoR on success; on
failure use SMS-SUBMIT / SPI2 0x20).
- pushSectionApdus vectors cover the 01+02 pair and its mandatory port;
sw cache -> simple-v250.
|
||
|
|
9cbf301f09 |
ui: group the TCP request with the channel/link triggers; identification packet as a same-message follow-up
The Push commands pill now has three sub-pills: Trigger (Push SMS), Store (SD admin params) and Channel / link trigger, which builds all three TS 102 226 §9 establishment requests - 01 BIP channel opening (OPEN CHANNEL), 02 CAT_TP link, 03 TCP connection - with one shared destination-port field and per-request field visibility. The TCP parameters are the OPEN CHANNEL TCP set (BIP, or a direct IP connection per TS 102 483 where supported), so 03 belongs with the other channel/link requests. The identification packet (04) is not a trigger: it presupposes an already open TCP channel and only makes sense as a follow-up in the same message, so it is no longer a standalone request. The TCP request carries an "Also send the identification packet (04) in the same message" checkbox (optional data, ICCID when empty): the preview then shows both commands (a command string sent in one secured packet) and → Expanded Script appends one 22 Command TLV per command. pushSectionApdus() returns the APDU list; 04 stays available in the RAM/GP chain's PUSH row for script sequences. Help 2.7 EN/RU, READMEs and AGENTS describe the three sub-pills and the BIP vs direct-IP nuance; sw cache -> simple-v249; version stays 3.5.0. html.test.js also asserts that the document is complete (ends with </html>, every <script> closed, the inline script parses and defines cApduSwitchSubtab): a truncated index.html made the browser fail to parse the inline script, so every onclick handler reported "function is not defined" while the Node tests still passed. |
||
|
|
731cb53105 |
ui: Parser pill — C-APDU / R-APDU parsers as sub-pills; R-APDU rename (v3.5.0)
Remote APDU now has six top-level pills (SIM RFM, USIM RFM, RAM/GP, Expanded Script, Push commands, Parser); the C-APDU and R-APDU parsers are nested panels behind the Parser pill, switched by parserSwitchSubtab(). - "Response parser" renamed to "R-APDU parser" (RU: «Разбор R-APDU»), the umbrella is "Parser" / «Разбор»; the C-APDU panel keeps its id and label. - cApduSwitchSubtab() routes nested names through the Parser pill first, so pysimSendOta()'s post-send jump keeps working; the help deep-link anchor follows the selected parser (c-apdu-parser / response-parser) and the Push commands pill now links to the renamed #push-commands section. - The small C-APDU panel moved next to the R-APDU panel so both live in the #c-apdu-sub-parser container; html.test.js checks the nesting and sub-pills. - Pill lists updated in help EN/RU (2.6 note, 2.8 heading), READMEs, AGENTS; sw cache -> simple-v248. |
||
|
|
c011c02f8e |
ui: Push commands sub-pills — one form at a time (v3.5.0)
The Push commands pill now switches between four sub-pills - Trigger (Push
SMS), Store (SD admin params), BIP / CAT_TP trigger, TCP trigger - and shows
only the selected form:
- Trigger/Store share one GP administration form (fields unchanged); the mode
only changes the payload wrapping and the form heading, which now carries
the HTTP OTA prefix: "HTTP OTA Administrative session trigger (Push SMS)"
/ "HTTP OTA Administrative session store data (SD admin params)".
- BIP/CAT_TP and TCP keep their own headings ("... (TS 102 226 §9)") and
their "not the administration session" note.
- hotaSetForm() owns the pill styling and form visibility and delegates to
hotaSetMode() (Trigger/Store) or pushUpdate() (SS9 forms); the view always
opens on Trigger, nothing is persisted.
sw cache -> simple-v247; help 2.7 EN/RU, READMEs and AGENTS updated.
|
||
|
|
f17e7cb8da |
feat: Push commands pill — GP administration trigger + TS 102 226 §9 pushes (v3.5.0)
The former HTTP OTA pill becomes "Push commands" (RU: Пуш/триггер) and groups everything that makes the card dial out: - Administrative session (GP Amd B §4.7): the existing Trigger/Store UI, unchanged, as the first section (most used). - BIP / CAT_TP trigger (§9): request 01 BIP channel opening (optional OPEN CHANNEL COMPREHENSION-TLVs) or 02 CAT_TP link (destination port, optional max SDU / identification data). - TCP trigger (§9): request 03 TCP connection (bearer, transport level with protocol type 02, destination address 21/57/F0, NAA/APN, extra TLVs) or 04 identification packet (optional data, ICCID when absent — sent over an already open channel). The guided sections reuse chainPushData (the RAM/GP chain PUSH encoder) and preview the C-APDU; mandatory parameters (02 port, 03 port + address) are enforced there while the chain row stays lenient. Actions: Pack into Secured packet (no auto-TAR — a §9 PUSH goes to the target application) and → Expanded Script (22 Command TLV import; berAppendApdu extracted from loadExpandedScript). Each §9 request has a note separating it from the administration session. Chain-builder PUSH command and the expanded-script C-APDU picker are unchanged. Tests: pushSectionApdu vectors in ts102226.test.js; help 2.7 EN/RU restructured, READMEs, AGENTS; sw cache -> simple-v246. |
||
|
|
cab54a5504 |
ui: order the Remote APDU pills RAM/GP, Expanded Script
Swap the two pills in the Remote APDU view (SIM RFM, USIM RFM, RAM/GP, Expanded Script, HTTP OTA, C-APDU Parser, Response parser) and keep the subtab-toggling name list in the same order. Pill order updated in help EN/RU, READMEs and AGENTS; sw cache -> simple-v245; version stays 3.4.0. |
||
|
|
a5f7ca840d |
ui: start the SELECT picker session in MF for both RFM builders
The file picker's implicit current DF now defaults to MF for SIM RFM and USIM RFM alike (the real context follows the TAR the packet is sent to, TS 102 226 7.2/7.3, so the default is neutral and the per-row "starts in" selector covers ADF sessions - ADF.USIM files appear once it is switched). Tests set the ADF session explicitly where they exercise ADF-relative fills plus a new default assertion; help 2.1 EN/RU, READMEs and AGENTS updated. sw cache -> simple-v244; version stays 3.4.0. |
||
|
|
1035bf069d |
ui: show SELECT picker paths from the root instead of "fid — parent"
The picker option label read backwards, e.g. "EF.IMSI — 6F07 — 7F20". It now shows the path within the optgroup's root with the FIDs in order, matching the path-field convention: "EF.IMSI — 7F20/6F07", "EF.ICCID — 2FE2", "EF.PSC — 5F3A/4F22" (ADF session). Switch markers and the filter (which also matches the symbolic path) are unchanged. sw cache -> simple-v243; version stays 3.4.0. |
||
|
|
b8734189cf |
feat: offline file list + SELECT picker for the SIM/USIM RFM builders (v3.4.0)
The SELECT rows (compact RFM chains and the Expanded Script C-APDU picker, which shares the row editor) now have a file picker instead of typing FIDs, paths or chains by hand. Data: - `pysim_simple_server/uicc_files.py` builds the standard file list cardless from pySim's profiles and application classes (CardProfileUICC + CardProfileSIM for the MF tree, every concrete CardApplication subclass for the ADFs) and writes `frontend/uicc_files.json` (490 entries: canonical FID path, symbolic name, fid, kind, root, ADF AID). `tests/test_uicc_files.py` regenerates it and fails when pySim adds or renames files. The asset is precached by the service worker, so the builders keep working offline. Picker: - Sources merged per canonical path: the loaded file-manager tree (card names and probed presence; probed-absent files hidden), custom files, and the shipped standard list (specs-default until "Probe all files" has run). - Default list shows what the current method can express; "all files" reveals the rest, and picking one auto-switches the method - preferring path (one SELECT) over chain - with a note line explaining the switch. A "starts in" selector sets the implicit current DF (UICC shared-FS RFM app starts in MF, an ADF RFM app in its ADF, TS 102 226 7.2/7.3 - the TAR decides; default per builder, overridable per row). - Fill rules: by FID only for direct children of the current DF; path = FID sequence from MF without the MF identifier (ISO 7816-4) or the relative tail (USIM P1=09); chain stays relative and follows the TS 102 221 11.1.1.2 FID search order (children, parent, siblings), so no hop to the common ancestor. ADF roots are not pickable (selection is by AID; TS 102 226 7.1 forbids P1=04 for RFM) - the By AID method stays manual. Tests/docs: frontend/tests/uicc_files.test.js (asset shape, merge/priority/ exclusion, fill matrix, relative chains, session-context tracking, option grouping); help 2.1/2.2 EN+RU, READMEs, AGENTS. sw cache -> simple-v242. |
||
|
|
7aee1818b8 |
feat: remote-script command palette — RFM/RAM commands in Expanded Script, TS 102 226 §9 PUSH (v3.3.0)
Phase 1 — the Expanded Script C-APDU rows are no longer hex-only: - chain containers gained a kind (chainKind/chainIsEmbedded/chainCommands), so the SIM RFM / USIM RFM / RAM-GP row editors and hex builders can be embedded as one-row "virtual" chains (ber-<kind>-<uid>) with a listener hook (chainListen/chainNotify) refreshing the owning row. - the C-APDU row offers Hex / SIM RFM / USIM RFM / RAM-GP; the built APDU is echoed next to the picker and wrapped in the 22 Command TLV. GET RESPONSE is not offered in the embedded pickers (TS 102 226 5.2.1.1). Phase 2 — "→ Expanded Script" in the SIM RFM / USIM RFM / RAM-GP views imports the built chain as C-APDU rows (chainApduList drops GET RESPONSE and splits multi-APDU FID-chain selects; the RAM "To expanded" preview stays). Phase 3 — the TS 102 226 table 7.1/8.1/9.1 gaps: - RFM: SEARCH RECORD (TS 102 221 11.1.7 / SEEK per TS 151 011 9.2.7), INCREASE (11.1.8 / TS 151 011 9.2.8), CREATE FILE / DELETE FILE / RESIZE FILE (TS 102 222 6.3/6.4/6.10, with an FCP skeleton builder and CLA 80 for RESIZE), SET DATA / RETRIEVE DATA (11.3, block + SFI coding); - RAM: PUT KEY (GP Card Spec 11.8) and the TS 102 226 §9 PUSH command (80 EC 01 P2) with BIP opening (optional OPEN CHANNEL TLVs), CAT_TP (3C/39/36), TCP (35/3C/3E/47) and identification-packet variants. Tests: new frontend/tests/ts102226.test.js (byte-exact spec vectors for every new command, the FCP skeletons, chainApduList) plus C-APDU row tests in ber.test.js; 530 frontend / 390 Python green. Help EN/RU, README/RUS and AGENTS document the Command TLV taxonomy (Tables 5.5/5.9 limits), the picker and the new commands. |
||
|
|
43f50d3b72 |
feat: SCP80 SMS concatenation + packet size / SMS count display (v3.2.0)
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. |
||
|
|
f72054f61f |
ui: stop showing the all-FF record marker as a field; label it in the diff (v3.1.4)
A profile check comparing an empty PNN record against a populated one
showed a cryptic 'Empty: yes' row: the decoders return {empty: true} for
an all-FF record and efFlatten() rendered that marker as a pseudo-field
('empty' prettified to 'Empty', the boolean as 'yes').
- efFlatten() skips the marker (the decoded views already show their own
translated 'empty' note next to the record).
- efDiffData() fills the cells of a side that decodes to an all-FF record
with 'Empty (all FF)' (RU: 'Пусто (только FF)') instead of leaving them
blank; a field merely missing from a non-empty record stays blank.
- tests: efFlatten marker, one-sided and both-sided empty diffs, and the
PNN case from the report; help EN/RU note the new text.
- sw.js simple-v239.
|
||
|
|
fa600cb45d |
ui: rename the SCP81 script templates to Explore ISD / Delete AID (v3.1.3)
'Explore' and 'Delete' were ambiguous next to the other SCP81 actions. - template selector: Explore -> Explore ISD, Delete -> Delete AID (EN/RU); - a new Explore script is named 'Explore ISD'; the Delete template keeps its count-based name (e.g. 'Delete 2 AIDs'); - new scriptKindLabel() maps the stored kinds (unchanged: explore/delete/install/empty, so existing localStorage scripts keep working) to those labels and is now used for the scripts table kind column, which used to print the raw lowercase kind untranslated; - docs: help EN/RU, README/RUS, docs/api.md examples, AGENTS; - sw.js simple-v238. |
||
|
|
b87b3905ee |
fix: recover from a PC/SC service failure without a server restart (v3.1.2)
After pcscd restarted (or the reader re-enumerated) the server stayed permanently cardless: - pyscard's presence-monitor thread stops itself on SCARD_E_NO_SERVICE and pyscard never starts it again, so card insertions were no longer noticed and auto-equip was dead; - PCSCCardConnection.connect() reuses the context handle captured when the reader was opened, so the old connection could not be revived and every APDU kept failing with 'Service not available'. - _start_card_watchdog(): a daemon that every 5 s checks whether CardMonitor().rmthread.instance is alive and recreates the stopped thread (CardMonitoringThread.instance = None + a fresh rmthread); the new thread reports already-present cards as inserted, which triggers auto-equip. start_card_monitor() starts the watchdog. - _is_pcsc_error(): pyscard exceptions carry an hresult, card-level errors do not; the disconnect paths (AUTO-STATUS, status poll, timer expiration, net-sim, tree, event send, OTA send) pass the verdict to _handle_card_disconnect(stale=...), which marks the transport stale. - _ensure_transport(): builds a fresh transport via server.transport_factory (installed by __main__ as mod.init_reader(opts, **tracer_kwargs)) and installs it into app.sl/server.sl; called by the auto-equip worker, the /api/command equip branch and _esim_reinit. - tests: watchdog tick, transport recreation (fresh/stale/failure/no factory), PC/SC classification, stale disconnect flag. - docs: AGENTS card-behavior section, README troubleshooting (EN/RU); version 3.1.2, sw.js simple-v237. |