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, asyncFn) {
|
||||
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
||||
const m = re.exec(src);
|
||||
@@ -46,6 +52,10 @@ code += extractFunc(html, 'pysimUpdatePresetIndicator') + '\n';
|
||||
code += extractFunc(html, 'pysimUpdatePresetIndicators') + '\n';
|
||||
code += extractFunc(html, 'cardsMatchedPreset') + '\n';
|
||||
code += extractFunc(html, 'cardsAdmPresent') + '\n';
|
||||
code += extractConst(html, 'CARDS_TAR_DEFAULTS') + extractConst(html, 'CARDS_TAR_ROLES') + extractConst(html, 'CARDS_MSL_DEFAULT');
|
||||
code += extractFunc(html, 'cardsTarList') + '\n';
|
||||
code += extractFunc(html, 'spKeysetList') + '\n';
|
||||
code += extractFunc(html, 'spKeysetKvnOf') + '\n';
|
||||
code += extractFunc(html, 'cardsScp80Complete') + '\n';
|
||||
code += extractFunc(html, 'cardsScp81Complete') + '\n';
|
||||
code += extractFunc(html, 'pysimSetServerAvailable') + '\n';
|
||||
@@ -339,8 +349,11 @@ test('the ADM badge marks an ADM key in the matching preset', () => {
|
||||
|
||||
test('the header SCP80/SCP81 markers follow the matching preset', () => {
|
||||
const { scp80El, scp81El } = setup();
|
||||
globalThis.cards = [{ name: 'C', kic: '15', kid: '15', spi1: '16', spi2: '01',
|
||||
cntr: '0000000001', kicKey: 'AA', kidKey: 'BB', pskIdentity: 'id', pskKey: 'KEY' }];
|
||||
globalThis.cards = [{ name: 'C', kic: '15', kid: '15',
|
||||
cntr: '0000000001', kicKey: 'AA', kidKey: 'BB', pskIdentity: 'id', pskKey: 'KEY',
|
||||
tars: [{ role: 'isd', tar: '000000', msl: '16' },
|
||||
{ role: 'uiccRfm', tar: 'B00000', msl: '16' },
|
||||
{ role: 'usimRfm', tar: 'B00001', msl: '16' }] }];
|
||||
globalThis.cardsFindByIccid = () => 0;
|
||||
pysimCardStateUpdate(status({ connected: true, card_session: 2, iccid: '89701450001700031958' }));
|
||||
assert.strictEqual(scp80El.textContent, 'SCP80');
|
||||
@@ -356,8 +369,11 @@ test('the header SCP80/SCP81 markers follow the matching preset', () => {
|
||||
|
||||
test('losing the server hides the ADM badge and the preset markers', () => {
|
||||
const { adm, scp80El, scp81El } = setup();
|
||||
globalThis.cards = [{ name: 'C', adm: '0011', kic: '15', kid: '15', spi1: '16', spi2: '01',
|
||||
cntr: '0000000001', kicKey: 'AA', kidKey: 'BB', pskIdentity: 'id', pskKey: 'KEY' }];
|
||||
globalThis.cards = [{ name: 'C', adm: '0011', kic: '15', kid: '15',
|
||||
cntr: '0000000001', kicKey: 'AA', kidKey: 'BB', pskIdentity: 'id', pskKey: 'KEY',
|
||||
tars: [{ role: 'isd', tar: '000000', msl: '16' },
|
||||
{ role: 'uiccRfm', tar: 'B00000', msl: '16' },
|
||||
{ role: 'usimRfm', tar: 'B00001', msl: '16' }] }];
|
||||
globalThis.cardsFindByIccid = () => 0;
|
||||
pysimCardStateUpdate(status({ connected: true, adm_verified: true, iccid: '89701450001700031958' }));
|
||||
assert.ok(!adm.classes.has('hidden'));
|
||||
|
||||
Reference in New Issue
Block a user