288c2e5954
SIM/USIM tab (TS 102 221): - Record P2 modes: 04/06/02 -> 04/02/03; P1=00 for next/previous - SELECT Le rules: USIM FID no Le, path/dfname P2=04+Le; SIM no Le - Path placeholder: 3F007FFF6FC5 -> 7FFF6FC5 - ACTIVATE/DEACTIVATE: 5-byte -> 4-byte case-1 - VERIFY PIN: dedicated input, Lc=08, FF-pad to 8 bytes - CHANGE PIN: old/new inputs, Lc=10, two 8-byte fields - ERASE BINARY removed from USIM tab RAM tab (GPC v2.3): - INSTALL: all 5 variants rewritten to strict LV + Le 00 - ELF/Module AID inputs for install-install - Privileges: 1 byte if only byte-1, else 3 bytes - InstallParams: C9 00 + toolkit TLV (mandatory) - LOAD/DELETE/GET STATUS: append Le 00 - GET STATUS P2: drop 40/42, keep 02/03/00 - GET DATA: 2F00/5031 -> case-4 5C00 data - SET STATUS: raw AID (no 4F), ISD card states, P1=60 - EXT AUTH: CLA 84, P1 security level, Lc=10 - INT AUTH: CLA 00 Toolkit (TS 102 226): - MSL: 01 <msl> -> 00 (no check) or 02 01 <msl> - SIM CA wrapped in EF: C9 00 EF <len> CA... - TAR length %3 validation - Menu IDs <= 7F validation Script Chaining (TS 102 226 Table 5.9a): - Removed Script ID + Additional Data fields - Emit strictly 83 01 <flags> - Deleted generateScriptIdHint Privilege label: Token Verification -> Token Management
293 lines
8.0 KiB
JavaScript
293 lines
8.0 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 extractFunc(src, name) {
|
|
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
|
const m = re.exec(src);
|
|
if (!m) throw new Error('function ' + name + ' not found');
|
|
let i = m.index + m[0].length - 1;
|
|
let depth = 0;
|
|
for (; i < src.length; i++) {
|
|
if (src[i] === '{') depth++;
|
|
else if (src[i] === '}') {
|
|
depth--;
|
|
if (depth === 0) break;
|
|
}
|
|
}
|
|
return src.slice(m.index, i + 1);
|
|
}
|
|
|
|
class StubEl {
|
|
constructor(value = '') {
|
|
this.value = value;
|
|
this.checked = false;
|
|
this.style = {};
|
|
}
|
|
}
|
|
|
|
const els = {};
|
|
function el(id, value = '') {
|
|
if (!els[id]) els[id] = new StubEl(value);
|
|
return els[id];
|
|
}
|
|
|
|
function reset() {
|
|
for (const id of Object.keys(els)) delete els[id];
|
|
}
|
|
|
|
const FNS = ['berLenStr', 'buildApdu', 'genRam'];
|
|
let code = '';
|
|
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
|
eval(code);
|
|
|
|
// document.getElementById stub: global getElementById in the extracted functions
|
|
const doc = {
|
|
getElementById: (id) => {
|
|
if (!els[id]) els[id] = new StubEl();
|
|
return els[id];
|
|
},
|
|
};
|
|
// genRam also references document.getElementById via global document
|
|
global.document = doc;
|
|
|
|
function set(id, v) { el(id).value = v; }
|
|
function setChecked(id, v) { el(id).checked = v; }
|
|
|
|
function genRamResult() {
|
|
genRam();
|
|
return els['ram-result'].value;
|
|
}
|
|
|
|
function genRamApdu() {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ea');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '2');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '2');
|
|
set('ram-tk-lastid', '02');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
return genRamResult();
|
|
}
|
|
|
|
test('UICC toolkit nested inside EA (m=2, services 0)', () => {
|
|
const apdu = genRamApdu();
|
|
assert.ok(apdu.includes('EA13801100000002010102020002011603B0000100'), apdu);
|
|
});
|
|
|
|
test('UICC m=1 emits single pair', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ea');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '1');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '0');
|
|
set('ram-tk-lastid', '00');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EA11800F0000000101010002011603B0000100'), apdu);
|
|
});
|
|
|
|
test('UICC m=3 fills middle pair with 0000', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ea');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '3');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '3');
|
|
set('ram-tk-lastid', '03');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EA158013000000030101000003030002011603B0000100'), apdu);
|
|
});
|
|
|
|
test('UICC services 7 appended as final byte', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ea');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '2');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '2');
|
|
set('ram-tk-lastid', '02');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '7');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EA13801100000002010102020002011603B0000107'), apdu);
|
|
});
|
|
|
|
test('SIM (CA) access domain FIRST, no services byte', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ca');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '2');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '2');
|
|
set('ram-tk-lastid', '02');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '5A');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EF14CA12015A00000002010102020002011603B00001'), apdu);
|
|
});
|
|
|
|
test('SIM (CA) blank access domain emits length byte 00', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ca');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '2');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '2');
|
|
set('ram-tk-lastid', '02');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EF13CA110000000002010102020002011603B00001'), apdu);
|
|
});
|
|
|
|
test('SIM (CA) m=3, no TAR, blank access domain', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ca');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '3');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '3');
|
|
set('ram-tk-lastid', '03');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', '');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EF12CA1000000000030101000003030002011600'), apdu);
|
|
});
|
|
|
|
test('UICC m=60 uses long-form BER lengths (EA 81 87 / inner 81 84)', () => {
|
|
reset();
|
|
set('ram-cmd', 'install-install');
|
|
set('ram-aid', 'A000000151000000');
|
|
set('ram-priv', '00');
|
|
setChecked('ram-toolkit-enable', true);
|
|
set('ram-tk-mode', 'ea');
|
|
set('ram-tk-priority', '0');
|
|
set('ram-tk-timers', '0');
|
|
set('ram-tk-textlen', '0');
|
|
set('ram-tk-menus', '60');
|
|
set('ram-tk-firstpos', '1');
|
|
set('ram-tk-firstid', '01');
|
|
set('ram-tk-lastpos', '60');
|
|
set('ram-tk-lastid', '3C');
|
|
set('ram-tk-channels', '0');
|
|
set('ram-tk-msl', '16');
|
|
set('ram-tk-tar', 'B00001');
|
|
set('ram-tk-ad', '');
|
|
set('ram-tk-services', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.includes('EA8188808185'), apdu);
|
|
});
|
|
|
|
test('LOAD P1 fixed to 80 (last block)', () => {
|
|
reset();
|
|
set('ram-cmd', 'load');
|
|
set('ram-data', 'AABBCC');
|
|
set('ram-block', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.startsWith('80E88000'), apdu);
|
|
});
|
|
|
|
test('DELETE: P1=00, mode in P2', () => {
|
|
reset();
|
|
set('ram-cmd', 'delete');
|
|
set('ram-aid', 'AA1902BC225501');
|
|
set('ram-del-mode', '00');
|
|
let apdu = genRamResult();
|
|
assert.ok(apdu.startsWith('80E40000'), apdu);
|
|
|
|
set('ram-del-mode', '80');
|
|
apdu = genRamResult();
|
|
assert.ok(apdu.startsWith('80E40080'), apdu);
|
|
});
|
|
|
|
test('STORE DATA ram-enc P1 values 00/40/80/C0/E0', () => {
|
|
for (const [enc, p1] of [['00','00'],['40','40'],['80','80'],['C0','C0'],['E0','E0']]) {
|
|
reset();
|
|
set('ram-cmd', 'store-data');
|
|
set('ram-data', 'AABB');
|
|
set('ram-enc', enc);
|
|
set('ram-block', '0');
|
|
const apdu = genRamResult();
|
|
assert.ok(apdu.startsWith('80E2' + p1 + '00'), enc + ' -> P1 ' + p1);
|
|
}
|
|
}); |