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:
@@ -22,6 +22,13 @@ def _resolver(name):
|
||||
return {n.upper(): c for c, n in S.PROACTIVE_TYPE_NAMES.items()}.get(name.upper())
|
||||
|
||||
|
||||
def preset_tars(isd='000000', msl='16'):
|
||||
"""The three mandatory role entries of a card preset's TAR table."""
|
||||
return [{'role': 'isd', 'tar': isd, 'msl': msl, 'desc': ''},
|
||||
{'role': 'uiccRfm', 'tar': 'B00000', 'msl': '16', 'desc': ''},
|
||||
{'role': 'usimRfm', 'tar': 'B00001', 'msl': '16', 'desc': ''}]
|
||||
|
||||
|
||||
class TestValidation(unittest.TestCase):
|
||||
def test_minimal_script_normalises(self):
|
||||
script = T.normalise_script({'name': 'demo', 'steps': [
|
||||
@@ -354,8 +361,10 @@ class TestRunnerActions(RunnerTestCase):
|
||||
def test_scp80_uses_the_preset_and_advances_the_counter(self):
|
||||
scc = FakeScc()
|
||||
server = FakeServer(scc)
|
||||
# the ISD entry carries a distinctive TAR: the assertion below must
|
||||
# fail if the runner ever falls back to a built-in default
|
||||
preset = {'kic': '15', 'kid': '15', 'kicKey': 'AA' * 16, 'kidKey': 'BB' * 16,
|
||||
'counter': '0000000A', 'tar': 'B00000', 'spi1': '16', 'spi2': '01'}
|
||||
'counter': '0000000A', 'tars': preset_tars(isd='B00000')}
|
||||
with mock.patch.object(S, '_build_secured_packet',
|
||||
return_value=('AA' * 10, {})) as build, \
|
||||
mock.patch.object(S, '_send_secured_packet',
|
||||
@@ -401,7 +410,7 @@ class TestRunnerActions(RunnerTestCase):
|
||||
def test_scp80_step_overrides_tar_and_spi_only(self):
|
||||
scc = FakeScc()
|
||||
preset = {'kic': '15', 'kid': '15', 'kicKey': 'AA' * 16, 'kidKey': 'BB' * 16,
|
||||
'counter': '00000001', 'tar': 'B00000', 'spi1': '16', 'spi2': '01'}
|
||||
'counter': '00000001', 'tars': preset_tars()}
|
||||
with mock.patch.object(S, '_build_secured_packet', return_value=('AA' * 10, {})) as build, \
|
||||
mock.patch.object(S, '_send_secured_packet',
|
||||
return_value={'success': True, 'sw': '9000',
|
||||
@@ -421,9 +430,9 @@ class TestRunnerActions(RunnerTestCase):
|
||||
]}, S._test_command_type)
|
||||
self.assertIn('incomplete', S._test_preset_error(script, {}) or '')
|
||||
full = {'kic': '15', 'kid': '15', 'kicKey': 'AA', 'kidKey': 'BB',
|
||||
'counter': '00000000', 'tar': 'B00000', 'spi1': '16', 'spi2': '01'}
|
||||
'counter': '00000000', 'tars': preset_tars()}
|
||||
self.assertIsNone(S._test_preset_error(script, full))
|
||||
no_tar = dict(full, tar='')
|
||||
no_tar = dict(full, tars=[])
|
||||
self.assertIn('TAR', S._test_preset_error(script, no_tar) or '')
|
||||
|
||||
def test_file_write_and_read_actions(self):
|
||||
|
||||
Reference in New Issue
Block a user