fix: SCP80/RAM re-read the preset before every operation; no counter bump on a rejected send (v3.6.3)
The SCP80/RAM forms hold a copy of the preset (counter, keys, TAR, SPI). Editing the preset on the Cards tab saved correctly, but the form kept the old copy: the operation sent the stale counter (the card answers cntr_low) and the post-send sync wrote the stale value back over the preset - the saved counter silently reverted. Reproduced in a real DOM: after select in SCP80: preset=0000000001 sp=0000000001 after Cards edit+save: preset=00000000AA sp=0000000001 after a sync: preset=0000000001 (edit lost) - `cardsApply()` split into `cardsApplyFields()` (field copy, no packet) and `cardsApply()` = fields + genSp; new `spRefreshFromPreset(selId)` re-reads the selected preset from `sp-card-sel` / `ram-card-sel` and re-applies it. - `pysimSendOta()` and `ramExecute()` call it before starting, so every SCP80/RAM operation uses the preset as it is now. - A rejected send no longer advances the counter: new `spPorAccepted(por)` gates the advance+write-back in `pysimSendOta`, the Explore pagination and its GET DATA step, and the server's RAM install (`_ram_next_cntr`: advance only for `por_ok`/`no_por` steps). A failed install still returns `final_cntr` (the accepted prefix) and the PWA persists it, so a retry never replays a counter the card already consumed. - tests: cards_counter.test.js (the stale-form regression, RAM selector, fields-without-genSp, spPorAccepted) and `_ram_next_cntr` cases; the cards_form/ram harnesses updated for the split. - docs/api.md counter semantics; AGENTS preset-source-of-truth rule. 607 frontend / 488 Python green; version 3.6.3; sw simple-v276.
This commit is contained in:
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.6.2'
|
||||
VERSION = '3.6.3'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -1121,6 +1121,16 @@ def _por_remote_sw(por):
|
||||
return ''
|
||||
|
||||
|
||||
def _ram_next_cntr(cntr, advance):
|
||||
"""Advance the SCP80 counter by one only when the card accepted the
|
||||
packet (PoR ok / no PoR expected): a rejected packet (cntr_low,
|
||||
rc_cc_ds_failed, ...) leaves the card's expectation and the preset
|
||||
counter untouched."""
|
||||
if not advance:
|
||||
return cntr
|
||||
return '%010X' % ((int(cntr, 16) + 1) % (2 ** 32))
|
||||
|
||||
|
||||
def _ram_step_result(step_name, last_sw, por, por_hex, bytes_, segments):
|
||||
"""Assemble a RAM-install step record and its failure reason.
|
||||
|
||||
@@ -6105,8 +6115,9 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
step.get('por_sw', '-'),
|
||||
(' error=%s' % step_error) if step_error else '',
|
||||
result['bytes'], result['segments']))
|
||||
# Increment counter
|
||||
cntr = '%010X' % ((int(cntr, 16) + 1) % (2 ** 32))
|
||||
# Advance the counter only for an accepted packet
|
||||
cntr = _ram_next_cntr(
|
||||
cntr, step.get('por_status') in ('por_ok', 'no_por'))
|
||||
if step_error:
|
||||
failure['error'] = '%s: %s' % (step_name, step_error)
|
||||
return False
|
||||
@@ -6134,6 +6145,7 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
resp = {'success': False, 'steps': steps, 'failed_step': len(steps),
|
||||
'error': (encode_error or failure.get('error')
|
||||
or ('%s failed' % step_name)),
|
||||
'final_cntr': cntr,
|
||||
'load_file_aid': loadfile_aid, 'module_aid': module_aid,
|
||||
'load_block_size': block_size,
|
||||
'load_block_size_requested': block_size_req,
|
||||
|
||||
Reference in New Issue
Block a user