Each rule now carries an fciMode ('type' | 'type_size' | 'exact') plus an
fciHex (raw SELECT response) so rules can verify different depths of file
control information:
- Filetype only (FCP): exists + file type
- Filetype + size (FCP): adds file size (or record length/count) - previous
behavior
- Exact FCI: adds byte-for-byte comparison of the raw SELECT response (the
FCP '62' template), catching FID/AID, life-cycle, security-attribute and
proprietary-parameter changes
/profile from card/ gains a matching FCP/FCI selector (default Filetype +
size); rules store fciMode and always capture fciHex so they can be
upgraded to Exact FCI in the editor without rescanning. The rule editor
adds the selector, hides size/record fields in 'type' mode and shows an
editable FCI hex textarea in 'exact' mode. Contents checks stay independent.
Server /api/select now returns fci_hex (raw FCP template hex, uppercased).
profilerValidateProfile accepts the new mode; profilerNormHexStrict added
for byte-exact comparison (no '?' wildcards). Legacy rules without fciMode
default to type_size.
10 new tests (build-rule fields, run-rule modes incl. byte compare +
missing-FCI, validation). Docs + RU i18n synced. SW cache v52 -> v53,
version 1.9.28.
Rules now persist the pySim symbolic name captured during 'Profile from
card' (profilerBuildFileRule stores c.name || sel.name), and the profile
edit view renders it next to the Path label using the same lookup order
as the check view: profilerCustomNameForPath(path) || rule.name.
Path edits call the new profilerUpdateRulePath, which drops the stale
scan-time name and refreshes the label from the custom-files dictionary
live, so the displayed name always corresponds to the current path.
Manually added rules get name: null (resolved via custom-file lookup).
Old profiles without rule.name still work.
4 new tests (rule.name from child/select/null, custom name lookup, path
edit clears name + refreshes label). SW cache v51 -> v52.
The 'Profile from card' ignore checkboxes matched only by FID, but the
list had EF.KcGPRS at 4F52 (TS 31.102 DF.GSM-ACCESS) while the live card
exposes it at 6F52 (TS 51.011 DF.GSM, verified in TS 51.011 v4.15.0
10.3.32 and the DF.GSM allocation table). The miss made profilerBuildFileRule
capture full contents (Exact) for a file the user had checked to ignore.
- Correct EF.KcGPRS FID to 6F52 (spec-verified)
- Match ignores by FID OR by pySim name: each checkbox now carries
data-ignore-name, profilerScanStart builds an ignoreNames set, and
profilerBuildFileRule checks both. Covers FID variants (6F52 vs 4F52)
and future constant typos; ignoreNames is optional for back-compat.
- 6 new tests: ignore-list FID/name sanity (incl. KcGPRS=6F52), duplicates,
ignored-by-FID, ignored-by-name-only regression case, non-ignored control,
back-compat when ignoreNames is omitted.
SW cache v50 -> v51.
- Seed one empty record row when record content is created or reset
(filetype change or Contents None->Exact/Mask) via profilerEmptyRecordContent
- Add per-record delete and '+ Record' add controls so the record view is
always editable; delete is hidden on the last remaining row
SW cache v47.
- Show Size only for transparent/ber_tlv; Record length/count only for
linear_fixed/cyclic; hide all size/record fields for df (unknown shows all)
- Hide the Contents section entirely for df (existence-only check)
- Changing file type clears now-inapplicable fields and resets content
(keeps mode) when its kind switches between record and transparent
- profilerSetContent defaults content kind from the selected file type
Add profilerFileFields + profilerContentKindForFileType helpers + tests.
SW cache v45.
When generating a ruleset from the card, capture EF.IMSI (6F07) and
EF.ICCID (2FE2) contents as a mask matching only the first 4 bytes
(e.g. 08290591??????????), the rest are '?' wildcards.
Add profilerMaskPrefix4 helper + PROFILER_MASK_PREFIX4_FIDS map + tests.
SW cache v44.
When a declared fileSize/recordLen/numRecords attribute mismatches, compare
file contents only over the overlapping (shorter) portion, so a length
difference alone doesn't fail the content check when the common bytes match.
When sizes match, contents are compared fully as before.
Add profilerMatchMin helper + tests. SW cache v43.
New 'Profiler' sub-tab in card-reader view:
- Named rulesets (profiles) persisted in localStorage; each has an ordered
list of filesystem rules (type 'file', extensible to OTA/TAR checks later)
- List page: New profile / Profile from card / Import profile + per-profile
Edit / Check / Export / Delete
- Editor page: inline rule fields (path, file type, size, record length,
record count, contents with exact/mask/'?'-wildcard modes)
- Results page: sequential rule checks with live progress and a pass/fail
report (existence + FCI attributes + content match)
- 'Profile from card' scans the equipped card; creates a rule only for files
that exist (FCI present). A scan-options dialog lets the user skip contents
of frequently-overwritten dynamic files (LOCI/PSLOCI/EPSLOCI/5GS3GPPLOCI/
Keys/KeysPS/SMS/Kc/KcGPRS/LOCIGPRS/CBMID/SMSS), checked by default
- ADF-rooted paths use the AID; paths resolve via a new server _select_path
Server:
- _select_path() resolves MF/ADF-AID-rooted paths (pySim can't select ADF by AID)
- /api/select and /api/read accept 'path'; /api/select returns file_size,
record_len, num_of_rec
Also fix: silent SELECT (P2=0x0C) no longer emits a trailing Le byte
(chain builder) - matches ETSI TS 102 221 silent-select behavior.
Tests: +profiler.test.js; frontend 117 pass, Python 61 pass.
SW cache v37.
Key fix: re-add chained C0000000 (GET RESPONSE Le=00) in paginate() so the
full GP APDU inside SCP80 is 80F2<p1>p2024F0000C0000000, matching the
working tool. The card's SCP80 layer executes both commands internally
(GET STATUS → 61XX → GET RESPONSE) and puts the final 9000 + data in
the PoR.
Also: server logging improvements (RAM RESPONSE-PACKET label, no truncation
of FETCH/PoR hex), docs for /api/ram-install endpoint, minor test fix.
OTA inner-APDU SWs from SIM-domain applications surfaced as 'Unknown
status word' (e.g. 9404 on a failed SELECT inside an authenticated
B00000 packet). SW_MAP.generic gains the complete TS 51.011 §9.4
families, so they decode in every resp-cmd context:
- 9200/9240 memory management (retry / memory problem)
- 9400/9402/9404/9408 referencing management (no EF selected,
out of range, file ID/pattern not found, file inconsistent)
- 9802/9804/9808/9810/9840/9850 security management (CHV/access-
condition/invalidation contradictions and blocked states; 9850
also added to generic alongside its gp/uicc copy)
- 9EXX/9FXX data-download error length / response length wildcards
Exact keys safely coexist with the 92XX proactive wildcard (exact
match wins). TS 102 221 sweep found no further stragglers: 63C0-C9
PIN retry counters are covered by the existing 63CX wildcard.
Secured packet page: enforce fresh packet per send
- any change to APDU/TAR/CNTR/keys/padding/SPI now clears the secured
packet textarea via spInvalidate(), so Send cannot reuse a stale
packet ('No secured packet to send' guards the path)
- on por_ok the CNTR field auto-increments (10-digit hex normalized,
wraps modulo 2^40) and the textarea clears; security-error verdicts
leave both untouched for retry after fixing the cause
SIM/USIM SELECT: P1/P2 per spec + live RFM idioms
- USIM FID selects requested no response data (P2=0C) - that coding is
reserved for the select-MF-by-empty-data special case; ordinary FID
selects now request the FCP template (P2=04) with Le='00' per
TS 102 221 Table 11.2 / pySim sel_ctrl convention; preset tables
fixed in both genSimUsim and updateP1P2Display
- path method gains base selector: from MF (P1=08) / from current DF
(P1=09) - reproduces the dominant live RFM idiom 09/0C
- 'silent' checkbox on fid/path/chain emits P2=0C without Le for hops
where the FCI is not needed
- chain syntax gains GET RESPONSE hops: 'C0' emits CLA C0 00 00 00 and
'C0:NN' sets explicit Le, enabling classic SELECT -> 9FXX ->
GET RESPONSE pairs in a single secured packet so the PoR carries the
actual FCI/response bytes instead of a bare length SW
- SW dictionary: 9FXX reworded to point at GET RESPONSE
- placeholders show the new chain syntax
- findings & backport decisions written to ~/WSL/RFM_notes.md
response_map.test.js: +8 assertions across the new families.
sim.test.js: USIM FID expectation updated; silent/base/chain cases.
apdu_parse.test.js: live capture lines 1-3 as regression fixtures.
Version 1.9.5 -> 1.9.6 everywhere; SW cache otaman-v16 -> otaman-v17
pySim encode_cmd transmits the 2-byte CPL only when ciphering is
applied; unciphered packets start at CHL. genSp always emitted it,
so every SPI1=0x00 packet diverged from the pySim reference at byte 0
(sp-verify MISMATCH) and carried a length octet pair real cards need
not expect.
- genSp output: bytesToHex(ciphering ? packet : packet.subarray(2));
MAC input unchanged (still covers the virtual-CPL frame, matching
pySim's sign-then-strip convention)
- Secured packet page: PoR verdict now rendered prominently in a
dedicated text-base semibold line ('PoR: por_ok' green / other
statuses red) above the small detail line; hidden when no PoR was
requested or the ENVELOPE failed
- sp.test.js: three unciphered expectations updated to CHL-first form;
ciphered vectors untouched (byte-identical)
- version 1.9.4 -> 1.9.5 everywhere; SW cache otaman-v15 -> otaman-v16
- PARSE_INS CB: rename 'RETRIEVE DATA (GET DATA)' -> 'RETRIEVE DATA'
(CA = GET DATA, CB = RETRIEVE DATA are separate commands per TS 102 221)
- Add trailing newlines to sim.test.js + response_map.test.js
SIM/USIM encoder (registry §2.3):
- New PIN commands: DISABLE '26' / ENABLE '28' (single FF-padded PIN,
Lc=08) and UNBLOCK '2C' (unblock+new, Lc=10); dynamic field labels
- ACTIVATE/DEACTIVATE FILE target selection: Current EF (case 1),
by FID (P1=00), path from MF (P1=08), path from current DF (P1=09)
with Lc+FID/path data form
- LANG_RU entries for new labels
Tests:
- sim.test.js: new stub-DOM genSimUsim harness; exact-hex for all PIN
ops, ACTIVATE/DEACTIVATE forms, SELECT Le rules, RECORD P1=00 rule
- response_map.test.js: lookupSw wildcards (91XX / 63CX), LIFECYCLE_MAP
per GPC v2.3, PRIVILEGE_NAMES vs Tables 11-7/11-8/11-9
GET STATUS E3 template decoding verified already present
(TLV_TAG_NAMES + decodeTlvValue cover 4F/9F70/C5/CF/C4/CC/CE/84).
Version 1.9.1 -> 1.9.2, SW cache otaman-v12 -> otaman-v13
Parser labels (§8.3, verified against GPC v2.3 / TS 102 221 PDFs):
- describeP1P2: SELECT P1/P2 (FID/DF-name/path-MF/path-DF; FCP/no-data),
READ/UPDATE RECORD modes (next/previous/absolute + SFI, P1-ignored note),
GET STATUS P1 (ISD/Apps/ELF/ELF+Modules) and P2 formats,
INSTALL P1 bit-aware roles ('for install + for make selectable'),
SET STATUS P1 (80 ISD / 40 App-or-SSD / 60 SD+associated) with
card states vs lock/unlock P2, VERIFY/CHANGE PIN ref
Parser structure (§8.4):
- describeInstallDataLv: exact-sum LV walker for all 5 INSTALL layouts
with privilege bit names and params tag nesting (C9, EF->CA, EA->80);
falls back to legacy TLV view when lengths do not sum exactly
- GET DATA: case-2 (P3=Le) vs case-4 (Lc + tag list + Le)
- SET STATUS data: 'ignored for ISD' / raw AID / legacy 4F-TLV labeled
- Trailing single byte consumed as Le at end of compact chain
- ACTIVATE/DEACTIVATE: case-1 (4 bytes), legacy empty-Lc, FID/path forms
- Expanded script C-APDU rows now decode into structured APDU nodes
- Compact matcher accepts CLA 84-87 (GP secure messaging)
Version 1.9.0 -> 1.9.1, SW cache otaman-v10 -> otaman-v11
New 'Parser' subtab in C-APDU section. Detects format (compact C-APDU
chain, expanded AA/AE80 script) and renders collapsible tree with decoded
parameters. Key features:
- BER-TLV parser with multi-byte tag support and CR-bit handling
- Proactive command decoder (types from BER_QUAL, devices from BER_DEVICES)
- SIM/UICC toolkit parameter decoder (CA/EA/80)
- GSM7 text decode (7-bit packed, escape sequences)
- Best-effort UCS2/GSM7 text detection
- Compact C-APDU chain with implied-CLA support (SELECT + op chaining)
- 18 test cases from exchange vectors (apdu_parse.test.js)