v3.26.0 review: the keyless-TAR path

An MSL 00 TAR without a default keyset is a legitimate keyless entry, but
_resolve_operation_keyset left the request's KIc/KID empty and the RAM /
APDU builds failed with "invalid literal for int() with base 16: ''".
The resolution now fills the spec's no-security 00/00 KIc/KID form for a
preset entry without a keyset, and refuses a TAR outside the preset with
a hint (the old silent '25' default was arbitrary).  The PWA's TAR change
resets the form the same way - a stale previous TAR's KIc/KID would
otherwise travel explicitly and build a non-keyless packet.

Also: the dead _preset_tar_keyset helper removed; tests for both keyless
paths.  892 frontend + 886 Python; sw cache simple-v435.
This commit is contained in:
2026-10-14 12:32:04 +03:00
parent cb40662e4e
commit 8dfbb02c93
5 changed files with 101 additions and 11 deletions
+4
View File
@@ -5075,6 +5075,10 @@ function spTarChanged() {
if (entry) {
const ks = entry.keyset ? spKeysetFor(preset, entry.keyset) : null;
if (ks) spKeysetApply(ks);
// A TAR without a default keyset (MSL 00) gets the spec's no-security
// values - a stale previous TAR's KIc/KID would otherwise travel
// explicitly and build a non-keyless packet (v3.26.0 review).
else spKeysetApply({ kic: '00', kid: '00', kicKey: '', kidKey: '' });
if (entry.msl) spSpi1Seed(entry.msl);
}
spInvalidate();
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v434';
const CACHE = 'simple-v435';
const URLS = [
'index.html',
'help.html',
+39 -1
View File
@@ -25,7 +25,8 @@ let code = '';
for (const fn of ['spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spTarOptionsHtml', 'spTarRender', 'spSpi1Seed', 'spTarKeysetGuard', 'cardsTarKeysetCell', 'cardsTarKeysetFromForm', 'cardsTarKeysetRefresh', 'cardsTarRowsRender', 'cardsTarRowHtml', 'cardsTarsFromTable', 'cardsFreeTarsFromForm', 'spCounterTracked', 'spPresetIdx', 'ramPresetIdx',
'tarPresetIdx', 'spKeysetSync', 'ramRender', 'esc', 'escHtml',
'cardsTarList', 'cardsTarEntry', 'cardsRoleTar']) {
'cardsTarList', 'cardsTarEntry', 'cardsRoleTar',
'spTarChanged', 'spKeysetApply', 'updateSpKic', 'updateSpKid']) {
code += extractFunc(html, fn) + '\n';
}
code += 'function t(s){return s;}\n';
@@ -180,6 +181,43 @@ test('the RAM and TAR views rebuild their keyset lists when their preset is know
assert.ok(html.includes("if (name === 'tar') { tarRender(); tarProbeRender(); }"));
});
test('spTarChanged applies the TAR keyset and clears it for a keyless TAR', () => {
// v3.26.0 review: a TAR without a default keyset (MSL 00) must reset the
// form to the no-security '00/00' form - a stale previous TAR's KIc/KID
// would otherwise travel explicitly and build a non-keyless packet
globalThis.updateKeyPlaceholder = () => {};
globalThis.spInvalidate = () => {};
globalThis.updateSp = () => {};
globalThis.spCntrPlaceholder = () => {};
const els = {
'sp-tar': { value: '', disabled: false, innerHTML: '', options: [] },
'sp-card-sel': { value: '0' },
'sp-kic-idx': { value: '' }, 'sp-kic-alg': { value: '' }, 'sp-kic-hex': { value: '' },
'sp-kid-idx': { value: '' }, 'sp-kid-alg': { value: '' }, 'sp-kid-hex': { value: '' },
'sp-kic-key': { value: '' }, 'sp-kid-key': { value: '' }, 'sp-cntr': { value: 'X' },
};
globalThis.document = { getElementById: id => els[id] || null };
globalThis.cards = [{ name: 'EP', keysets: [
{ kic: '15', kid: '15', kicKey: 'AA', kidKey: 'BB', cntr: '0000000001' }],
tars: [
{ role: 'isd', tar: '000000', msl: '16', keyset: 1 },
{ tar: 'B00011', msl: '00' }] }];
// a keyset TAR applies its KIc/KID and keys (the counter override clears)
els['sp-tar'].value = '000000';
spTarChanged();
assert.strictEqual(els['sp-kic-hex'].value, '15');
assert.strictEqual(els['sp-kid-hex'].value, '15');
assert.strictEqual(els['sp-kic-key'].value, 'AA');
assert.strictEqual(els['sp-cntr'].value, '');
// a keyless TAR (MSL 00, no default keyset) resets to the no-security form
els['sp-kic-key'].value = 'AA';
els['sp-tar'].value = 'B00011';
spTarChanged();
assert.strictEqual(els['sp-kic-hex'].value, '00');
assert.strictEqual(els['sp-kid-hex'].value, '00');
assert.strictEqual(els['sp-kic-key'].value, '');
});
test('the TAR selector waits for a preset and lists its entries', () => {
const els = {
'sp-tar': { value: '', disabled: false, innerHTML: '', options: [] },