v3.28.2: the outfile ADM encoding fix

The ADM column carries the key's hex value (the length varies, 4-16 hex
digits) - the form the preset store and /api/verify-adm use (pySim pads
it with 'f' to the 8 CHV bytes).  The parser treated it as an ASCII
string hex-encoded and either mangled it (3632333239463337 -> 62329F37,
a wrong ADM that would fail VERIFY and consume an attempt) or dropped it
(CFA3DD9C6B7F0F8C - the live report with AM0027_alfa_dsa.out).

- The ADM is stored as-is: non-hex stripped, uppercased, [0-9A-F]{4,16};
  a non-hex or too-short value is dropped with "ADM is not 4-16 hex
  digits".
- _ascii_from_hex() is gone (its only user was the ADM column).
- Docs: help EN/RU, api.md and the repo AGENTS now say "the key's hex
  value, 4-16 hex digits (the length varies)".

Note: presets imported from a BAP-style file before this fix hold the
mangled ADM - re-import or correct them.

Tests: the BAP fixture (the hex as-is), the AM0027 shape, a
short/lowercase value and the drop cases.  Both real samples verified
(BAP 3632333239463337, AM0027 CFA3DD9C6B7F0F8C; the ALFA sample has no
ADM column and is unchanged).  915 frontend + 925 Python; sw cache
simple-v440.
This commit is contained in:
2026-10-14 18:07:04 +03:00
parent 91996a941b
commit 09d8a12add
9 changed files with 45 additions and 25 deletions
+9 -14
View File
@@ -8,8 +8,9 @@ prefix) - and continues with one whitespace-separated row per card. The
header and every column the presets do not use are ignored.
Recognised columns (case-insensitive): ``ICCID``, ``PIN1``/``PIN2``,
``PUK1``/``PUK2``, ``ADM`` (the ASCII string, hex-encoded - the vendors print
``3632333239463337`` for ``62329F37``), ``PSK`` (the SCP81 key, 16 bytes; an
``PUK1``/``PUK2``, ``ADM`` (the key's hex value - the length varies, e.g. 8 or
16 hex digits; this is the form the preset store and ``/api/verify-adm`` use,
where pySim pads it with 'f' to the 8 CHV bytes), ``PSK`` (the SCP81 key, 16 bytes; an
optional ``PSKID``/``PSK_ID`` column names its identity, defaulting to the
card's ICCID) and ``KICn``/``KIDn`` - one keyset per numbered pair, up to 15.
Everything else (``IMSI*``, ``EKI``, ``KIKn`` - the key-management key, no
@@ -175,14 +176,6 @@ def _hex_value(tok):
return v if v and len(v) % 2 == 0 else ''
def _ascii_from_hex(v):
"""The ADM column: an ASCII string hex-encoded. '' when it is not."""
try:
return bytes.fromhex(v).decode('ascii')
except (ValueError, UnicodeDecodeError):
return ''
def parse_outfile(text, name='', algorithm16='3des2', tars=None, tar_keyset=None):
"""Parse an outfile into preset dicts (the store's shape).
@@ -273,11 +266,13 @@ def parse_outfile(text, name='', algorithm16='3des2', tars=None, tar_keyset=None
% (row_no, col, raw))
i = idx['ADM']
if 0 <= i < len(toks) and toks[i]:
adm = _ascii_from_hex(_hex_value(toks[i]))
if re.fullmatch(r'[0-9A-Fa-f]{4,16}', adm or ''):
entry['adm'] = adm.upper()
# The ADM column carries the key's hex value itself (the length
# varies) - the form the preset store and /api/verify-adm use.
adm = re.sub(r'[^0-9A-Fa-f]', '', str(toks[i])).upper()
if re.fullmatch(r'[0-9A-F]{4,16}', adm):
entry['adm'] = adm
else:
skip('line %d: ADM is not an ASCII-hex key - dropped' % row_no)
skip('line %d: ADM is not 4-16 hex digits - dropped' % row_no)
i = idx['PSK']
if 0 <= i < len(toks) and toks[i]:
psk = _hex_value(toks[i])
+1 -1
View File
@@ -37,7 +37,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.28.1'
VERSION = '3.28.2'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE