v3.28.3: the outfile ADM byte-pair rule
The review of v3.28.2 found the parser accepting an odd-length ADM (e.g.
12345), while /api/verify-adm requires byte pairs
((?:[0-9A-F]{2}){2,8}) - the stored value could never verify and was
refused with a misleading "adm must be 4-16 hex digits".
The parser now applies the verifier's exact rule: an odd length (a half
byte) or a value over 16 digits is dropped at import time with "ADM is
not 4-16 hex digits in byte pairs".
Docs: help EN/RU, api.md and the repo AGENTS say "4-16 hex digits in
byte pairs". Tests: the odd-length and the over-16 drop cases (both
real samples are even-length and unaffected: BAP 3632333239463337,
AM0027 CFA3DD9C6B7F0F8C). 915 frontend + 925 Python; sw cache
simple-v441.
This commit is contained in:
+2
-2
@@ -345,8 +345,8 @@ Recognised columns (case-insensitive): `ICCID` (the digits + odd-nibble F
|
||||
pad, the plain digits, spaced digits or the raw nibble-swapped EF hex all
|
||||
normalise to the same value), `PIN1`/`PIN2`, `PUK1`/`PUK2` (4–8 digits; an
|
||||
invalid value is dropped and reported, the card is kept), `ADM` (the key's
|
||||
**hex value**, 4–16 hex digits — the length varies; this is the form the preset
|
||||
store and `/api/verify-adm` use), `PSK` (the SCP81 key,
|
||||
**hex value**, 4–16 hex digits in byte pairs — the rule `/api/verify-adm`
|
||||
applies), `PSK` (the SCP81 key,
|
||||
exactly 32 hex chars) and `KICn`/`KIDn` — one keyset per numbered pair, up to
|
||||
15. The **KIc/KID bytes are never in the file**: the keyset number is the
|
||||
column number (`KIC2` → keyset 2) and the algorithm nibble follows the key
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-sm mb-3">Столбец <strong>SCP81</strong> показывает, задана ли в пресете рабочая пара PSK: <strong>✓</strong> (зелёный) — идентификатор и ключ заданы; <strong>⚠</strong> (жёлтый) — задано только одно из двух, и слушатель такой пресет игнорирует; <strong>—</strong> — PSK нет. Идентификатор и ключ PSK задаются вместе. Кнопка <strong>Добавить пресет</strong> открывает редактор с новым пресетом; <strong>Редактировать</strong> загружает пресет строки в редактор, поэтому поля можно менять без повторного ввода карты; <strong>Сохранить</strong> сохраняет и возвращает к списку, <strong>Отмена</strong> отменяет правки; <strong>Удалить</strong> удаляет строку (только в списке). Обмен пресетами: <strong>Экспорт в файл</strong> для выгрузки и <strong>Импорт из файла</strong> для загрузки. Выбранный пресет автоматически заполняет форму Secured Packet и устанавливает TAR для операции (RAM/GP → ISD TAR, SIM RFM → UICC RFM TAR, USIM RFM → ADF RFM TAR); TAR, введённый в форму Secured Packet вручную, сохраняется при отправке - счётчик, ключи и MSL этого TAR по-прежнему берутся из пресета; SPI1 берётся из MSL, а меньшее значение даёт предупреждение (сервер тоже возвращает его как <code class="font-mono text-sm">warning</code>); изменения сразу передаются работающему слушателю SCP81.</p>
|
||||
<p class="text-sm mb-3"><strong>Импорт outfile</strong> загружает сразу много карт из <em>outfile</em> поставщика карт — текстового дампа инструмента персонализации: шапка с баннером и параметрами, затем разделённая пробелами <strong>строка колонок</strong> (среди токенов есть <code class="font-mono text-sm">ICCID</code>; первый токен может нести префикс вида <code class="font-mono text-sm">var_out:</code>) и по одной строке на карту. Всё до строки колонок и все неиспользуемые колонки игнорируются. Разбираются <code class="font-mono text-sm">ICCID</code> (цифры, цифры с добивочным <code class="font-mono text-sm">F</code> до чётного числа нибблов, цифры с разделителями или сырой hex EF — всё приводится к одной форме), <code class="font-mono text-sm">PIN1</code>/<code class="font-mono text-sm">PIN2</code>/<code class="font-mono text-sm">PUK1</code>/<code class="font-mono text-sm">PUK2</code>, <code class="font-mono text-sm">ADM</code> (hex-значение ключа, 4–16 hex-цифр — длина различается, например <code class="font-mono text-sm">3632333239463337</code>), <code class="font-mono text-sm">PSK</code> (16 байт; необязательная колонка <code class="font-mono text-sm">PSKID</code>/<code class="font-mono text-sm">PSK_ID</code> задаёт её идентификатор, по умолчанию берётся ICCID карты) и нумерованные пары ключей <code class="font-mono text-sm">KICn</code>/<code class="font-mono text-sm">KIDn</code> — по одному keyset на пару, до 15. <strong>Байтов KIc/KID в файле нет</strong>: номер keyset — это номер колонки (<code class="font-mono text-sm">KIC2</code> → keyset 2), а ниббл алгоритма определяется размером ключа — 8 байт DES, 24 байта 3DES3, а для <strong>16 байт</strong> — выбором в предпросмотре: <strong>3DES2</strong> (по умолчанию) или <strong>AES</strong>; любой другой размер пропускает этот keyset с пометкой. TAR также не берутся из файла: предпросмотр показывает таблицу TAR, которую получат импортируемые карты (по умолчанию три ролевые записи с TAR по спецификации и MSL 16; их можно править, кнопка <strong>+ TAR</strong> добавляет записи), и один <strong>keyset по умолчанию</strong>, применяемый ко всем записям TAR — он обязателен для карты с несколькими keysets (ненулевой MSL должен называть keyset). <strong>Общее имя</strong> применяется ко всем импортируемым картам (пустое — берётся ICCID); <strong>режим импорта</strong> выбирает <strong>Дополнить</strong> (по умолчанию — существующие пресеты сохраняются, дубли ICCID пропускаются и перечисляются) или <strong>Заменить</strong> (хранилище сначала очищается). Предпросмотр перечисляет разобранные карты (ICCID, keysets, найденные коды) и каждую пропущенную строку/ключ с причиной — до того, как что-либо сохранено.</p>
|
||||
<p class="text-sm mb-3"><strong>Импорт outfile</strong> загружает сразу много карт из <em>outfile</em> поставщика карт — текстового дампа инструмента персонализации: шапка с баннером и параметрами, затем разделённая пробелами <strong>строка колонок</strong> (среди токенов есть <code class="font-mono text-sm">ICCID</code>; первый токен может нести префикс вида <code class="font-mono text-sm">var_out:</code>) и по одной строке на карту. Всё до строки колонок и все неиспользуемые колонки игнорируются. Разбираются <code class="font-mono text-sm">ICCID</code> (цифры, цифры с добивочным <code class="font-mono text-sm">F</code> до чётного числа нибблов, цифры с разделителями или сырой hex EF — всё приводится к одной форме), <code class="font-mono text-sm">PIN1</code>/<code class="font-mono text-sm">PIN2</code>/<code class="font-mono text-sm">PUK1</code>/<code class="font-mono text-sm">PUK2</code>, <code class="font-mono text-sm">ADM</code> (hex-значение ключа, 4–16 hex-цифр парами байт — например <code class="font-mono text-sm">3632333239463337</code>), <code class="font-mono text-sm">PSK</code> (16 байт; необязательная колонка <code class="font-mono text-sm">PSKID</code>/<code class="font-mono text-sm">PSK_ID</code> задаёт её идентификатор, по умолчанию берётся ICCID карты) и нумерованные пары ключей <code class="font-mono text-sm">KICn</code>/<code class="font-mono text-sm">KIDn</code> — по одному keyset на пару, до 15. <strong>Байтов KIc/KID в файле нет</strong>: номер keyset — это номер колонки (<code class="font-mono text-sm">KIC2</code> → keyset 2), а ниббл алгоритма определяется размером ключа — 8 байт DES, 24 байта 3DES3, а для <strong>16 байт</strong> — выбором в предпросмотре: <strong>3DES2</strong> (по умолчанию) или <strong>AES</strong>; любой другой размер пропускает этот keyset с пометкой. TAR также не берутся из файла: предпросмотр показывает таблицу TAR, которую получат импортируемые карты (по умолчанию три ролевые записи с TAR по спецификации и MSL 16; их можно править, кнопка <strong>+ TAR</strong> добавляет записи), и один <strong>keyset по умолчанию</strong>, применяемый ко всем записям TAR — он обязателен для карты с несколькими keysets (ненулевой MSL должен называть keyset). <strong>Общее имя</strong> применяется ко всем импортируемым картам (пустое — берётся ICCID); <strong>режим импорта</strong> выбирает <strong>Дополнить</strong> (по умолчанию — существующие пресеты сохраняются, дубли ICCID пропускаются и перечисляются) или <strong>Заменить</strong> (хранилище сначала очищается). Предпросмотр перечисляет разобранные карты (ICCID, keysets, найденные коды) и каждую пропущенную строку/ключ с причиной — до того, как что-либо сохранено.</p>
|
||||
</section>
|
||||
|
||||
<section class="mb-10">
|
||||
|
||||
+1
-1
@@ -368,7 +368,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-sm mb-3">The <strong>SCP81</strong> column shows whether the preset supplies a usable PSK pair: <strong>✓</strong> (green) — identity and key set; <strong>⚠</strong> (amber) — only one of the two, so the listener ignores the preset; <strong>—</strong> — no PSK. PSK identity and key must be set together. <strong>Add preset</strong> opens the editor with a fresh preset; <strong>Edit</strong> loads the row's preset into it so fields can be changed without re-entering the card; <strong>Save</strong> stores it and returns to the list, <strong>Cancel</strong> discards the edits; <strong>Remove</strong> deletes the row (list only). Presets can be shared with <strong>Export to file</strong> and restored with <strong>Import from file</strong>. The selected card preset auto-fills the Secured Packet form and sets the TAR for the operation (RAM/GP → ISD TAR, SIM RFM → UICC RFM TAR, USIM RFM → ADF RFM TAR); a TAR typed into the Secured Packet form by hand is kept when sending - the counter, keys and the TAR's MSL are still re-read from the preset; the SPI1 is seeded from the TAR's MSL and a lower value warns (the server returns it as a <code class="font-mono text-sm">warning</code> too); edits are pushed into a running SCP81 listener automatically.</p>
|
||||
<p class="text-sm mb-3"><strong>Import outfile</strong> imports many cards at once from a card vendor's <em>outfile</em> — the personalization tool's plain-text dump: a banner and a configuration header, then a whitespace-separated <strong>column line</strong> (the tokens include <code class="font-mono text-sm">ICCID</code>; the first token may carry a <code class="font-mono text-sm">var_out:</code>-style prefix) and one row per card. Everything before the column line and every unused column is ignored. The parser reads <code class="font-mono text-sm">ICCID</code> (digits, the digits with an odd-nibble <code class="font-mono text-sm">F</code> pad, spaced digits or the raw EF hex — all normalised to one form), <code class="font-mono text-sm">PIN1</code>/<code class="font-mono text-sm">PIN2</code>/<code class="font-mono text-sm">PUK1</code>/<code class="font-mono text-sm">PUK2</code>, <code class="font-mono text-sm">ADM</code> (the key's hex value, 4–16 hex digits — the length varies, e.g. <code class="font-mono text-sm">3632333239463337</code>), <code class="font-mono text-sm">PSK</code> (16 bytes; an optional <code class="font-mono text-sm">PSKID</code>/<code class="font-mono text-sm">PSK_ID</code> names its identity, defaulting to the card's ICCID) and the numbered key pairs <code class="font-mono text-sm">KICn</code>/<code class="font-mono text-sm">KIDn</code> — one keyset per pair, up to 15. The <strong>KIc/KID bytes are never in the file</strong>: the keyset number is the column number (<code class="font-mono text-sm">KIC2</code> → keyset 2) and the algorithm nibble follows the key size — 8 bytes DES, 24 bytes 3DES3, and for <strong>16 bytes</strong> the preview's choice, <strong>3DES2</strong> (default) or <strong>AES</strong>; any other size skips that keyset with a note. The TARs never come from the file either: the preview shows the TAR table the imported cards get (the three role entries with their spec TARs and MSL 16 by default, editable, with <strong>+ TAR</strong> for extra entries) and one <strong>default keyset</strong> applied to every TAR entry — required for a card with several keysets (a non-zero MSL must name a keyset). A <strong>common name</strong> is applied to every imported card (empty falls back to the ICCID); <strong>Import mode</strong> chooses <strong>Merge</strong> (default — existing presets are kept, duplicate ICCIDs are skipped and reported) or <strong>Replace</strong> (the store is wiped first). The preview lists the parsed cards (ICCID, keysets, the codes found) and every skipped row/key with its reason before anything is stored.</p>
|
||||
<p class="text-sm mb-3"><strong>Import outfile</strong> imports many cards at once from a card vendor's <em>outfile</em> — the personalization tool's plain-text dump: a banner and a configuration header, then a whitespace-separated <strong>column line</strong> (the tokens include <code class="font-mono text-sm">ICCID</code>; the first token may carry a <code class="font-mono text-sm">var_out:</code>-style prefix) and one row per card. Everything before the column line and every unused column is ignored. The parser reads <code class="font-mono text-sm">ICCID</code> (digits, the digits with an odd-nibble <code class="font-mono text-sm">F</code> pad, spaced digits or the raw EF hex — all normalised to one form), <code class="font-mono text-sm">PIN1</code>/<code class="font-mono text-sm">PIN2</code>/<code class="font-mono text-sm">PUK1</code>/<code class="font-mono text-sm">PUK2</code>, <code class="font-mono text-sm">ADM</code> (the key's hex value, 4–16 hex digits in byte pairs — e.g. <code class="font-mono text-sm">3632333239463337</code>), <code class="font-mono text-sm">PSK</code> (16 bytes; an optional <code class="font-mono text-sm">PSKID</code>/<code class="font-mono text-sm">PSK_ID</code> names its identity, defaulting to the card's ICCID) and the numbered key pairs <code class="font-mono text-sm">KICn</code>/<code class="font-mono text-sm">KIDn</code> — one keyset per pair, up to 15. The <strong>KIc/KID bytes are never in the file</strong>: the keyset number is the column number (<code class="font-mono text-sm">KIC2</code> → keyset 2) and the algorithm nibble follows the key size — 8 bytes DES, 24 bytes 3DES3, and for <strong>16 bytes</strong> the preview's choice, <strong>3DES2</strong> (default) or <strong>AES</strong>; any other size skips that keyset with a note. The TARs never come from the file either: the preview shows the TAR table the imported cards get (the three role entries with their spec TARs and MSL 16 by default, editable, with <strong>+ TAR</strong> for extra entries) and one <strong>default keyset</strong> applied to every TAR entry — required for a card with several keysets (a non-zero MSL must name a keyset). A <strong>common name</strong> is applied to every imported card (empty falls back to the ICCID); <strong>Import mode</strong> chooses <strong>Merge</strong> (default — existing presets are kept, duplicate ICCIDs are skipped and reported) or <strong>Replace</strong> (the store is wiped first). The preview lists the parsed cards (ICCID, keysets, the codes found) and every skipped row/key with its reason before anything is stored.</p>
|
||||
</section>
|
||||
|
||||
<section class="mb-10">
|
||||
|
||||
+1
-1
@@ -1910,7 +1910,7 @@
|
||||
// ===== Version =====
|
||||
// Single source of truth for the PWA version: shown in the header and used
|
||||
// by the server version check in pysimConnect().
|
||||
const SIMPLE_VERSION = '3.28.2';
|
||||
const SIMPLE_VERSION = '3.28.3';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== view history (the browser Back/Forward buttons) =====
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v440';
|
||||
const CACHE = 'simple-v441';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pysim-simple-server"
|
||||
version = "3.28.2"
|
||||
version = "3.28.3"
|
||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||
requires-python = ">=3.8"
|
||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||
|
||||
@@ -8,9 +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 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
|
||||
``PUK1``/``PUK2``, ``ADM`` (the key's hex value - 4-16 hex digits in **byte
|
||||
pairs**, the rule ``/api/verify-adm`` applies; this is the form the preset store
|
||||
uses, 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
|
||||
@@ -269,10 +269,14 @@ def parse_outfile(text, name='', algorithm16='3des2', tars=None, tar_keyset=None
|
||||
# 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):
|
||||
# Byte pairs, exactly like /api/verify-adm: an odd length is a half
|
||||
# byte and the verifier would always refuse the stored value
|
||||
# (v3.28.3).
|
||||
if re.fullmatch(r'(?:[0-9A-F]{2}){2,8}', adm):
|
||||
entry['adm'] = adm
|
||||
else:
|
||||
skip('line %d: ADM is not 4-16 hex digits - dropped' % row_no)
|
||||
skip('line %d: ADM is not 4-16 hex digits in byte pairs - dropped'
|
||||
% row_no)
|
||||
i = idx['PSK']
|
||||
if 0 <= i < len(toks) and toks[i]:
|
||||
psk = _hex_value(toks[i])
|
||||
|
||||
@@ -37,7 +37,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.28.2'
|
||||
VERSION = '3.28.3'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
|
||||
@@ -262,6 +262,22 @@ class OptionTests(unittest.TestCase):
|
||||
self.assertNotIn('adm', res['presets'][0])
|
||||
self.assertTrue(any('ADM is not 4-16 hex digits' in s
|
||||
for s in res['report']['skipped']))
|
||||
# an odd length is a half byte: /api/verify-adm would always refuse the
|
||||
# stored value, so it is dropped at import time (v3.28.3)
|
||||
text = ('ICCID KIC1 KID1 ADM\n' + DIGITS_F + ' ' + K16A + ' ' + K16B +
|
||||
' 12345\n')
|
||||
res = outfile.parse_outfile(text)
|
||||
self.assertNotIn('adm', res['presets'][0])
|
||||
self.assertTrue(any('ADM is not 4-16 hex digits in byte pairs' in s
|
||||
for s in res['report']['skipped']))
|
||||
# ...and so is a value over the 16-digit limit (the verifier pads to 8
|
||||
# CHV bytes, a longer value would overflow)
|
||||
text = ('ICCID KIC1 KID1 ADM\n' + DIGITS_F + ' ' + K16A + ' ' + K16B +
|
||||
' ' + 'AB' * 9 + '\n')
|
||||
res = outfile.parse_outfile(text)
|
||||
self.assertNotIn('adm', res['presets'][0])
|
||||
self.assertTrue(any('ADM is not 4-16 hex digits in byte pairs' in s
|
||||
for s in res['report']['skipped']))
|
||||
# a present but invalid PIN is dropped, the card kept
|
||||
text = ('ICCID KIC1 KID1 PIN1\n' + DIGITS_F + ' ' + K16A + ' ' + K16B + ' 12\n')
|
||||
res = outfile.parse_outfile(text)
|
||||
|
||||
Reference in New Issue
Block a user