From 8c90958718b6186029b01835ffad55a6944eb495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Wed, 16 Sep 2026 13:32:26 +0300 Subject: [PATCH] scp81: PSK by identity, scripts page, exact snapshots, SCP80 LOAD fit (v2.2.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cards / SCP81: - Cards is a top-level tab; presets gain PSK identity + key, HTTP-OTA column, Edit/Update and a live PSK-map push into a running listener. - SCP81 has Listener/Scripts pills; scripts are named local APDU lists (Empty / Explore / Install from .cap / Delete templates), sent to the server explicitly at start. The listener takes mode/host/port/script only; PSK inputs and the .cap row are gone. - Multi-PSK TLS listener: identity -> key lookup from the card presets (POST /api/scp81/psk-map updates a running listener), unknown identities log tls-psk-unknown and fail; handshake logs carry psk_match. - Script engine: execution tracking (next/done/pending/results), a resumed dialog sends only the unexecuted tail (unreported APDU is resent), a fresh dialog restarts, listing continuation pages are tracked separately (pending.pages/complete). Restart script button. - POST /api/scp81/gen-install replaces the SCP81 ram-install queueing (generation only; the .cap is never stored). Profiler / snapshots: - Snapshot comparison is always exact (mask checkboxes removed; the first-4-bytes mask remains a profile-creation option). - "matching records" line shows count + #record numbers. - New Clone action: copy named "Copy of ", opened in the editor. - Matched-record count/numbers fix ("1 из 8 (#8)"). SCP80: - Configurable / auto-fitted LOAD block size: each LOAD APDU encodes into one SMS (pySim rejects secured packets above 140 octets, so a 240-byte block could never be sent). Response reports the effective size and clamps; encode failures are reported per step with the pySim message. SW cache otaman-v161; docs/api.md, scp81-findings and help EN/RU updated. Tests: 226 Python + 356 frontend. --- docs/api.md | 137 +++-- docs/scp81-findings.md | 7 +- frontend/help-ru.html | 20 +- frontend/help.html | 18 +- frontend/index.html | 933 ++++++++++++++++++++++++-------- frontend/sw.js | 2 +- frontend/tests/html.test.js | 15 +- frontend/tests/profiler.test.js | 109 ++-- frontend/tests/scripts.test.js | 142 +++++ pyproject.toml | 2 +- pysim_otaman_server/scp81.py | 82 ++- pysim_otaman_server/server.py | 564 ++++++++++++++----- tests/test_ota_helpers.py | 66 +++ tests/test_scp81.py | 465 ++++++++++++---- 14 files changed, 1998 insertions(+), 564 deletions(-) create mode 100644 frontend/tests/scripts.test.js diff --git a/docs/api.md b/docs/api.md index 62328fc..d7d63fc 100644 --- a/docs/api.md +++ b/docs/api.md @@ -47,9 +47,13 @@ connect and warns if versions are incompatible. | `/api/pli-dict` | GET | Current dictionary (hex values per qualifier) | | `/api/pli-dict` | POST | Update dictionary entries | | `/api/scp81/bip` | POST | Start/stop the HTTP OTA listener (dump capture or PSK TLS server) | -| `/api/scp81/status` | GET | BIP terminal + listener state (channels, PSK identity seen) | +| `/api/scp81/status` | GET | BIP terminal + listener state (channels, PSK identities, handshake identity) | | `/api/scp81/log` | GET | HTTP OTA event log (`?after=`) | | `/api/scp81/log-clear` | POST | Clear the HTTP OTA event log | +| `/api/scp81/queue` | POST | Replace the SCP81 command script (optionally force-restart) | +| `/api/scp81/script` | GET | Active command script + execution state and R-APDUs | +| `/api/scp81/psk-map` | POST | Replace the PSK table of a running TLS listener | +| `/api/scp81/gen-install` | POST | Generate the RAM APDU list for a `.cap` (no queueing) | ## Endpoint details @@ -176,6 +180,7 @@ Install a Java Card `.cap` file on the card via GlobalPlatform commands (INSTALL | `stk_params` | no | Hex CA TLV (TS 102 226 §8.2.1.3.2.1) for SIM toolkit app-specific params | | `nv_quota` / `volatile_quota` | no | Integer memory quotas (bytes) for `gen_install_parameters()` | | `make_selectable` | no | If true (default), final INSTALL uses P1=`0C` (install + make selectable) | +| `load_block_size` | no | Bytes of load-file payload per LOAD APDU, 1–240. When empty/omitted the server auto-fits: the largest size whose SCP80 secured packet still encodes into one SMS (140 octets; e.g. 107 for the 3DES `spi1=16/spi2=01` configuration). An explicit value larger than the fitting size is clamped; over SCP80 the default 240 does **not** fit and used to fail with pySim's "Cannot encode command in a single SMS". | **Response (success):** ```json @@ -186,9 +191,17 @@ Install a Java Card `.cap` file on the card via GlobalPlatform commands (INSTALL "final_cntr": "0000000004", "load_file_aid": "A000000003000000", "module_aid": "A000000003000000", - "application_aid": "A000000003000000"} + "application_aid": "A000000003000000", + "load_block_size": 107, + "load_block_size_requested": null, + "load_block_size_clamped": false} ``` +`load_block_size` is the effective size used for the LOAD blocks, +`load_block_size_requested` echoes an explicit `load_block_size` (null = +auto-fit) and `load_block_size_clamped` is true when the requested size was +reduced to fit one SMS. + **Response (failure):** ```json {"success": false, "failed_step": "load_1", @@ -467,19 +480,29 @@ ClientHello) without answering: {"action": "start", "mode": "dump", "host": "127.0.0.1", "port": 8443} ``` -TLS mode runs the Phase B PSK TLS server (GPC v2.2 Amendment B): the PSK key -and optional identity are applied to the TLS handshake, and the GP HTTP -administration dialog (`X-Admin-*` headers, 200 with a command string or 204 -No Content) is served. `psk_hex` is required (the previous key is reused when -omitted); `psk_identity` restricts the accepted identity. The key is never -stored or logged. +TLS mode runs the Phase B PSK TLS server (GPC v2.2 Amendment B): the PSK +table is applied to the TLS handshake, and the GP HTTP administration dialog +(`X-Admin-*` headers, 200 with a command string or 204 No Content) is served. +`psk_map` is the lookup table for the identity the card presents in the TLS +handshake — the PWA sends it from the card presets (`{identity, psk_hex}` +objects or an `{identity: psk_hex}` map); a handshake whose identity is not +listed fails with the log entry `tls-psk-unknown`. The legacy single-key form +`psk_hex` (with optional `psk_identity`, empty = accept any identity) is still +accepted; when both are omitted the table of the previous start is reused. +Keys are never stored or logged. ```json {"action": "start", "mode": "tls", "host": "127.0.0.1", "port": 8443, - "psk_hex": "00112233445566778899aabbccddeeff", - "psk_identity": "89012345678901234567"} + "psk_map": [{"identity": "89012345678901234567", + "psk_hex": "00112233445566778899aabbccddeeff"}], + "script": ["80CAFF2100", "80F28002024F0000"], "script_kind": "Explore"} ``` +`script` is the APDU list served to the card (an explicit list, or `none`); +the server is agnostic to what the APDUs do. `script_kind` is an optional +label for the logs/results. Omitting `script` keeps the configured script and +its run progress. + Stop either mode with `{"action": "stop"}` (also disables the BIP terminal). ### `GET /api/scp81/status` @@ -487,9 +510,29 @@ Stop either mode with `{"action": "stop"}` (also disables the BIP terminal). ```json {"bip": {"enabled": true, "target": "127.0.0.1:8443", "channels": [], "seq": 12}, "listener": {"mode": "tls", "host": "127.0.0.1", "port": 8443, - "psk_identity": null, "identity_seen": "89012345678901234567"}} + "psk_identities": ["89012345678901234567"], "psk_wildcard": false, + "identity_seen": "89012345678901234567", "identity_matched": true}} ``` +`psk_identities` lists the identities the listener accepts (keys are never +exposed); `psk_wildcard` marks the legacy single-key mode. `identity_seen` / +`identity_matched` reflect the last handshake: an unknown identity is logged +as `tls-psk-unknown` and the handshake fails. + +### `POST /api/scp81/psk-map` + +Replaces the PSK table of the running TLS listener (the PWA pushes card-preset +edits without a listener restart): + +```json +{"psk_map": [{"identity": "89012345678901234567", + "psk_hex": "00112233445566778899aabbccddeeff"}]} +``` + +Returns `{"ok": true, "identities": [...], "listener": {...}}`; entries +without an identity or a valid key are skipped, and an empty table is +rejected. + ### `GET /api/scp81/log` Returns the BIP/TLS event log (open/close, SEND/RECEIVE DATA hex, TLS @@ -498,50 +541,68 @@ newer entries; `seq` echoes the latest sequence number. ### `POST /api/scp81/queue` -Queue explicit commands as the SCP81 script (used by the Remote APDU tab's -RAM chain "Queue in SCP81"). Body `{"apdus": ["80E60C002E...", ...]}` (or a -single `apdu`), optional `kind` and `force`. Entries that already are -Command Scripting templates (`AA...`/`AE80...`, the expanded format) are -sent verbatim instead of being wrapped again. Refused while a script is -mid-run unless forced. +Replace the SCP81 command script (used by the Remote APDU tab's RAM chain +"Queue in SCP81" and the PWA's "Restart script"). Body +`{"apdus": ["80E60C002E...", ...]}` (or a single `apdu`), optional `kind` and +`force`. Entries that already are Command Scripting templates +(`AA...`/`AE80...`, the expanded format) are sent verbatim instead of being +wrapped again. Refused while a script is mid-run unless forced; queuing resets +the execution progress. -### `POST /api/scp81/ram-install` +### `POST /api/scp81/gen-install` -Queue a RAM (GP) install as the SCP81 command script. The `.cap` is parsed -server-side (same parser as `/api/ram-install`) and expanded to the APDU -sequence INSTALL [for load] -> LOAD blocks (240-byte payloads) -> INSTALL -[for install]; the list runs on the card's next POST, one C-APDU per request. +Generate the RAM (GP) APDU sequence for a `.cap` without touching the listener +or the running script; the PWA's "Install from .cap" script template stores +the returned list. The `.cap` is parsed server-side (same parser as +`/api/ram-install`) and expanded to INSTALL [for load] -> LOAD blocks +(240-byte payloads) -> INSTALL [for install]; the file itself is never stored. ```json {"cap_hex": "504B0304...", "sd_aid": "A000000003000000", "privileges": "00", - "install_params": "", "stk_params": "", "make_selectable": true, "force": false} + "install_params": "", "stk_params": "", "make_selectable": true} ``` -`sd_aid` empty = the ISD. Refused while a script is mid-run unless `force` is -true. Responds with `{"ok": true, "queued": true, "apdus": N, "load_file_aid": -..., "module_aid": ...}`; the results appear in `/api/scp81/script` and the -R-APDU log. `GET /api/scp81/script` reports the script `kind` -(`explore`/`none`/`custom`/`ram-install`). +`sd_aid` empty = the ISD. Responds with `{"ok": true, "apdus": [...], +"load_file_aid": ..., "module_aid": ...}`. ### `GET /api/scp81/script` -Returns the active command script and the R-APDUs collected so far: +Returns the configured command script and the execution state: ```json -{"script": ["80CAFF2100", "80F28002024F0000"], "sent": 1, - "results": [{"index": 1, "sw": "9000", "rapdu": "FF210C810102..."}]} +{"script": ["80CAFF2100", "80F28002024F0000"], "next": 2, "total": 2, + "done": [0, 1], "kind": "Explore", + "pending": {"index": 17, "pos": null, "page": true, "apdu": "80F28003024F0000"}, + "pages": 11, "pages_queued": 0, "complete": false, + "results": [{"index": 1, "pos": 0, "page": false, "sw": "9000", + "apdu": "80CAFF2100", "rapdu": "FF210C810102..."}]} ``` -The script is selected when starting the TLS listener with the `script` -parameter: `explore` (default — the reference administration server's command -sequence: GET DATA FF21 extended resources / free memory, GET STATUS P1=80 -Issuer Security Domain, GET DATA 0085 HTTP administration parameters, GET -STATUS P1=40 executable load files and P1=10 applications), `none` (answer -every POST with 204), or an explicit list of APDU hex strings. Each APDU is +`next` is the index of the next script APDU to send; `done` lists the script +indices the card reported. `pending` describes the C-APDU awaiting the card's +`X-Admin-Script-Status` report as `{index, pos, page, apdu}` (`pos` = script +index, `null` for an auto continuation page) or `null`; `pages` counts the +continuation pages queued so far and `pages_queued` those not yet sent. +`complete` is true when every configured APDU was reported and nothing is in +flight — a script can therefore be complete while a listing page is still +being fetched (`pending.page` = true), which is tracked separately from the +script's own progress. `results` entries carry the send order (`index`), the +script position (`pos`, `null` for continuation pages) and the `page` flag. + +Execution tracking and resume: an APDU counts as executed only when the card +reports it in the next POST's Response Scripting template. A POST with +`X-Admin-Resume` continues with the unexecuted tail (the pending APDU is +resent if its report never arrived), a POST without it is a fresh dialog where +the script runs from the start, and a completed script closes the session with +204. + +Each APDU is delivered in an `AE 80 22 00 00` Command Scripting template (TS 102 226 §5.2.1) with `X-Admin-Next-URI`; the card returns its R-APDUs in the next POST's Response Scripting template, which is parsed and logged -(`script-rapdu`, `script-memory`). +(`script-rapdu`, `script-memory`). Long GET STATUS listings that answer +`63 10` / `CA FE` ("more data available") are auto-continued with the same +command carrying P2.b1=1. TLS mode also accepts `chunked` (**default `true`** — the reference server's chunked framing; the card rejects a chunked response that also carries a diff --git a/docs/scp81-findings.md b/docs/scp81-findings.md index 0a32474..ad2382e 100644 --- a/docs/scp81-findings.md +++ b/docs/scp81-findings.md @@ -306,11 +306,12 @@ INSTALL [for install] -> registries. 1. **UI:** group the per-page R-APDUs under their logical command in the SCP81 tab (page merging/decoding for ELF and application listings); expose the framing options in the tab. -2. **Load/store over SCP81:** implemented - `POST /api/scp81/ram-install` +2. **Load/store over SCP81:** implemented - `POST /api/scp81/gen-install` takes a `.cap`, expands it with the shared `_cap_apdu_sequence` helper (INSTALL [for load] -> 240-byte LOAD blocks -> INSTALL [for install]) and - queues it as the command script, one C-APDU per POST. Live install test - pending (needs a push with a suitable applet). + returns the APDU list, which the PWA stores as an "Install from .cap" + script (the `.cap` itself is never stored). Live install verified + 2026-09-16. ## Tooling diff --git a/frontend/help-ru.html b/frontend/help-ru.html index 96a7cfe..0b86290 100644 --- a/frontend/help-ru.html +++ b/frontend/help-ru.html @@ -44,7 +44,7 @@
  • Шапка — версия приложения, кнопка INSTALL PWA (появляется, когда браузер предлагает установку, для офлайн-работы), ссылки на проект на GitHub и на эту справку, переключатель языка EN/RU и переключатель тёмной/светлой темы.
  • Выбор языка и темы хранится в localStorage и сохраняется между перезагрузками.
  • -
  • Вкладки верхнего уровня: Remote APDU (SIM RFM, USIM RFM, Expanded Script, RAM/GP, HTTP OTA, Разбор C-APDU, «Парсер ответов»), SCP80 (Secured Packet, Карты, RAM), «Профайлер» (вкладки «Профили», «Снимки карт», «Пользовательские файлы»), «Картридер» (Файловый менеджер, Командная строка pySim, Отправка APDU) и «Симулятор телефона».
  • +
  • Вкладки верхнего уровня: Remote APDU (SIM RFM, USIM RFM, Expanded Script, RAM/GP, HTTP OTA, Разбор C-APDU, «Парсер ответов»), SCP80 (Secured Packet, RAM), SCP81 («Слушатель», «Скрипты»), «Карты», «Профайлер» (вкладки «Профили», «Снимки карт», «Пользовательские файлы»), «Картридер» (Файловый менеджер, Командная строка pySim, Отправка APDU) и «Симулятор телефона».
  • Ссылка справка открывает эту документацию на разделе, соответствующем текущему представлению (например, вкладка «Профайлер» открывает §5).
@@ -241,7 +241,7 @@

3. Вкладка SCP80

-

Верхнеуровневая вкладка SCP80 объединяет разделы, связанные с SCP80. Переключение — тремя переключателями: Secured Packet, Карты и RAM. Собирает защищённые пакеты SCP80 по ETSI TS 102 225.

+

Верхнеуровневая вкладка SCP80 объединяет разделы, связанные с SCP80. Переключение — двумя переключателями: Secured Packet и RAM. Собирает защищённые пакеты SCP80 по ETSI TS 102 225.

3.1 Secured Packet

Собирает защищённые пакеты SCP80 по ETSI TS 102 225.

@@ -275,7 +275,7 @@

Кнопка Проверить в pySim сверяет собранный пакет с эталонной реализацией OtaDialectSms.encode_cmd. Кнопка Отправить на карту доставляет пакет через ENVELOPE SMS-PP-DOWNLOAD (при подключении к серверу). Полученный Proof of Receipt декодируется и показывается строкой статуса PoR (статус, TAR, счётчик, сырой PoR); статусное слово и данные ответа последней команды подставляются в подвкладку «Парсер ответов» (Remote APDU), а успешный PoR увеличивает счётчик повторов и очищает пакет.

3.2 Карты

-

Хранит предустановки карт локально в браузере (localStorage), чтобы представление Secured Packet могло автоматически подставлять ключи и параметры.

+

Хранит предустановки карт локально в браузере (localStorage), чтобы представление Secured Packet могло автоматически подставлять ключи и параметры, а слушатель SCP81 HTTP OTA — находить PSK-ключи. Вкладка «Карты» — верхнеуровневая.

@@ -285,20 +285,22 @@ - + + +
ПолеОписание
SPI1 / SPI2Security Parameter Indicators
TARToolkit Application Reference
CounterСчётчик повторов (5 байт)
KIc key / KID key16/24/32 hex-символа (ключи 8/16/24 байта 3DES) или 32/48/64 hex-символа (ключи 16/24/32 байта AES)
KIc key / KID key16/24/32 hex-символа (ключи 8/16/24 байта 3DES) или 32/48/64 hex-символа (ключи 16/24/32 байта AES)
PSK identitySCP81 HTTP OTA: идентификатор, который карта присылает в TLS-рукопожатии (например, 89390100000129506903)
PSK keySCP81 HTTP OTA: 32 hex-символа (16 байт). Слушатель выбирает этот ключ, когда карта предъявляет совпадающий идентификатор; предустановка с ключом без идентификатора игнорируется (и помечается в таблице)
-

Обмен предустановками: Экспорт в JSON и Экспорт в файл для выгрузки, Импорт из файла, Вставить и импортировать или Импорт JSON из буфера для загрузки. Выбранная предустановка автоматически заполняет форму Secured Packet.

+

Столбец SCP81 показывает, задана ли в предустановке рабочая пара PSK. Кнопка Изменить загружает предустановку в форму (кнопка становится Сохранить; Отмена очищает форму), поэтому поля можно менять без повторного ввода карты. Обмен предустановками: Экспорт в JSON и Экспорт в файл для выгрузки, Импорт из файла, Вставить и импортировать или Импорт JSON из буфера для загрузки. Выбранная предустановка автоматически заполняет форму Secured Packet; изменения сразу передаются работающему слушателю SCP81.

3.3 RAM

-

Выполняет операции удалённого управления приложениями (Remote Application Management) как защищённые пакеты SCP80 через SMS-PP-DOWNLOAD ENVELOPE. Карта должна поддерживать SCP03 (AES или 3DES). Предустановка карты из подвкладки Карты обеспечивает SPI, ключи, TAR и счётчик.

+

Выполняет операции удалённого управления приложениями (Remote Application Management) как защищённые пакеты SCP80 через SMS-PP-DOWNLOAD ENVELOPE. Карта должна поддерживать SCP03 (AES или 3DES). Предустановка карты со вкладки Карты обеспечивает SPI, ключи, TAR и счётчик.

Операции

- +
ОперацияОписание
Обзор карты (все данные GP)Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками Удалить для каждого элемента.
Установка пакета (.cap файл)Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)].
Установка пакета (.cap файл)Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU, каждый из которых помещается в один SMS SCP80; поле размер блока LOAD переопределяет авто-подобранный размер (пусто = максимальный размер, чей secured-пакет укладывается в 140 октетов), так что большой .cap просто занимает несколько SMS.
@@ -378,7 +380,7 @@
  • Профиль с карты — сканирует подключённую карту и создаёт по одному правилу на каждый существующий файл (см. ниже), затем открывает редактор.
  • Профиль из снимка — выбирает сохранённый снимок карты и создаёт по правилу на каждый захваченный файл с теми же опциями сканирования (см. ниже), без картридера; имя профиля подставляется из имени снимка.
  • Импорт профиля — загружает набор правил из JSON-файла (имя хранится внутри JSON).
  • -
  • В каждой строке профиля показаны имя и время создания, а также действия Проверить карту ▶, Проверить снимок карты, Редактировать, Экспорт (скачать JSON) и Удалить.
  • +
  • В каждой строке профиля показаны имя и время создания, а также действия Проверить карту ▶, Проверить снимок карты, Редактировать, Клонировать, Экспорт (скачать JSON) и Удалить. Клонировать создаёт копию профиля с именем Копия <профиль> (с суффиксом (2), (3)…, если такое имя уже занято) и открывает копию в редакторе, чтобы изменить имя и содержимое перед сохранением.
  • Правила файловой системы

    Правила выполняются последовательно. Редактор показывает символьное имя файла pySim (если известно) рядом с путём правила; Добавить правило добавляет правило, Сохранить сохраняет изменения. Правило файловой системы задаётся:

    @@ -399,7 +401,7 @@
  • Импорт снимка — загружает снимок из JSON-файла.
  • В каждой строке снимка — Открыть, Экспорт и Удалить. Открыть показывает все захваченные данные только для чтения (сырой FCI с декодированным FCI, содержимое); редактируется только имя снимка.
  • Проверить снимок карты в строке профиля выполняет правила профиля на выбранном из списка снимке, без картридера. Отчёт такой же, как при проверке карты: фактическая сторона подписана именем снимка (фактически (имя снимка)), а в заголовке — Результаты проверки профиля: <профиль> → <имя снимка>; файлы, содержимое которых не было захвачено при сканировании, помечаются как непроверяемые ошибки.
  • -
  • Сравнить снимки сравнивает два снимка без картридера так же, как проверка профиля: выберите эталонный снимок и снимок для проверки, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт; в этом отчёте заголовок — Результаты сравнения снимков: <эталон> → <проверяемый>, а поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».
  • +
  • Сравнить снимки сравнивает два снимка без картридера так же, как проверка профиля, но всегда точно (маскирование содержимого не применяется): выберите эталонный снимок и снимок для проверки и получите такой же отчёт; в этом отчёте заголовок — Результаты сравнения снимков: <эталон> → <проверяемый>, а поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».
  • diff --git a/frontend/help.html b/frontend/help.html index cf3492b..77b4b77 100644 --- a/frontend/help.html +++ b/frontend/help.html @@ -44,7 +44,7 @@
    • Header — the app version, an INSTALL PWA button (shown when the browser offers installation, enabling offline use), links to the project on GitHub and to this help, an EN/RU language toggle, and a dark/light theme toggle.
    • Language and theme choices are stored in localStorage and persist across reloads.
    • -
    • Top-level tabs: Remote APDU (SIM RFM, USIM RFM, Expanded Script, RAM/GP, HTTP OTA, C-APDU Parser, Response parser), SCP80 (Secured Packet, Cards, RAM), Profiler (list tabs Profiles, Card snapshots, Custom files), Card reader (File manager, pySim command line, Raw APDU), and Phone simulator.
    • +
    • Top-level tabs: Remote APDU (SIM RFM, USIM RFM, Expanded Script, RAM/GP, HTTP OTA, C-APDU Parser, Response parser), SCP80 (Secured Packet, RAM), SCP81 (Listener, Scripts), Cards, Profiler (list tabs Profiles, Card snapshots, Custom files), Card reader (File manager, pySim command line, Raw APDU), and Phone simulator.
    • The help link opens this documentation at the section matching the current view (e.g. the Profiler tab opens §5).
    @@ -275,7 +275,7 @@

    A “Verify vs pySim” button cross-checks the assembled packet against pySim’s reference OtaDialectSms.encode_cmd. A “Send to Card” button delivers it via SMS-PP-DOWNLOAD ENVELOPE (when connected to the server). The returned Proof of Receipt is decoded and shown as a PoR status line (status, TAR, counter, raw PoR); the last command’s status word and response data are filled into the Response parser tab, and a successful PoR advances the replay counter and clears the packet.

    3.2 Cards

    -

    Stores card presets locally in the browser (localStorage) so the Secured Packet view can auto-fill keys and parameters.

    +

    Stores card presets locally in the browser (localStorage) so the Secured Packet view can auto-fill keys and parameters, and so the SCP81 HTTP OTA listener can look up PSK keys. The Cards tab is a top-level tab.

    @@ -285,20 +285,22 @@ - + + +
    FieldDescription
    SPI1 / SPI2Security Parameter Indicators
    TARToolkit Application Reference
    CounterReplay counter (5 bytes)
    KIc key / KID key16/24/32 hex chars (8/16/24-byte 3DES) or 32/48/64 hex chars (16/24/32-byte AES) keys
    KIc key / KID key16/24/32 hex chars (8/16/24-byte 3DES) or 32/48/64 hex chars (16/24/32-byte AES) keys
    PSK identitySCP81 HTTP OTA: the identity the card sends in the TLS handshake (e.g. 89390100000129506903)
    PSK keySCP81 HTTP OTA: 32 hex chars (16 bytes). The listener selects this key when the card presents the matching identity; a preset with a key but no identity is ignored (and flagged in the table)
    -

    Presets can be shared with Export as JSON and Export to file, and restored with Import from file, Paste & import, or Import JSON from clipboard. The selected card preset auto-fills the Secured Packet form.

    +

    The SCP81 column shows whether the preset supplies a usable PSK pair. Edit loads a preset into the form (the button becomes Save; Cancel clears it) so fields can be changed without re-entering the card. Presets can be shared with Export as JSON and Export to file, and restored with Import from file, Paste & import, or Import JSON from clipboard. The selected card preset auto-fills the Secured Packet form; edits are pushed into a running SCP81 listener automatically.

    3.3 RAM

    -

    Delivers Remote Application Management operations as SCP80 secured packets via SMS-PP-DOWNLOAD ENVELOPE. The card must support SCP03 (AES or 3DES). A saved card preset from the Cards sub-tab provides the SPI, keys, TAR, and counter.

    +

    Delivers Remote Application Management operations as SCP80 secured packets via SMS-PP-DOWNLOAD ENVELOPE. The card must support SCP03 (AES or 3DES). A saved card preset from the Cards tab provides the SPI, keys, TAR, and counter.

    Operations

    - +
    OperationDescription
    Explore Card (all GP data)Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item Delete buttons.
    Install Package (.cap file)Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)].
    Install Package (.cap file)Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs that each fit one SCP80 SMS; the LOAD block size field overrides the auto-fitted size (empty = largest size whose secured packet still encodes into 140 octets), so a large .cap simply takes several SMS.
    @@ -378,7 +380,7 @@
  • Profile from card — scans the equipped card and generates one rule per existing file (see below), then opens the editor.
  • Profile from snapshot — picks a saved card snapshot and generates one rule per captured file using the same scan options (see below), without a card reader; the profile name is prefilled with the snapshot name.
  • Import profile — loads a ruleset from a JSON file (the name is stored inside the JSON).
  • -
  • Each profile row shows its name and creation time, with Check card ▶, Check card snapshot, Edit, Export (download JSON), and Delete actions.
  • +
  • Each profile row shows its name and creation time, with Check card ▶, Check card snapshot, Edit, Clone, Export (download JSON), and Delete actions. Clone copies the profile under the name Copy of <profile> (with a (2), (3)… suffix when that name already exists) and opens the copy in the editor, so both the name and the contents can be adjusted before saving.
  • Filesystem rules

    Rules run sequentially. The editor shows the symbolic pySim name (when known) next to each rule’s path; use Add rule to append one and Save to keep the changes. A filesystem rule is defined by:

    @@ -399,7 +401,7 @@
  • Import snapshot — loads a snapshot from a JSON file.
  • Each snapshot row has Open, Export, and Delete. Open shows all captured data read-only (raw FCI with the decoded FCI, contents); only the snapshot name is editable.
  • Check card snapshot on a profile row runs the profile rules against a snapshot you pick from the list, without a card reader. The report is the same as a live check: the actual side is labelled with the snapshot name (actual (snapshot name)) and the header reads Profile verification results for: <profile> → <snapshot name>; files whose contents were not captured during the scan are reported as unverifiable errors.
  • -
  • Compare snapshots compares two snapshots offline, exactly like a profile check: pick the master snapshot and the snapshot to check, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report; in that report the header reads Snapshot comparison results: <master> → <checked> and the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.
  • +
  • Compare snapshots compares two snapshots offline, exactly like a profile check but always exact (no content masking): pick the master snapshot and the snapshot to check and get the same pass/fail report; in that report the header reads Snapshot comparison results: <master> → <checked> and the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.
  • diff --git a/frontend/index.html b/frontend/index.html index 491d7d7..22e224c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,7 +18,7 @@
    -

    OTAMan SIM OTA with a Human Face v2.1.18

    +

    OTAMan SIM OTA with a Human Face v2.2.0

    @@ -36,6 +36,7 @@ + @@ -391,7 +392,6 @@