v3.10.0: preset TAR table with per-TAR MSL; derive the SPI1 from the TAR
Presets drop the card-wide SPI1/SPI2 and the fixed tar/uiccTar/usimTar fields: a preset now carries `tars` - the three mandatory role entries (isd/uiccRfm/usimRfm, spec defaults pre-settled) plus free TARs with an optional description, each with its MSL (Minimum SPI1, TS 102 226 8.2.1.3.2.4). v3.9.1 presets convert on load/import (the spi1 seeds all three MSLs); duplicate TARs and empty TAR/MSL values are refused; the store schema version is 3. Operations derive a packet's SPI1 from its TAR's MSL (_spi1_for_tar): the RAM ops, the test runner (per-step overrides still win), the counter probe and each TAR-probe row; the plain send keeps an explicit SPI1 with a `warning` when it is below the MSL, and a TAR outside the preset without an explicit SPI1 is refused with a hint. The TAR probe resolves per checked TAR before sending and reports the effective spi1 per row. The secured packet form's SPI2 default is 00 (the RAM/probe operations pin their own). PWA: the Cards form edits the TAR table (role rows with MSL inputs plus Add/Remove free rows with a description), the list shows a TARs (MSL) column, the SCP80 TAR field suggests the preset's entries (datalist) and a preset apply seeds the SPI1 from the operation TAR's MSL with an amber warning below it; the preset-wide SPI1/SPI2 fields and the header marker's SPI requirement are gone (completeness = a keyset + the three role entries with their MSLs). Help EN/RU, docs/api.md and AGENTS updated; sw cache simple-v358. Live: TAR probe 000000 at its MSL 16 (registered, por_ok, counter advanced); a send without spi1 derived 16 (por_ok, counter advanced); a send with spi1 0A returned the warning and the card refused it (6200, no counter consumed); an unknown TAR without spi1 was refused with the hint.
This commit is contained in:
@@ -5,6 +5,12 @@ const path = require('node:path');
|
||||
|
||||
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
||||
|
||||
function extractConst(src, name) {
|
||||
const m = new RegExp('const\\s+' + name + '\\s*=\\s*([^;]+);').exec(src);
|
||||
if (!m) throw new Error('const ' + name + ' not found');
|
||||
return 'var ' + name + ' = ' + m[1] + ';\n';
|
||||
}
|
||||
|
||||
function extractFunc(src, name) {
|
||||
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
||||
const m = re.exec(src);
|
||||
@@ -25,9 +31,13 @@ let code = '';
|
||||
for (const fn of ['swapNibbles', 'encIccid', 'decIccid',
|
||||
'cardsNormIccid', 'cardsFindByIccid', 'cardsFindDuplicateIccid',
|
||||
'cardsIccidFromCard', 'cardsAutoSelectByIccid', 'cardsRender',
|
||||
'esc', 'spKeysetList', 'spKeysetKvnOf', 'cardsTarValue', 'cardsScp81Complete']) {
|
||||
'esc', 'spKeysetList', 'spKeysetKvnOf',
|
||||
'cardsTarList', 'cardsRoleDefault', 'cardsRoleTar', 'cardsRoleMsl',
|
||||
'cardsTarEntry', 'cardsTarMsl', 'cardsTarText', 'cardsScp81Complete']) {
|
||||
code += extractFunc(html, fn) + '\n';
|
||||
}
|
||||
code += extractConst(html, 'CARDS_TAR_DEFAULTS') + extractConst(html, 'CARDS_TAR_ROLES')
|
||||
+ extractConst(html, 'CARDS_MSL_DEFAULT');
|
||||
eval(code);
|
||||
|
||||
// live-card style raw EF.ICCID content (nibble-swapped digits + 'F' pad)
|
||||
@@ -176,8 +186,8 @@ test('cardsRender highlights the equipped card preset', () => {
|
||||
const els = { 'cards-tbody': { innerHTML: '' } };
|
||||
globalThis.document = { getElementById: id => els[id] || null };
|
||||
globalThis.cards = [
|
||||
{ name: 'other', iccid: '1111111111111111111', spi1: '16', spi2: '01', keysets: [] },
|
||||
{ name: 'live', iccid: DIGITS, spi1: '16', spi2: '01', keysets: [] },
|
||||
{ name: 'other', iccid: '1111111111111111111', keysets: [] },
|
||||
{ name: 'live', iccid: DIGITS, keysets: [] },
|
||||
];
|
||||
globalThis._pysimCardIccid = DIGITS;
|
||||
cardsRender();
|
||||
|
||||
Reference in New Issue
Block a user