docs: fix version compatibility (PWA vs server majors), broaden descriptions, drop the header slogan (v2.3.1)
- version check in pysimConnect() now compares the server major against SIMPLE_VERSION (2.3.1) instead of the hardcoded 1.x assumption; the header version is rendered from the same constant - removed the 'SIM OTA with a Human Face' slogan (span, i18n key, translate hook) to free header space - broaden the PWA description in manifest.json/package.json and the help overview (EN/RU): APDU workbench, OTA lab, CAT/STK simulator, card profiler - version compatibility tables in README/RUS, help EN/RU and docs/api.md now describe the major-version rule - README/RUS: Cards promoted to a top-level section; EN duplicate Theme/Localisation sections removed - docs: /api/cardinfo documented; SCP81 'expose framing options' next-work item dropped (done); local AGENTS.md not-implemented note corrected - SW cache simple-v190
This commit is contained in:
@@ -438,28 +438,6 @@ Delivery PoR (SPI2 `01`) is simpler — the card returns the PoR directly in the
|
|||||||
- ISO 9797-1: MAC algorithms
|
- ISO 9797-1: MAC algorithms
|
||||||
- NIST SP 800-38B: CMAC
|
- NIST SP 800-38B: CMAC
|
||||||
|
|
||||||
### Cards
|
|
||||||
|
|
||||||
Stores saved card configurations (presets) in `localStorage`. A preset holds the cryptographic keys, SPI settings, TAR and replay counter for SCP80 operations, plus the **PSK identity / PSK key** pair used by the SCP81 HTTP OTA listener. Cards is a **top-level tab**. When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in both SCP80 views.
|
|
||||||
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| Name | Human-readable label (required) |
|
|
||||||
| ICCID | Optional card identifier |
|
|
||||||
| SPI1 / SPI2 | Security level and PoR settings |
|
|
||||||
| KIc / KID index | Key version number (required together with the keys) |
|
|
||||||
| KIc / KID key | Encryption and MAC key hex |
|
|
||||||
| TAR | Toolkit Application Reference (3 bytes) |
|
|
||||||
| Counter (CNTR) | 10-digit hex replay counter, auto-incremented after each successful SCP80 send |
|
|
||||||
| PSK identity | SCP81 HTTP OTA: the identity the card sends in the TLS handshake |
|
|
||||||
| PSK key | SCP81 HTTP OTA: 32 hex chars (16 bytes); the listener picks it by the identity the card presents |
|
|
||||||
|
|
||||||
The **SCP81** column shows whether the preset supplies a usable PSK pair: **✓** (identity and key), **⚠** (only one of the two — the listener ignores such a preset), **—** (no PSK). Identity and key must be set together.
|
|
||||||
|
|
||||||
**Add a card:** fill in the name, ICCID (optional — **From card** fills it from the equipped card's EF.ICCID), SPI1/SPI2, KIc/KID keys and indices, TAR, the SCP81 PSK pair (optional) and click **Add**. A duplicate ICCID (compared ignoring spaces and the raw-hex form) is refused, naming the conflicting preset. The card appears in the list and becomes available in the RAM tab's **Card preset** dropdown.
|
|
||||||
|
|
||||||
**Edit / remove:** **Edit** loads a preset into the form (the Add button becomes **Save**; **Cancel** clears the form); **Remove** deletes the row from `localStorage`. A successful SCP80 send advances and stores the replay counter, and edits are pushed into a running SCP81 listener automatically.
|
|
||||||
|
|
||||||
### RAM
|
### RAM
|
||||||
|
|
||||||
All RAM operations are delivered as SCP80 secured packets (ETSI TS 102 225) via SMS-PP-DOWNLOAD ENVELOPE. The card must support SCP03 (AES or 3DES) for secure transport.
|
All RAM operations are delivered as SCP80 secured packets (ETSI TS 102 225) via SMS-PP-DOWNLOAD ENVELOPE. The card must support SCP03 (AES or 3DES) for secure transport.
|
||||||
@@ -485,6 +463,30 @@ Delete confirms via a browser prompt before sending the GP `DELETE` command via
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Cards
|
||||||
|
|
||||||
|
Stores saved card configurations (presets) in `localStorage`. A preset holds the cryptographic keys, SPI settings, TAR and replay counter for SCP80 operations, plus the **PSK identity / PSK key** pair used by the SCP81 HTTP OTA listener. Cards is a **top-level tab**. When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in both SCP80 views.
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|---|---|
|
||||||
|
| Name | Human-readable label (required) |
|
||||||
|
| ICCID | Optional card identifier |
|
||||||
|
| SPI1 / SPI2 | Security level and PoR settings |
|
||||||
|
| KIc / KID index | Key version number (required together with the keys) |
|
||||||
|
| KIc / KID key | Encryption and MAC key hex |
|
||||||
|
| TAR | Toolkit Application Reference (3 bytes) |
|
||||||
|
| Counter (CNTR) | 10-digit hex replay counter, auto-incremented after each successful SCP80 send |
|
||||||
|
| PSK identity | SCP81 HTTP OTA: the identity the card sends in the TLS handshake |
|
||||||
|
| PSK key | SCP81 HTTP OTA: 32 hex chars (16 bytes); the listener picks it by the identity the card presents |
|
||||||
|
|
||||||
|
The **SCP81** column shows whether the preset supplies a usable PSK pair: **✓** (identity and key), **⚠** (only one of the two — the listener ignores such a preset), **—** (no PSK). Identity and key must be set together.
|
||||||
|
|
||||||
|
**Add a card:** fill in the name, ICCID (optional — **From card** fills it from the equipped card's EF.ICCID), SPI1/SPI2, KIc/KID keys and indices, TAR, the SCP81 PSK pair (optional) and click **Add**. A duplicate ICCID (compared ignoring spaces and the raw-hex form) is refused, naming the conflicting preset. The card appears in the list and becomes available in the RAM tab's **Card preset** dropdown.
|
||||||
|
|
||||||
|
**Edit / remove:** **Edit** loads a preset into the form (the Add button becomes **Save**; **Cancel** clears the form); **Remove** deletes the row from `localStorage`. A successful SCP80 send advances and stores the replay counter, and edits are pushed into a running SCP81 listener automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Card Reader (pySim integration)
|
## Card Reader (pySim integration)
|
||||||
|
|
||||||
Connects to the bundled [`pysim-simple-server`](pysim_simple_server/) for live card operations.
|
Connects to the bundled [`pysim-simple-server`](pysim_simple_server/) for live card operations.
|
||||||
@@ -696,20 +698,12 @@ pysim-simple-server --http-port 8080
|
|||||||
|
|
||||||
See [docs/api.md](docs/api.md) for the full endpoint reference.
|
See [docs/api.md](docs/api.md) for the full endpoint reference.
|
||||||
|
|
||||||
## Theme
|
|
||||||
|
|
||||||
Dark theme is supported. The app follows the OS preference on first visit, and a manual toggle button (🌙/☀️) at the top-right corner persists the choice in `localStorage`.
|
|
||||||
|
|
||||||
## Localisation
|
|
||||||
|
|
||||||
The UI is in English with Russian language support. Language is detected from the browser's `navigator.language` preference. A manual toggle button (EN/RU) in the header persists the choice in `localStorage`.
|
|
||||||
|
|
||||||
## Version compatibility
|
## Version compatibility
|
||||||
|
|
||||||
| PWA (SIMple) | Server | Status |
|
| PWA (SIMple) | Server | Status |
|
||||||
|-------------|--------|--------|
|
|-------------|--------|--------|
|
||||||
| 1.x.x | 1.x.x | ✅ Compatible |
|
| any | same major | ✅ Compatible |
|
||||||
| 1.x.x | 0.x.x | ❌ Outdated — update server |
|
| any | older major | ❌ Outdated — update server |
|
||||||
| 1.x.x | 2.x.x+ | ⚠️ Server newer — update PWA |
|
| any | newer major | ⚠️ Server newer — update PWA |
|
||||||
|
|
||||||
The PWA checks the server version on connect via `GET /api/version` and warns if versions are incompatible.
|
The PWA checks the server version on connect via `GET /api/version` and compares the major version (e.g. a 2.x PWA with a 2.x server; a 1.x server is flagged as outdated).
|
||||||
|
|||||||
+28
-26
@@ -412,28 +412,6 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
|||||||
- ISO 9797-1
|
- ISO 9797-1
|
||||||
- NIST SP 800-38B (CMAC)
|
- NIST SP 800-38B (CMAC)
|
||||||
|
|
||||||
### Карты
|
|
||||||
|
|
||||||
Хранит сохранённые конфигурации карт (пресеты) в `localStorage`. Пресет содержит криптографические ключи, настройки SPI, TAR и счётчик повторов для SCP80-операций, а также пару **PSK identity / PSK key** для слушателя SCP81 HTTP OTA. «Карты» — **верхнеуровневая вкладка**. При подключении карты читается её EF.ICCID, и пресет с тем же ICCID автоматически выбирается в обоих видах SCP80.
|
|
||||||
|
|
||||||
| Поле | Описание |
|
|
||||||
|---|---|
|
|
||||||
| Name | Понятная метка (обязательна) |
|
|
||||||
| ICCID | Опциональный идентификатор карты; кнопка **С карты** подставляет EF.ICCID подключённой карты (активна, только когда ICCID читается) |
|
|
||||||
| SPI1 / SPI2 | Уровень безопасности и настройки PoR |
|
|
||||||
| Индекс KIc / KID | Номер версии ключа (задаётся вместе с ключами) |
|
|
||||||
| Ключ KIc / KID | Hex-ключи шифрования и MAC |
|
|
||||||
| TAR | Toolkit Application Reference (3 байта) |
|
|
||||||
| Счётчик (CNTR) | 10-значный hex-счётчик повторов, автоматически увеличивается после каждой успешной отправки SCP80 |
|
|
||||||
| PSK identity | SCP81 HTTP OTA: идентификатор, который карта присылает в TLS-рукопожатии |
|
|
||||||
| PSK key | SCP81 HTTP OTA: 32 hex-символа (16 байт); ключ выбирается по идентификатору, который предъявляет карта |
|
|
||||||
|
|
||||||
Столбец **SCP81** показывает, задана ли в пресете рабочая пара PSK: **✓** (идентификатор и ключ), **⚠** (только одно из двух — слушатель такой пресет игнорирует), **—** (PSK нет). Идентификатор и ключ задаются вместе.
|
|
||||||
|
|
||||||
**Добавить карту:** заполните имя, ICCID (опционально — кнопка **С карты** подставляет EF.ICCID подключённой карты), SPI1/SPI2, ключи и индексы KIc/KID, TAR, при необходимости пару PSK и нажмите **Add**. Дубликат ICCID (сравнение без пробелов и с учётом сырой hex-формы) отклоняется с указанием конфликтующего пресета. Карта появится в списке и станет доступна в выпадающем списке **Card preset** на вкладке RAM.
|
|
||||||
|
|
||||||
**Изменить/удалить:** **Edit** загружает пресет в форму (кнопка Add становится **Save**; **Cancel** очищает форму); **Remove** удаляет строку из `localStorage`. Успешная отправка SCP80 увеличивает и сохраняет счётчик повторов, а изменения сразу передаются работающему слушателю SCP81.
|
|
||||||
|
|
||||||
### RAM
|
### RAM
|
||||||
|
|
||||||
Все операции RAM отправляются как защищённые пакеты SCP80 (ETSI TS 102 225) через SMS-PP-DOWNLOAD ENVELOPE. Карта должна поддерживать SCP03 (AES или 3DES) для безопасной транспортировки.
|
Все операции RAM отправляются как защищённые пакеты SCP80 (ETSI TS 102 225) через SMS-PP-DOWNLOAD ENVELOPE. Карта должна поддерживать SCP03 (AES или 3DES) для безопасной транспортировки.
|
||||||
@@ -459,6 +437,30 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Карты
|
||||||
|
|
||||||
|
Хранит сохранённые конфигурации карт (пресеты) в `localStorage`. Пресет содержит криптографические ключи, настройки SPI, TAR и счётчик повторов для SCP80-операций, а также пару **PSK identity / PSK key** для слушателя SCP81 HTTP OTA. «Карты» — **верхнеуровневая вкладка**. При подключении карты читается её EF.ICCID, и пресет с тем же ICCID автоматически выбирается в обоих видах SCP80.
|
||||||
|
|
||||||
|
| Поле | Описание |
|
||||||
|
|---|---|
|
||||||
|
| Name | Понятная метка (обязательна) |
|
||||||
|
| ICCID | Опциональный идентификатор карты; кнопка **С карты** подставляет EF.ICCID подключённой карты (активна, только когда ICCID читается) |
|
||||||
|
| SPI1 / SPI2 | Уровень безопасности и настройки PoR |
|
||||||
|
| Индекс KIc / KID | Номер версии ключа (задаётся вместе с ключами) |
|
||||||
|
| Ключ KIc / KID | Hex-ключи шифрования и MAC |
|
||||||
|
| TAR | Toolkit Application Reference (3 байта) |
|
||||||
|
| Счётчик (CNTR) | 10-значный hex-счётчик повторов, автоматически увеличивается после каждой успешной отправки SCP80 |
|
||||||
|
| PSK identity | SCP81 HTTP OTA: идентификатор, который карта присылает в TLS-рукопожатии |
|
||||||
|
| PSK key | SCP81 HTTP OTA: 32 hex-символа (16 байт); ключ выбирается по идентификатору, который предъявляет карта |
|
||||||
|
|
||||||
|
Столбец **SCP81** показывает, задана ли в пресете рабочая пара PSK: **✓** (идентификатор и ключ), **⚠** (только одно из двух — слушатель такой пресет игнорирует), **—** (PSK нет). Идентификатор и ключ задаются вместе.
|
||||||
|
|
||||||
|
**Добавить карту:** заполните имя, ICCID (опционально — кнопка **С карты** подставляет EF.ICCID подключённой карты), SPI1/SPI2, ключи и индексы KIc/KID, TAR, при необходимости пару PSK и нажмите **Add**. Дубликат ICCID (сравнение без пробелов и с учётом сырой hex-формы) отклоняется с указанием конфликтующего пресета. Карта появится в списке и станет доступна в выпадающем списке **Card preset** на вкладке RAM.
|
||||||
|
|
||||||
|
**Изменить/удалить:** **Edit** загружает пресет в форму (кнопка Add становится **Save**; **Cancel** очищает форму); **Remove** удаляет строку из `localStorage`. Успешная отправка SCP80 увеличивает и сохраняет счётчик повторов, а изменения сразу передаются работающему слушателю SCP81.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Card Reader (интеграция с pySim)
|
## Card Reader (интеграция с pySim)
|
||||||
|
|
||||||
Подключение к встроенному [`pysim-simple-server`](pysim_simple_server/) для работы с картой.
|
Подключение к встроенному [`pysim-simple-server`](pysim_simple_server/) для работы с картой.
|
||||||
@@ -612,11 +614,11 @@ SIMple — Progressive Web App. Можно установить для offline-
|
|||||||
|
|
||||||
| PWA (SIMple) | Сервер | Статус |
|
| PWA (SIMple) | Сервер | Статус |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1.x.x | 1.x.x | ✅ Совместимы |
|
| любая | та же мажорная | ✅ Совместимы |
|
||||||
| 1.x.x | 0.x.x | ❌ Сервер устарел |
|
| любая | старее мажорная | ❌ Сервер устарел — обновите сервер |
|
||||||
| 1.x.x | 2.x.x+ | ⚠️ Сервер новее — обновите PWA |
|
| любая | новее мажорная | ⚠️ Сервер новее — обновите PWA |
|
||||||
|
|
||||||
PWA проверяет версию сервера при подключении через `GET /api/version`.
|
PWA проверяет версию сервера при подключении через `GET /api/version` и сравнивает мажорную версию (например, PWA 2.x с сервером 2.x; сервер 1.x помечается как устаревший).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+12
-4
@@ -8,12 +8,13 @@ on the preflight), so the API is reachable from a separately-hosted PWA.
|
|||||||
|
|
||||||
| Server | PWA (SIMple) | Status |
|
| Server | PWA (SIMple) | Status |
|
||||||
|--------|-------------|--------|
|
|--------|-------------|--------|
|
||||||
| 1.x.x | 1.x.x | ✅ Compatible |
|
| same major | any | ✅ Compatible |
|
||||||
| 0.x.x | 1.x.x | ❌ Outdated — update server |
|
| older major | any | ❌ Outdated — update server |
|
||||||
| 2.x.x+ | 1.x.x | ⚠️ Server newer — update PWA |
|
| newer major | any | ⚠️ Server newer — update PWA |
|
||||||
|
|
||||||
The server reports its version via `GET /api/version`. The PWA checks this on
|
The server reports its version via `GET /api/version`. The PWA checks this on
|
||||||
connect and warns if versions are incompatible.
|
connect and compares the major version (a 1.x server is flagged as outdated by
|
||||||
|
a 2.x PWA).
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ connect and warns if versions are incompatible.
|
|||||||
| `/api/status` | GET | Card reader + card info + current selection |
|
| `/api/status` | GET | Card reader + card info + current selection |
|
||||||
| `/api/command` | POST | pySim command (equip, status, tree, etc.) |
|
| `/api/command` | POST | pySim command (equip, status, tree, etc.) |
|
||||||
| `/api/commands` | GET | List available pySim commands |
|
| `/api/commands` | GET | List available pySim commands |
|
||||||
|
| `/api/cardinfo` | GET | pySim `cardinfo` output |
|
||||||
| `/api/tree` | POST | File tree browser for given FID/name |
|
| `/api/tree` | POST | File tree browser for given FID/name |
|
||||||
| `/api/select` | POST | Select a file by name or FID |
|
| `/api/select` | POST | Select a file by name or FID |
|
||||||
| `/api/read` | POST | Read file content |
|
| `/api/read` | POST | Read file content |
|
||||||
@@ -87,6 +89,12 @@ with the same ICCID in both SCP80 views (Secured Packet and RAM).
|
|||||||
|
|
||||||
List all available shell commands for the current card profile.
|
List all available shell commands for the current card profile.
|
||||||
|
|
||||||
|
### `GET /api/cardinfo`
|
||||||
|
|
||||||
|
Runs pySim's `cardinfo` command and returns its output as `{"output": "..."}`
|
||||||
|
(card type, ATR, ICCID and other information pySim reports for the equipped
|
||||||
|
card). A shortcut for `POST /api/command` with `{"cmd": "cardinfo"}`.
|
||||||
|
|
||||||
### `POST /api/command`
|
### `POST /api/command`
|
||||||
|
|
||||||
Execute any pysim-shell command.
|
Execute any pysim-shell command.
|
||||||
|
|||||||
@@ -308,8 +308,7 @@ INSTALL [for install] -> registries.
|
|||||||
## Next tests / work
|
## Next tests / work
|
||||||
|
|
||||||
1. **UI:** group the per-page R-APDUs under their logical command in the
|
1. **UI:** group the per-page R-APDUs under their logical command in the
|
||||||
SCP81 tab (page merging/decoding for ELF and application listings);
|
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/gen-install`
|
2. **Load/store over SCP81:** implemented - `POST /api/scp81/gen-install`
|
||||||
takes a `.cap`, expands it with the shared `_cap_apdu_sequence` helper
|
takes a `.cap`, expands it with the shared `_cap_apdu_sequence` helper
|
||||||
(INSTALL [for load] -> 240-byte LOAD blocks -> INSTALL [for install]) and
|
(INSTALL [for load] -> 240-byte LOAD blocks -> INSTALL [for install]) and
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Документация</span></h1>
|
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Документация</span></h1>
|
||||||
<section class="mb-10">
|
<section class="mb-10">
|
||||||
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Обзор</h2>
|
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Обзор</h2>
|
||||||
<p class="mb-3">SIMple — Progressive Web App (PWA) для построения APDU-команд, сборки защищённых пакетов SCP80, просмотра меню SIM Toolkit (STK) и симуляции реальной сетевой среды для тестирования SIM/USIM/UICC-карт через PC/SC-ридер.</p>
|
<p class="mb-3">SIMple — инструментарий для специалистов по UICC/SIM-картам: автономный Progressive Web App (PWA), закрывающий весь цикл лабораторной работы — <strong>APDU-верстак</strong> (конструкторы SIM/USIM RFM и Expanded Script, команды GlobalPlatform RAM, парсер C-APDU/R-APDU и декодер ответов), <strong>OTA-лаборатория</strong> (защищённые пакеты SCP80 и установка Java Card RAM, а также терминальная сторона HTTP OTA — SCP81: эмуляция BIP-каналов, PSK-TLS-сервер администрирования и выполнение скриптов APDU), <strong>симулятор телефона</strong> (меню SIM Toolkit, proactive-команды и события, редактирование TERMINAL PROFILE, журнал C-AT-диалога) и <strong>профайлер карты</strong> (профили и неизменяемые снимки с таймингами, точное сравнение снимков, декодер FCP/FCI).</p>
|
||||||
<p class="mb-3">Приложение — один статический файл <code class="font-mono text-sm">index.html</code>. Все вычисления выполняются в браузере; доступ к карте — через локальный HTTP-сервер (<code class="font-mono text-sm">pysim-simple-server</code>), оборачивающий библиотеку <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a>.</p>
|
<p class="mb-3">Приложение — один статический файл <code class="font-mono text-sm">index.html</code>. Все вычисления выполняются в браузере; доступ к карте — через локальный HTTP-сервер (<code class="font-mono text-sm">pysim-simple-server</code>), оборачивающий библиотеку <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a>.</p>
|
||||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Браузер (SIMple PWA) → HTTP :8080 → pysim-simple-server → pySim → PC/SC → ридер → UICC/SIM</pre>
|
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Браузер (SIMple PWA) → HTTP :8080 → pysim-simple-server → pySim → PC/SC → ридер → UICC/SIM</pre>
|
||||||
<p class="mb-3">Стандарты, на которые опирается приложение:</p>
|
<p class="mb-3">Стандарты, на которые опирается приложение:</p>
|
||||||
@@ -542,12 +542,12 @@ pysim-simple-server --http-port 8080</pre>
|
|||||||
<table class="w-full text-sm mb-3 border-collapse">
|
<table class="w-full text-sm mb-3 border-collapse">
|
||||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Сервер</th><th class="text-left py-1 px-2">Статус</th></tr></thead>
|
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Сервер</th><th class="text-left py-1 px-2">Статус</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2">✅ Совместимы</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">любая</td><td class="py-1 px-2 font-mono">та же мажорная</td><td class="py-1 px-2">✅ Совместимы</td></tr>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">0.x.x</td><td class="py-1 px-2">❌ Устарел — обновите сервер</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">любая</td><td class="py-1 px-2 font-mono">старее мажорная</td><td class="py-1 px-2">❌ Устарел — обновите сервер</td></tr>
|
||||||
<tr><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">2.x.x+</td><td class="py-1 px-2">⚠️ Сервер новее — обновите PWA</td></tr>
|
<tr><td class="py-1 px-2 font-mono">любая</td><td class="py-1 px-2 font-mono">новее мажорная</td><td class="py-1 px-2">⚠️ Сервер новее — обновите PWA</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="text-sm">PWA проверяет версию сервера при подключении через <code class="font-mono text-sm">GET /api/version</code> и предупреждает о несовместимости.</p>
|
<p class="text-sm">PWA проверяет версию сервера при подключении через <code class="font-mono text-sm">GET /api/version</code> и сравнивает мажорную версию (например, PWA 2.x с сервером 2.x; сервер 1.x помечается как устаревший).</p>
|
||||||
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+5
-5
@@ -19,7 +19,7 @@
|
|||||||
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Documentation</span></h1>
|
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Documentation</span></h1>
|
||||||
<section class="mb-10">
|
<section class="mb-10">
|
||||||
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Overview</h2>
|
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Overview</h2>
|
||||||
<p class="mb-3">SIMple is a Progressive Web App (PWA) for building APDU commands, assembling SCP80 secured packets, browsing the SIM Toolkit (STK) menu, and simulating a real network environment against a SIM/USIM/UICC card via a PC/SC reader.</p>
|
<p class="mb-3">SIMple is a workbench for UICC/SIM card specialists: an offline Progressive Web App (PWA) covering the full lab cycle — an <strong>APDU workbench</strong> (SIM/USIM RFM and Expanded Script builders, GlobalPlatform RAM commands, a C-APDU/R-APDU parser and a response decoder), an <strong>OTA lab</strong> (SCP80 secured packets and Java Card RAM installation, plus the terminal side of HTTP OTA — SCP81: BIP channel emulation, a PSK TLS administration server and APDU script execution), a <strong>phone/CAT simulator</strong> (SIM Toolkit menu, proactive commands and events, TERMINAL PROFILE editing, the C-AT dialogue log) and a <strong>card profiler</strong> (profiles and immutable snapshots with timings, exact snapshot comparison, FCP/FCI decoding).</p>
|
||||||
<p class="mb-3">The application is a single static <code class="font-mono text-sm">index.html</code> file. All computation runs in the browser; the card is accessed through a local HTTP server (<code class="font-mono text-sm">pysim-simple-server</code>) that wraps the <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a> library.</p>
|
<p class="mb-3">The application is a single static <code class="font-mono text-sm">index.html</code> file. All computation runs in the browser; the card is accessed through a local HTTP server (<code class="font-mono text-sm">pysim-simple-server</code>) that wraps the <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a> library.</p>
|
||||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Browser (SIMple PWA) → HTTP :8080 → pysim-simple-server → pySim → PC/SC → card reader → UICC/SIM</pre>
|
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Browser (SIMple PWA) → HTTP :8080 → pysim-simple-server → pySim → PC/SC → card reader → UICC/SIM</pre>
|
||||||
<p class="mb-3">Standards referenced across the application:</p>
|
<p class="mb-3">Standards referenced across the application:</p>
|
||||||
@@ -542,12 +542,12 @@ pysim-simple-server --http-port 8080</pre>
|
|||||||
<table class="w-full text-sm mb-3 border-collapse">
|
<table class="w-full text-sm mb-3 border-collapse">
|
||||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Server</th><th class="text-left py-1 px-2">Status</th></tr></thead>
|
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Server</th><th class="text-left py-1 px-2">Status</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2">✅ Compatible</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">any</td><td class="py-1 px-2 font-mono">same major</td><td class="py-1 px-2">✅ Compatible</td></tr>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">0.x.x</td><td class="py-1 px-2">❌ Outdated — update server</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">any</td><td class="py-1 px-2 font-mono">older major</td><td class="py-1 px-2">❌ Outdated — update server</td></tr>
|
||||||
<tr><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">2.x.x+</td><td class="py-1 px-2">⚠️ Server newer — update PWA</td></tr>
|
<tr><td class="py-1 px-2 font-mono">any</td><td class="py-1 px-2 font-mono">newer major</td><td class="py-1 px-2">⚠️ Server newer — update PWA</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="text-sm">The PWA checks the server version on connect via <code class="font-mono text-sm">GET /api/version</code> and warns if versions are incompatible.</p>
|
<p class="text-sm">The PWA checks the server version on connect via <code class="font-mono text-sm">GET /api/version</code> and compares the major version (e.g. 2.x PWA with a 2.x server; a 1.x server is flagged as outdated).</p>
|
||||||
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+10
-6
@@ -18,7 +18,7 @@
|
|||||||
<div class="max-w-7xl mx-auto px-6 py-2">
|
<div class="max-w-7xl mx-auto px-6 py-2">
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center justify-between mb-3">
|
||||||
<h1 class="text-2xl font-bold text-heading">SIMple <span id="slogan" class="text-sm font-normal text-gray-500 dark:text-slate-400 ml-2" data-l10n="SIM OTA with a Human Face">SIM OTA with a Human Face</span> <span class="text-xs text-gray-400 dark:text-slate-500 ml-1">v2.3.0</span></h1>
|
<h1 class="text-2xl font-bold text-heading">SIMple <span id="app-version" class="text-xs text-gray-400 dark:text-slate-500 ml-1"></span></h1>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span id="state-indicator" class="flex items-center select-none" style="cursor:default" title="Connecting...">
|
<span id="state-indicator" class="flex items-center select-none" style="cursor:default" title="Connecting...">
|
||||||
<span id="state-indicator-dot" class="text-xs text-gray-400" title="Connecting...">●</span>
|
<span id="state-indicator-dot" class="text-xs text-gray-400" title="Connecting...">●</span>
|
||||||
@@ -1302,6 +1302,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// ===== 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 = '2.3.1';
|
||||||
|
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||||
|
|
||||||
// ===== Tab switching =====
|
// ===== Tab switching =====
|
||||||
function switchTab(name) {
|
function switchTab(name) {
|
||||||
if (_scp81Timer && name !== 'scp81') { clearInterval(_scp81Timer); _scp81Timer = null; }
|
if (_scp81Timer && name !== 'scp81') { clearInterval(_scp81Timer); _scp81Timer = null; }
|
||||||
@@ -4912,13 +4918,14 @@ async function pysimConnect() {
|
|||||||
if (!ver.version) throw new Error('No version');
|
if (!ver.version) throw new Error('No version');
|
||||||
_pysimServerAvailable = true;
|
_pysimServerAvailable = true;
|
||||||
const sv = (ver.version || '').split('.').map(Number);
|
const sv = (ver.version || '').split('.').map(Number);
|
||||||
if (sv[0] < 1 || (sv[0] === 1 && sv[1] < 0)) {
|
const pv = SIMPLE_VERSION.split('.').map(Number);
|
||||||
|
if (sv[0] < pv[0]) {
|
||||||
msgEl.innerHTML = '<span class="text-red-500">Server version ' + esc(ver.version) + ' is outdated. Please update pysim-simple-server.</span>';
|
msgEl.innerHTML = '<span class="text-red-500">Server version ' + esc(ver.version) + ' is outdated. Please update pysim-simple-server.</span>';
|
||||||
btn.textContent = t('Connect');
|
btn.textContent = t('Connect');
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sv[0] > 1) {
|
if (sv[0] > pv[0]) {
|
||||||
msgEl.innerHTML = '<span class="text-yellow-500">Warning: server version ' + esc(ver.version) + ' is newer than expected. Update SIMple for full compatibility.</span>';
|
msgEl.innerHTML = '<span class="text-yellow-500">Warning: server version ' + esc(ver.version) + ' is newer than expected. Update SIMple for full compatibility.</span>';
|
||||||
}
|
}
|
||||||
const status = await pysimFetch('/api/status');
|
const status = await pysimFetch('/api/status');
|
||||||
@@ -11400,7 +11407,6 @@ const LANG_RU = {
|
|||||||
'Response data (hex)': 'Данные ответа (hex)',
|
'Response data (hex)': 'Данные ответа (hex)',
|
||||||
'— Select command —': '— Выберите команду —',
|
'— Select command —': '— Выберите команду —',
|
||||||
'— select preset —': '— выберите предустановленный —',
|
'— select preset —': '— выберите предустановленный —',
|
||||||
'SIM OTA with a Human Face': 'SIM OTA с человеческим лицом',
|
|
||||||
'Error: specify SELECT parameters': 'Ошибка: укажите параметры SELECT',
|
'Error: specify SELECT parameters': 'Ошибка: укажите параметры SELECT',
|
||||||
'Error: unknown command': 'Ошибка: неизвестная команда',
|
'Error: unknown command': 'Ошибка: неизвестная команда',
|
||||||
'Error: specify data': 'Ошибка: укажите данные',
|
'Error: specify data': 'Ошибка: укажите данные',
|
||||||
@@ -11890,8 +11896,6 @@ function translatePage() {
|
|||||||
const key = el.getAttribute('data-l10n');
|
const key = el.getAttribute('data-l10n');
|
||||||
el.textContent = t(key);
|
el.textContent = t(key);
|
||||||
});
|
});
|
||||||
const slogan = document.getElementById('slogan');
|
|
||||||
if (slogan) slogan.textContent = t('SIM OTA with a Human Face');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isViewVisible(id) {
|
function isViewVisible(id) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "SIMple",
|
"name": "SIMple",
|
||||||
"short_name": "SIMple",
|
"short_name": "SIMple",
|
||||||
"description": "Offline APDU helper for SIM/USIM/RAM commands",
|
"description": "UICC/SIM workbench: APDU toolkit, SIM OTA (SCP80/SCP81), STK/CAT simulator, card profiler",
|
||||||
"start_url": "index.html",
|
"start_url": "index.html",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#fafafa",
|
"background_color": "#fafafa",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "simple",
|
"name": "simple",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Standalone offline HTML/JS tool for building APDU commands for SIM, USIM, and GlobalPlatform RAM, plus encoding conversions.",
|
"description": "SIMple — a UICC workbench: APDU toolkit, SCP80/SCP81 OTA lab, STK/CAT (phone) simulator and card profiler.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npx tailwindcss -i src/style.css -o style.css --config tailwind.config.js && cat src/contrast.css >> style.css",
|
"build": "npx tailwindcss -i src/style.css -o style.css --config tailwind.config.js && cat src/contrast.css >> style.css",
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simple-v189';
|
const CACHE = 'simple-v190';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pysim-simple-server"
|
name = "pysim-simple-server"
|
||||||
version = "2.3.0"
|
version = "2.3.1"
|
||||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from osmocom.construct import GsmOrUcs2Adapter
|
|||||||
from osmocom.tlv import BER_TLV_IE
|
from osmocom.tlv import BER_TLV_IE
|
||||||
|
|
||||||
|
|
||||||
VERSION = '2.3.0'
|
VERSION = '2.3.1'
|
||||||
|
|
||||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user