7ece8a531c
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
69 lines
2.9 KiB
JavaScript
69 lines
2.9 KiB
JavaScript
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
|
|
|
function extractBlock(startMarker, endMarker) {
|
|
const start = html.indexOf(startMarker);
|
|
const end = html.indexOf(endMarker, start);
|
|
if (start < 0 || end < 0) throw new Error('block not found');
|
|
return html.slice(start, end);
|
|
}
|
|
|
|
// Response parser maps + lookupSw live between SW_MAP and updateRespCmd.
|
|
// Rewrite top-level const -> var so the maps leak out of sloppy-mode eval.
|
|
eval(extractBlock('const SW_MAP = {', 'function updateRespCmd').replace(/^const /gm, 'var '));
|
|
|
|
test('lookupSw wildcard 91XX resolves proactive pending (any length)', () => {
|
|
const desc = lookupSw('91', '14', 'uicc');
|
|
assert.ok(desc.includes('proactive'), desc);
|
|
assert.ok(!desc.includes('no response data'));
|
|
});
|
|
|
|
test('lookupSw 63CX extracts retry counter', () => {
|
|
const desc = lookupSw('63', 'C3', 'uicc');
|
|
assert.ok(desc.includes('3'), desc);
|
|
});
|
|
|
|
test('SW_MAP.generic covers ISO 6A85/6A89/6A8A', () => {
|
|
assert.ok(SW_MAP.generic['6A85']);
|
|
assert.ok(SW_MAP.generic['6A89']);
|
|
assert.ok(SW_MAP.generic['6A8A']);
|
|
});
|
|
|
|
test('gp 6310 label does not reference removed P2=42 option', () => {
|
|
assert.ok(!SW_MAP.gp['6310'].includes('42'));
|
|
});
|
|
|
|
test('LIFECYCLE_MAP per GPC v2.3 life cycles', () => {
|
|
assert.strictEqual(LIFECYCLE_MAP['01'], 'OP_READY (card) / LOADED (ELF)');
|
|
assert.strictEqual(LIFECYCLE_MAP['03'], 'INSTALLED');
|
|
assert.strictEqual(LIFECYCLE_MAP['07'], 'SELECTABLE');
|
|
assert.strictEqual(LIFECYCLE_MAP['0F'], 'SECURED (card)');
|
|
assert.strictEqual(LIFECYCLE_MAP['1F'], 'PERSONALIZED (SD)');
|
|
assert.strictEqual(LIFECYCLE_MAP['7F'], 'CARD_LOCKED');
|
|
assert.strictEqual(LIFECYCLE_MAP['FF'], 'TERMINATED');
|
|
assert.strictEqual(LIFECYCLE_MAP['83'], 'LOCKED (SD)');
|
|
});
|
|
|
|
test('PRIVILEGE_NAMES matches GPC v2.3 Tables 11-7/11-8/11-9', () => {
|
|
assert.strictEqual(PRIVILEGE_NAMES[0][7], 'Mandated DAP Verification');
|
|
assert.strictEqual(PRIVILEGE_NAMES[1][2], 'Token Verification');
|
|
assert.strictEqual(PRIVILEGE_NAMES[1][7], 'Global Service');
|
|
assert.strictEqual(PRIVILEGE_NAMES[2][0], 'Receipt Generation');
|
|
assert.strictEqual(PRIVILEGE_NAMES[2][3], 'Contactless Self-Activation');
|
|
});
|
|
|
|
test('TS 51.011 SIM families resolve (94xx/98xx/92xx/9Exx/9Fxx)', () => {
|
|
assert.ok(lookupSw('94', '04', 'uicc').includes('not found'));
|
|
assert.ok(lookupSw('94', '00', 'uicc').includes('No EF selected'));
|
|
assert.ok(lookupSw('94', '08', 'uicc').includes('inconsistent with the command'));
|
|
assert.ok(lookupSw('98', '40', 'uicc').includes('blocked'));
|
|
assert.ok(lookupSw('92', '40', 'uicc').includes('Memory problem'));
|
|
assert.ok(lookupSw('98', '50', 'gp').includes('max value'));
|
|
assert.ok(lookupSw('9E', '15', 'uicc').includes('download'));
|
|
assert.ok(lookupSw('9F', '20', 'uicc').includes('GET RESPONSE'));
|
|
});
|