ui: regroup tabs — Remote APDU, Card reader, Profiler, Phone simulator
- C-APDU tab renamed Remote APDU (label untranslated EN/RU); Response parser moved from a top-level tab to a pill under it - Profiler moved from the Card reader sub-tabs to a top-level tab - Proactive UICC moved to a top-level tab and renamed Phone simulator - Card reader keeps File manager, Custom files, pySim command line, Raw APDU - modals (event send, profiler scan/snapshot, STK menu) moved outside the tab containers so they can open from any tab - STK overlay disables the top-level tabs while waiting for user input - OTA PoR jump now goes to Remote APDU > Response parser - help EN/RU restructured (2.8 Response parser, 5 Profiler, 6 Phone simulator, subsequent sections renumbered, anchors kept); READMEs and AGENTS.md updated - html.test.js asserts the new tab/pill structure; SW cache v92 -> v93
This commit is contained in:
+101
-92
@@ -44,13 +44,14 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Шапка</strong> — версия приложения, кнопка <strong>INSTALL PWA</strong> (появляется, когда браузер предлагает установку, для офлайн-работы), ссылки на проект на GitHub и на эту справку, переключатель языка <strong>EN/RU</strong> и переключатель тёмной/светлой <strong>темы</strong>.</li>
|
||||
<li>Выбор языка и темы хранится в <code class="font-mono text-sm">localStorage</code> и сохраняется между перезагрузками.</li>
|
||||
<li>Ссылка <strong>справка</strong> открывает эту документацию на разделе, соответствующем текущему представлению (например, подвкладка «Профайлер» открывает §5.6).</li>
|
||||
<li>Вкладки верхнего уровня: <strong>Remote APDU</strong> (<strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong>, <strong>Разбор C-APDU</strong>, <strong>«Парсер ответов»</strong>), <strong>SCP80</strong> (<strong>Secured Packet</strong>, <strong>Карты</strong>, <strong>RAM</strong>), <strong>«Картридер»</strong> (<strong>Файловый менеджер</strong>, <strong>Пользовательские файлы</strong>, <strong>Командная строка pySim</strong>, <strong>Отправка APDU</strong>), <strong>«Профайлер»</strong> и <strong>«Симулятор телефона»</strong>.</li>
|
||||
<li>Ссылка <strong>справка</strong> открывает эту документацию на разделе, соответствующем текущему представлению (например, вкладка «Профайлер» открывает §5).</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="c-apdu" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">2. Вкладка C-APDU</h2>
|
||||
<p class="mb-3">Построение командных APDU (C-APDU). Шесть подвкладок охватывают разные поколения карт и наборы команд: <strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong> и <strong>Разбор C-APDU</strong>.</p>
|
||||
<h2 id="c-apdu" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">2. Вкладка Remote APDU</h2>
|
||||
<p class="mb-3">Построение командных APDU (C-APDU). Семь подвкладок охватывают разные поколения карт, наборы команд и инструменты разбора: <strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong>, <strong>Разбор C-APDU</strong> и <strong>«Парсер ответов»</strong>.</p>
|
||||
|
||||
<h3 id="sim-rfm" class="text-lg font-medium mb-2">2.1 SIM RFM</h3>
|
||||
<p class="mb-2">CLA = <code class="font-mono text-sm">A0</code> (GSM 11.11 / TS 151 011, ISO 7816-4). Удалённое управление файлами классических SIM-карт.</p>
|
||||
@@ -138,7 +139,7 @@
|
||||
</ul>
|
||||
|
||||
<h4 id="expanded-response" class="font-medium mb-2 text-base">Декодирование ответов (TS 102 226 §5.2.2)</h4>
|
||||
<p class="text-sm mb-2">Входящие ответы Proof of Receipt декодируются сервером — формат expanded Remote Application response data (TS 102 226 §5.2.2) или компактный формат. Представление Secured Packet показывает результат после <strong>Отправить на карту</strong> (см. <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>): статус PoR (TAR, счётчик, сырой PoR), а статусное слово и данные ответа последней команды подставляются на вкладку <strong>«Парсер ответов»</strong>.</p>
|
||||
<p class="text-sm mb-2">Входящие ответы Proof of Receipt декодируются сервером — формат expanded Remote Application response data (TS 102 226 §5.2.2) или компактный формат. Представление Secured Packet показывает результат после <strong>Отправить на карту</strong> (см. <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>): статус PoR (TAR, счётчик, сырой PoR), а статусное слово и данные ответа последней команды подставляются в подвкладку <strong>«Парсер ответов»</strong> (Remote APDU).</p>
|
||||
|
||||
<h3 id="ram-gp" class="text-lg font-medium mb-2">2.4 RAM/GP</h3>
|
||||
<p class="mb-2">CLA = <code class="font-mono text-sm">80</code> (GlobalPlatform Card Specification v2.3.1). Команды удалённого управления приложениями. Строятся тем же сборщиком цепочки, что и SIM/USIM.</p>
|
||||
@@ -227,6 +228,17 @@
|
||||
<p class="text-sm mb-2"><strong>Упаковать в Secured packet</strong> отправляет готовый payload на вкладку SCP80 для заполнения SPI/счётчика — там укажите TAR, который слушает SD (обычно TAR OTASD).</p>
|
||||
|
||||
|
||||
|
||||
<h3 id="response-parser" class="text-lg font-medium mb-2">2.8 «Парсер ответов»</h3>
|
||||
<p class="mb-3">Декодирует raw-ответ команды: выберите отправленную команду, введите SW (например, <code class="font-mono text-sm">9000</code>) и hex данных ответа, затем нажмите <strong>Декодировать</strong>. Поля также автоматически заполняются статусным словом и данными ответа последней команды после успешного нажатия <strong>Отправить на карту</strong> (см. <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>).</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1">
|
||||
<li><strong>Команда</strong> — группа SIM/USIM (SELECT, STATUS, READ/UPDATE, PIN-операции, CAT-команды типа TERMINAL PROFILE/ENVELOPE/FETCH/TERMINAL RESPONSE, MANAGE CHANNEL, …) или группа RAM/GP (INSTALL, LOAD, DELETE, GET/STORE DATA, auth, SCP-команды).</li>
|
||||
<li><strong>Декодирование SW</strong> — статусные слова разрешаются по generic-, UICC- (TS 102 221) и GlobalPlatform-таблицам, контекст определяется автоматически.</li>
|
||||
<li><strong>Декодирование привилегий</strong> — ответы GET DATA / INSTALL декодируют байты привилегий в читаемые флаги.</li>
|
||||
<li><strong>Данные ответа</strong> — raw hex отображается и интерпретируется согласно команде (например, FCP-шаблоны SELECT).</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="scp80" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">3. Вкладка SCP80</h2>
|
||||
<p class="mb-3">Верхнеуровневая вкладка <strong>SCP80</strong> объединяет разделы, связанные с SCP80. Переключение — тремя переключателями: <strong>Secured Packet</strong>, <strong>Карты</strong> и <strong>RAM</strong>. Собирает защищённые пакеты SCP80 по ETSI TS 102 225.</p>
|
||||
@@ -260,7 +272,7 @@
|
||||
<li>AES требует счётчик с защитой от повтора: биты SPI1 b5 b4 должны быть <code class="font-mono text-sm">10</code> (счётчик больше) или <code class="font-mono text-sm">11</code> (счётчик +1) согласно TS 102 225 §5.1.2/§5.1.3.1</li>
|
||||
<li>Байт паддинга настраивается (<code class="font-mono text-sm">00</code> по умолчанию или <code class="font-mono text-sm">FF</code>)</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Кнопка <strong>Проверить в pySim</strong> сверяет собранный пакет с эталонной реализацией <code class="font-mono text-sm">OtaDialectSms.encode_cmd</code>. Кнопка <strong>Отправить на карту</strong> доставляет пакет через ENVELOPE SMS-PP-DOWNLOAD (при подключении к серверу). Полученный Proof of Receipt декодируется и показывается строкой статуса PoR (статус, TAR, счётчик, сырой PoR); статусное слово и данные ответа последней команды подставляются на вкладку <strong>«Парсер ответов»</strong>, а успешный PoR увеличивает счётчик повторов и очищает пакет.</p>
|
||||
<p class="text-sm mb-3">Кнопка <strong>Проверить в pySim</strong> сверяет собранный пакет с эталонной реализацией <code class="font-mono text-sm">OtaDialectSms.encode_cmd</code>. Кнопка <strong>Отправить на карту</strong> доставляет пакет через ENVELOPE SMS-PP-DOWNLOAD (при подключении к серверу). Полученный Proof of Receipt декодируется и показывается строкой статуса PoR (статус, TAR, счётчик, сырой PoR); статусное слово и данные ответа последней команды подставляются в подвкладку <strong>«Парсер ответов»</strong> (Remote APDU), а успешный PoR увеличивает счётчик повторов и очищает пакет.</p>
|
||||
|
||||
<h3 id="cards" class="text-lg font-medium mb-2">3.2 Карты</h3>
|
||||
<p class="mb-2">Хранит предустановки карт локально в браузере (<code class="font-mono text-sm">localStorage</code>), чтобы представление Secured Packet могло автоматически подставлять ключи и параметры.</p>
|
||||
@@ -301,21 +313,10 @@
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="response-parser" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">4. Вкладка «Парсер ответов»</h2>
|
||||
<p class="mb-3">Декодирует raw-ответ команды: выберите отправленную команду, введите SW (например, <code class="font-mono text-sm">9000</code>) и hex данных ответа, затем нажмите <strong>Декодировать</strong>. Поля также автоматически заполняются статусным словом и данными ответа последней команды после успешного нажатия <strong>Отправить на карту</strong> (см. <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>).</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1">
|
||||
<li><strong>Команда</strong> — группа SIM/USIM (SELECT, STATUS, READ/UPDATE, PIN-операции, CAT-команды типа TERMINAL PROFILE/ENVELOPE/FETCH/TERMINAL RESPONSE, MANAGE CHANNEL, …) или группа RAM/GP (INSTALL, LOAD, DELETE, GET/STORE DATA, auth, SCP-команды).</li>
|
||||
<li><strong>Декодирование SW</strong> — статусные слова разрешаются по generic-, UICC- (TS 102 221) и GlobalPlatform-таблицам, контекст определяется автоматически.</li>
|
||||
<li><strong>Декодирование привилегий</strong> — ответы GET DATA / INSTALL декодируют байты привилегий в читаемые флаги.</li>
|
||||
<li><strong>Данные ответа</strong> — raw hex отображается и интерпретируется согласно команде (например, FCP-шаблоны SELECT).</li>
|
||||
</ul>
|
||||
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">4. Вкладка «Картридер» (pySim)</h2>
|
||||
<p class="mb-3">Подключение к локальному <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> для работы с картой: введите URL сервера (по умолчанию <code class="font-mono text-sm">http://127.0.0.1:8080</code>) и нажмите <strong>Подключиться</strong>. Область статуса показывает состояние ридера/карты, а <strong>Подключить карту</strong> (пере)инициализирует карту после вставки. Подвкладки: <strong>Файловый менеджер</strong>, <strong>Пользовательские файлы</strong>, <strong>Командная строка pySim</strong> и <strong>Отправка APDU</strong>. <strong>«Профайлер»</strong> и <strong>«Симулятор телефона»</strong> — отдельные вкладки верхнего уровня.</p>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">5. Вкладка «Картридер» (pySim)</h2>
|
||||
<p class="mb-3">Подключение к локальному <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> для работы с картой: введите URL сервера (по умолчанию <code class="font-mono text-sm">http://127.0.0.1:8080</code>) и нажмите <strong>Подключиться</strong>. Область статуса показывает состояние ридера/карты, а <strong>Подключить карту</strong> (пере)инициализирует карту после вставки. Подвкладки: <strong>Файловый менеджер</strong>, <strong>Пользовательские файлы</strong>, <strong>Профайлер</strong>, <strong>Командная строка pySim</strong>, <strong>Отправка APDU</strong> и <strong>Проактивный UICC</strong>.</p>
|
||||
|
||||
<h3 id="file-manager" class="text-lg font-medium mb-2">5.1 Файловый менеджер</h3>
|
||||
<h3 id="file-manager" class="text-lg font-medium mb-2">4.1 Файловый менеджер</h3>
|
||||
<p class="text-sm mb-2">Дерево файловой системы отображается слева; выбор файла открывает панель деталей справа.</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Прочитать</strong> — чтение файла (автоопределение transparent/record)</li>
|
||||
@@ -323,48 +324,55 @@
|
||||
<li><strong>Данные как на карте / Декодированные данные</strong> — переключение между hex-дампом и декодированным JSON</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="custom-files" class="text-lg font-medium mb-2">5.2 Пользовательские файлы</h3>
|
||||
<h3 id="custom-files" class="text-lg font-medium mb-2">4.2 Пользовательские файлы</h3>
|
||||
<p class="text-sm mb-3">Добавление файлов, не покрытых моделью pySim: введите полный путь (например, <code class="font-mono text-sm">3F00/7F20/6F46</code>) и псевдоним (например, <code class="font-mono text-sm">EF.SPN</code>), затем нажмите <strong>Добавить</strong>; добавленные файлы появляются в дереве «Файловый менеджер». Список сохраняется в <code class="font-mono text-sm">localStorage</code>; обмен — <strong>Экспорт в JSON</strong> / <strong>Экспорт в файл</strong> и <strong>Импорт из файла</strong> / <strong>Вставить и импортировать</strong> / <strong>Импорт JSON из буфера</strong>.</p>
|
||||
|
||||
<h3 id="pysim-cmdline" class="text-lg font-medium mb-2">5.3 Командная строка pySim</h3>
|
||||
<h3 id="pysim-cmdline" class="text-lg font-medium mb-2">4.3 Командная строка pySim</h3>
|
||||
<p class="text-sm mb-3">Выполнение любых команд pySim-shell с подсказками (300 мс) и автодополнением.</p>
|
||||
|
||||
<h3 id="raw-apdu" class="text-lg font-medium mb-2">5.4 Отправка APDU</h3>
|
||||
<h3 id="raw-apdu" class="text-lg font-medium mb-2">4.4 Отправка APDU</h3>
|
||||
<p class="text-sm mb-3">Отправка произвольного APDU и просмотр ответа.</p>
|
||||
|
||||
<h3 id="proactive-uicc" class="text-lg font-medium mb-2">5.5 Проактивный UICC</h3>
|
||||
<p class="text-sm mb-3">Работа с сессией Card Application Toolkit: меню STK, подписанные события, журнал проактивных команд, словарь данных PROVIDE LOCAL INFORMATION и опрос STATUS.</p>
|
||||
<h3 id="usage-scenarios" class="text-lg font-medium mb-2">4.5 Сценарии использования</h3>
|
||||
|
||||
<h4 id="stk-menu" class="font-medium mb-1">5.5.1 Меню STK</h4>
|
||||
<p class="text-sm mb-3">Если карта выдала команду SET UP MENU, вверху этого представления появляется блок «Меню STK» с изумрудной кнопкой <strong>STK: <название></strong>, открывающей оверлей меню (браузер STK-меню карты). Если карта не задала меню, вместо кнопки показывается «Меню не задано картой». Состояние меню обновляется при каждом открытии представления. Интерактивные проактивные команды всегда получают TERMINAL RESPONSE: оверлей ждёт вашего выбора, и если вы не ответили и не нажали <strong>Timeout</strong>, сервер сам отвечает результатом timeout через <code class="font-mono text-sm">--menu-timeout</code> секунд (по умолчанию 60, <code class="font-mono text-sm">0</code> отключает).</p>
|
||||
<h4 id="scenario-a" class="font-medium mb-1">Сценарий A — Работа с файлами, не входящими в модель pySim («Пользовательские файлы»)</h4>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Получите FID целевого файла (документация вендора или анализ ATR/файловой системы; такие файлы часто отсутствуют в открытых спецификациях).</li>
|
||||
<li>Откройте вкладку <strong>«Картридер»</strong> → подвкладку <strong>«Пользовательские файлы»</strong>.</li>
|
||||
<li>Введите полный путь (например, <code class="font-mono text-sm">3F00/7F20/6F46</code>) и псевдоним (например, <code class="font-mono text-sm">EF.SPN</code>).</li>
|
||||
<li>Нажмите <strong>Добавить</strong> — файл появится в дереве курсивом (непроверенный).</li>
|
||||
<li>Кликните по файлу для проверки существования; при успехе (<code class="font-mono text-sm">9000</code>) он работает как обычный файл.</li>
|
||||
<li>Читайте, редактируйте и сохраняйте hex-данные; переключайте <strong>Данные как на карте</strong> / <strong>Декодированные данные</strong>.</li>
|
||||
<li>Экспортируйте список пользовательских файлов в JSON для переноса на другие машины.</li>
|
||||
</ol>
|
||||
|
||||
<h4 id="subscribed-events" class="font-medium mb-1">5.5.2 Подписанные события (SET UP EVENT LIST)</h4>
|
||||
<p class="text-sm mb-2">События, которые отслеживает карта. У каждого события есть кнопка <strong>Отправить</strong>, открывающая форму, специфичную для типа события:</p>
|
||||
<h4 id="scenario-b" class="font-medium mb-1">Сценарий B — Симуляция реальной сетевой среды для тестирования SIM</h4>
|
||||
<p class="text-sm mb-1"><strong>B.1 Ответы на PROVIDE LOCAL INFORMATION (PLI)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Откройте <strong>Симулятор телефона</strong> → <strong>Данные для PROVIDE LOCAL INFORMATION</strong>.</li>
|
||||
<li>Используйте формы декодирования/кодирования для IMEI (<code class="font-mono text-sm">01</code>), Location Info (<code class="font-mono text-sm">00</code>), Access Technology (<code class="font-mono text-sm">06</code>) и т.д.</li>
|
||||
<li>Нажмите <strong>Сохранить</strong> — значения сохранятся на сервере.</li>
|
||||
<li>Включите <strong>Опрос</strong> (интервал 30 с), чтобы карта периодически выдавала PLI.</li>
|
||||
<li>Сервер вставляет значения словаря в каждый TERMINAL RESPONSE.</li>
|
||||
<li>Проверьте в журнале проактивных команд: запись PLI покажет декодированный ответ.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.2 Симуляция сетевых действий через ENVELOPE (event download)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Проверьте список <strong>подписанных событий</strong> (из SET UP EVENT LIST).</li>
|
||||
<li>Нажмите <strong>Отправить</strong> на событии (например, Location Status) и заполните форму; будет отправлен <code class="font-mono text-sm">ENVELOPE(Event Download)</code>.</li>
|
||||
<li>Для <strong>Network Rejection</strong> выберите тип регистрации → поля местоположения → технологию доступа → причину отклонения.</li>
|
||||
<li>Карта может ответить проактивной командой, которую обработчик цепочки зарегистрирует и обработает автоматически.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.3 Проверка симулированной среды</strong></p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>События без данных</strong> (User Activity, Idle Screen, Data Available, …) — уведомление в один клик</li>
|
||||
<li><strong>Location Status</strong> — выпадающий список: Normal / Limited / No service (тег <code class="font-mono text-sm">9B</code>)</li>
|
||||
<li><strong>Access Technology Change</strong> — 13 типов RAT (тег <code class="font-mono text-sm">BF</code>)</li>
|
||||
<li><strong>Network Rejection</strong> — полная адаптивная форма: тип регистрации (LU / GPRS / EPS / 5GS), поля местоположения (MCC, MNC, LAC, RAC, TAC), технология доступа и единый выпадающий список из 53 кодов причин (EMM, GMM, 5GMM и LU)</li>
|
||||
<li>Журнал проактивных команд показывает полный цикл (команда + байты TERMINAL RESPONSE).</li>
|
||||
<li>Кнопка <strong>Отправить STATUS</strong> / автопросмотр поддерживают сессию CAT (цикл дренажа).</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Отправка события использует <code class="font-mono text-sm">ENVELOPE(Event Download)</code> по TS 102 223 / TS 131 111.</p>
|
||||
|
||||
<h4 id="proactive-log" class="font-medium mb-1">5.5.3 Журнал проактивных команд</h4>
|
||||
<p class="text-sm mb-2">Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Для команд с данными ответа показывается строка <code class="font-mono text-sm">Ответ:</code> с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.</p>
|
||||
|
||||
<h4 id="pli-dict" class="font-medium mb-1">5.5.4 Словарь данных PROVIDE LOCAL INFORMATION</h4>
|
||||
<p class="text-sm mb-2">Редактируемые hex-значения для всех 22 квалификаторов PLI (TS 102 223 §8.6 + TS 131 111). У десяти квалификаторов есть встроенные формы декодирования/кодирования:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>00</strong> Location Info (MCC, MNC, LAC/TAC, Cell ID)</li>
|
||||
<li><strong>01</strong> IMEI · <strong>03</strong> Дата/время/TZ · <strong>04</strong> Язык · <strong>05</strong> Timing Advance</li>
|
||||
<li><strong>06</strong> Access Technology · <strong>08</strong> IMEISV · <strong>09</strong> Search Mode</li>
|
||||
<li><strong>0A</strong> Battery · <strong>0E</strong> Multiple Access Technologies</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Значения хранятся на сервере до перезапуска. Когда карта выдаёт PLI, сервер вставляет значения словаря в TERMINAL RESPONSE.</p>
|
||||
|
||||
<h4 id="status-polling" class="font-medium mb-1">5.5.5 Опрос STATUS</h4>
|
||||
<p class="text-sm mb-3">Кнопка <strong>Отправить STATUS</strong> отправляет STATUS (F2) вручную. Переключатель <strong>Опрос</strong> включает фоновый опрос: после настраиваемого интервала бездействия (аргумент сервера <code class="font-mono text-sm">--poll-interval</code>, 1–255 с, по умолчанию 30 с, <code class="font-mono text-sm">0</code> отключает опрос) сервер отправляет STATUS и обрабатывает любую ожидающую проактивную команду. При извлечении карты опрос останавливается, а состояние карты сбрасывается.</p>
|
||||
|
||||
<h3 id="profiler" class="text-lg font-medium mb-2">5.6 Профайлер</h3>
|
||||
<section class="mb-10">
|
||||
<h2 id="profiler" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">5. Профайлер</h2>
|
||||
<p class="text-sm mb-2">Проверяет соответствие карты именованному <strong>профилю</strong> — упорядоченному набору правил, описывающих ожидаемую файловую систему и (опционально) содержимое файлов. Профили хранятся в <code class="font-mono text-sm">localStorage</code>.</p>
|
||||
<h4 class="font-medium mb-1">Список профилей</h4>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
@@ -395,48 +403,49 @@
|
||||
<li><strong>Сравнить снимки</strong> сравнивает два снимка без картридера так же, как проверка профиля: выберите <em>эталонный</em> снимок и <em>снимок для проверки</em>, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="usage-scenarios" class="text-lg font-medium mb-2">5.7 Сценарии использования</h3>
|
||||
|
||||
<h4 id="scenario-a" class="font-medium mb-1">Сценарий A — Работа с файлами, не входящими в модель pySim («Пользовательские файлы»)</h4>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Получите FID целевого файла (документация вендора или анализ ATR/файловой системы; такие файлы часто отсутствуют в открытых спецификациях).</li>
|
||||
<li>Откройте вкладку <strong>«Картридер»</strong> → подвкладку <strong>«Пользовательские файлы»</strong>.</li>
|
||||
<li>Введите полный путь (например, <code class="font-mono text-sm">3F00/7F20/6F46</code>) и псевдоним (например, <code class="font-mono text-sm">EF.SPN</code>).</li>
|
||||
<li>Нажмите <strong>Добавить</strong> — файл появится в дереве курсивом (непроверенный).</li>
|
||||
<li>Кликните по файлу для проверки существования; при успехе (<code class="font-mono text-sm">9000</code>) он работает как обычный файл.</li>
|
||||
<li>Читайте, редактируйте и сохраняйте hex-данные; переключайте <strong>Данные как на карте</strong> / <strong>Декодированные данные</strong>.</li>
|
||||
<li>Экспортируйте список пользовательских файлов в JSON для переноса на другие машины.</li>
|
||||
</ol>
|
||||
|
||||
<h4 id="scenario-b" class="font-medium mb-1">Сценарий B — Симуляция реальной сетевой среды для тестирования SIM</h4>
|
||||
<p class="text-sm mb-1"><strong>B.1 Ответы на PROVIDE LOCAL INFORMATION (PLI)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Откройте <strong>Проактивный UICC</strong> → <strong>Данные для PROVIDE LOCAL INFORMATION</strong>.</li>
|
||||
<li>Используйте формы декодирования/кодирования для IMEI (<code class="font-mono text-sm">01</code>), Location Info (<code class="font-mono text-sm">00</code>), Access Technology (<code class="font-mono text-sm">06</code>) и т.д.</li>
|
||||
<li>Нажмите <strong>Сохранить</strong> — значения сохранятся на сервере.</li>
|
||||
<li>Включите <strong>Опрос</strong> (интервал 30 с), чтобы карта периодически выдавала PLI.</li>
|
||||
<li>Сервер вставляет значения словаря в каждый TERMINAL RESPONSE.</li>
|
||||
<li>Проверьте в журнале проактивных команд: запись PLI покажет декодированный ответ.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.2 Симуляция сетевых действий через ENVELOPE (event download)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Проверьте список <strong>подписанных событий</strong> (из SET UP EVENT LIST).</li>
|
||||
<li>Нажмите <strong>Отправить</strong> на событии (например, Location Status) и заполните форму; будет отправлен <code class="font-mono text-sm">ENVELOPE(Event Download)</code>.</li>
|
||||
<li>Для <strong>Network Rejection</strong> выберите тип регистрации → поля местоположения → технологию доступа → причину отклонения.</li>
|
||||
<li>Карта может ответить проактивной командой, которую обработчик цепочки зарегистрирует и обработает автоматически.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.3 Проверка симулированной среды</strong></p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li>Журнал проактивных команд показывает полный цикл (команда + байты TERMINAL RESPONSE).</li>
|
||||
<li>Кнопка <strong>Отправить STATUS</strong> / автопросмотр поддерживают сессию CAT (цикл дренажа).</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Установка сервера</h2>
|
||||
<p class="mb-3">Для работы с картой (вкладка «Картридер», «Проактивный UICC», доставка OTA) нужен локальный <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — встроенный в OTAMan HTTP-сервер, оборачивающий pySim, работающий с ридером через PC/SC или serial и раздающий сам PWA (откройте <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
|
||||
<h2 id="proactive-uicc" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Симулятор телефона</h2>
|
||||
<p class="text-sm mb-3">Работа с сессией Card Application Toolkit: меню STK, подписанные события, журнал проактивных команд, словарь данных PROVIDE LOCAL INFORMATION и опрос STATUS.</p>
|
||||
|
||||
<h3 id="prerequisites" class="text-lg font-medium mb-2">6.1 Требования</h3>
|
||||
<h3 id="stk-menu" class="text-lg font-medium mb-2">Меню STK</h3>
|
||||
<p class="text-sm mb-3">Если карта выдала команду SET UP MENU, вверху этого представления появляется блок «Меню STK» с изумрудной кнопкой <strong>STK: <название></strong>, открывающей оверлей меню (браузер STK-меню карты). Если карта не задала меню, вместо кнопки показывается «Меню не задано картой». Состояние меню обновляется при каждом открытии представления. Интерактивные проактивные команды всегда получают TERMINAL RESPONSE: оверлей ждёт вашего выбора, и если вы не ответили и не нажали <strong>Timeout</strong>, сервер сам отвечает результатом timeout через <code class="font-mono text-sm">--menu-timeout</code> секунд (по умолчанию 60, <code class="font-mono text-sm">0</code> отключает).</p>
|
||||
|
||||
<h3 id="subscribed-events" class="text-lg font-medium mb-2">6.2 Подписанные события (SET UP EVENT LIST)</h3>
|
||||
<p class="text-sm mb-2">События, которые отслеживает карта. У каждого события есть кнопка <strong>Отправить</strong>, открывающая форму, специфичную для типа события:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>События без данных</strong> (User Activity, Idle Screen, Data Available, …) — уведомление в один клик</li>
|
||||
<li><strong>Location Status</strong> — выпадающий список: Normal / Limited / No service (тег <code class="font-mono text-sm">9B</code>)</li>
|
||||
<li><strong>Access Technology Change</strong> — 13 типов RAT (тег <code class="font-mono text-sm">BF</code>)</li>
|
||||
<li><strong>Network Rejection</strong> — полная адаптивная форма: тип регистрации (LU / GPRS / EPS / 5GS), поля местоположения (MCC, MNC, LAC, RAC, TAC), технология доступа и единый выпадающий список из 53 кодов причин (EMM, GMM, 5GMM и LU)</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Отправка события использует <code class="font-mono text-sm">ENVELOPE(Event Download)</code> по TS 102 223 / TS 131 111.</p>
|
||||
|
||||
<h3 id="proactive-log" class="text-lg font-medium mb-2">6.3 Журнал проактивных команд</h3>
|
||||
<p class="text-sm mb-2">Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Для команд с данными ответа показывается строка <code class="font-mono text-sm">Ответ:</code> с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.</p>
|
||||
|
||||
<h3 id="pli-dict" class="text-lg font-medium mb-2">6.4 Словарь данных PROVIDE LOCAL INFORMATION</h3>
|
||||
<p class="text-sm mb-2">Редактируемые hex-значения для всех 22 квалификаторов PLI (TS 102 223 §8.6 + TS 131 111). У десяти квалификаторов есть встроенные формы декодирования/кодирования:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>00</strong> Location Info (MCC, MNC, LAC/TAC, Cell ID)</li>
|
||||
<li><strong>01</strong> IMEI · <strong>03</strong> Дата/время/TZ · <strong>04</strong> Язык · <strong>05</strong> Timing Advance</li>
|
||||
<li><strong>06</strong> Access Technology · <strong>08</strong> IMEISV · <strong>09</strong> Search Mode</li>
|
||||
<li><strong>0A</strong> Battery · <strong>0E</strong> Multiple Access Technologies</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Значения хранятся на сервере до перезапуска. Когда карта выдаёт PLI, сервер вставляет значения словаря в TERMINAL RESPONSE.</p>
|
||||
|
||||
<h3 id="status-polling" class="text-lg font-medium mb-2">6.5 Опрос STATUS</h3>
|
||||
<p class="text-sm mb-3">Кнопка <strong>Отправить STATUS</strong> отправляет STATUS (F2) вручную. Переключатель <strong>Опрос</strong> включает фоновый опрос: после настраиваемого интервала бездействия (аргумент сервера <code class="font-mono text-sm">--poll-interval</code>, 1–255 с, по умолчанию 30 с, <code class="font-mono text-sm">0</code> отключает опрос) сервер отправляет STATUS и обрабатывает любую ожидающую проактивную команду. При извлечении карты опрос останавливается, а состояние карты сбрасывается.</p>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">7. Установка сервера</h2>
|
||||
<p class="mb-3">Для работы с картой (вкладка «Картридер», «Симулятор телефона», доставка OTA) нужен локальный <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — встроенный в OTAMan HTTP-сервер, оборачивающий pySim, работающий с ридером через PC/SC или serial и раздающий сам PWA (откройте <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
|
||||
|
||||
<h3 id="prerequisites" class="text-lg font-medium mb-2">7.1 Требования</h3>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Python 3.8+</strong> с <code class="font-mono text-sm">pip</code></li>
|
||||
<li><strong>Git</strong></li>
|
||||
@@ -444,20 +453,20 @@
|
||||
<li><strong>Только Windows</strong> — используйте <strong>Python 3.10–3.13</strong> (рекомендуется 3.13): <code class="font-mono text-sm">pyscard</code> (обёртка драйвера PC/SC) поставляет готовые wheels для этих версий. На Python 3.9 / 3.14 pip собирает <code class="font-mono text-sm">pyscard</code> из исходников, для чего требуются Microsoft C++ Build Tools («Desktop development with C++»). Мост SMPP (<code class="font-mono text-sm">smpp.twisted3</code>) на Windows намеренно не устанавливается, поэтому для Python 3.10–3.13 C++ Build Tools не нужны.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">6.2 Быстрый старт — Linux / macOS</h3>
|
||||
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">7.2 Быстрый старт — Linux / macOS</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
|
||||
cd otaman
|
||||
chmod +x setup.sh start.sh
|
||||
./setup.sh # создаёт .venv, устанавливает pysim + сервер (однократно)
|
||||
./start.sh # запускает сервер (PWA + API, автоопределение ридера)</pre>
|
||||
|
||||
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">6.3 Быстрый старт — Windows</h3>
|
||||
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">7.3 Быстрый старт — Windows</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
|
||||
cd otaman
|
||||
setup.bat # создаёт .venv, устанавливает pysim + сервер (однократно)
|
||||
start.bat # запускает сервер (PWA + API)</pre>
|
||||
|
||||
<h3 id="helper-scripts" class="text-lg font-medium mb-2">6.4 Вспомогательные скрипты</h3>
|
||||
<h3 id="helper-scripts" class="text-lg font-medium mb-2">7.4 Вспомогательные скрипты</h3>
|
||||
<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">Скрипт</th><th class="text-left py-1 px-2">Назначение</th></tr></thead>
|
||||
<tbody>
|
||||
@@ -466,7 +475,7 @@ start.bat # запускает сервер (PWA + API)</pre>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 id="reader-autodetect" class="text-lg font-medium mb-2">6.5 Автоопределение ридера</h3>
|
||||
<h3 id="reader-autodetect" class="text-lg font-medium mb-2">7.5 Автоопределение ридера</h3>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>PC/SC (Linux)</strong> — <code class="font-mono text-sm">start.sh</code> передаёт <code class="font-mono text-sm">-p 0</code>, если запущен демон <code class="font-mono text-sm">pcscd</code></li>
|
||||
<li><strong>PC/SC (Windows)</strong> — <code class="font-mono text-sm">start.bat</code> всегда использует <code class="font-mono text-sm">-p 0</code> (PC/SC встроен в Windows)</li>
|
||||
@@ -475,7 +484,7 @@ start.bat # запускает сервер (PWA + API)</pre>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Если карта отсутствует, вкладка «Картридер» показывает «Карта не обнаружена. Вставьте карту и нажмите Подключить карту».</p>
|
||||
|
||||
<h3 id="manual-install" class="text-lg font-medium mb-2">6.6 Ручная установка</h3>
|
||||
<h3 id="manual-install" class="text-lg font-medium mb-2">7.6 Ручная установка</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3"># Создать и активировать venv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate # Linux/macOS
|
||||
@@ -494,7 +503,7 @@ pysim-otaman-server --http-port 8080</pre>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">7. Совместимость версий</h2>
|
||||
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">8. Совместимость версий</h2>
|
||||
<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 (OTAMan)</th><th class="text-left py-1 px-2">Сервер</th><th class="text-left py-1 px-2">Статус</th></tr></thead>
|
||||
<tbody>
|
||||
|
||||
+100
-91
@@ -44,13 +44,14 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Header</strong> — the app version, an <strong>INSTALL PWA</strong> button (shown when the browser offers installation, enabling offline use), links to the project on GitHub and to this help, an <strong>EN/RU</strong> language toggle, and a dark/light <strong>theme</strong> toggle.</li>
|
||||
<li>Language and theme choices are stored in <code class="font-mono text-sm">localStorage</code> and persist across reloads.</li>
|
||||
<li>The <strong>help</strong> link opens this documentation at the section matching the current view (e.g. the Profiler sub-tab opens §5.6).</li>
|
||||
<li>Top-level tabs: <strong>Remote APDU</strong> (<strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong>, <strong>C-APDU Parser</strong>, <strong>Response parser</strong>), <strong>SCP80</strong> (<strong>Secured Packet</strong>, <strong>Cards</strong>, <strong>RAM</strong>), <strong>Card reader</strong> (<strong>File manager</strong>, <strong>Custom files</strong>, <strong>pySim command line</strong>, <strong>Raw APDU</strong>), <strong>Profiler</strong>, and <strong>Phone simulator</strong>.</li>
|
||||
<li>The <strong>help</strong> link opens this documentation at the section matching the current view (e.g. the Profiler tab opens §5).</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="c-apdu" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">2. C-APDU tab</h2>
|
||||
<p class="mb-3">Builds command APDUs (C-APDUs). Six sub-tabs cover different card generations and command sets: <strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong>, and <strong>C-APDU Parser</strong>.</p>
|
||||
<h2 id="c-apdu" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">2. Remote APDU tab</h2>
|
||||
<p class="mb-3">Builds command APDUs (C-APDUs). Seven sub-tabs cover different card generations, command sets and decoding tools: <strong>SIM RFM</strong>, <strong>USIM RFM</strong>, <strong>Expanded Script</strong>, <strong>RAM/GP</strong>, <strong>HTTP OTA</strong>, <strong>C-APDU Parser</strong>, and <strong>Response parser</strong>.</p>
|
||||
|
||||
<h3 id="sim-rfm" class="text-lg font-medium mb-2">2.1 SIM RFM</h3>
|
||||
<p class="mb-2">CLA = <code class="font-mono text-sm">A0</code> (GSM 11.11 / TS 151 011, ISO 7816-4). Remote File Management for classic SIM cards.</p>
|
||||
@@ -138,7 +139,7 @@
|
||||
</ul>
|
||||
|
||||
<h4 id="expanded-response" class="font-medium mb-2 text-base">Response decoding (TS 102 226 §5.2.2)</h4>
|
||||
<p class="text-sm mb-2">Incoming Proof-of-Receipt responses are decoded by the server — expanded Remote Application response data (TS 102 226 §5.2.2) or the compact format. The Secured Packet view shows the outcome after <strong>Send to Card</strong> (see <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>): the PoR status (TAR, counter, raw PoR), with the last command’s status word and response data filled into the <strong>Response parser</strong> tab.</p>
|
||||
<p class="text-sm mb-2">Incoming Proof-of-Receipt responses are decoded by the server — expanded Remote Application response data (TS 102 226 §5.2.2) or the compact format. The Secured Packet view shows the outcome after <strong>Send to Card</strong> (see <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>): the PoR status (TAR, counter, raw PoR), with the last command’s status word and response data filled into the <strong>Response parser</strong> pill under Remote APDU.</p>
|
||||
|
||||
<h3 id="ram-gp" class="text-lg font-medium mb-2">2.4 RAM/GP</h3>
|
||||
<p class="mb-2">CLA = <code class="font-mono text-sm">80</code> (GlobalPlatform Card Specification v2.3.1). Remote Application Management commands for card content management. Built with the same chain builder as SIM/USIM: add rows, fill fields, and the chain preview updates automatically.</p>
|
||||
@@ -227,6 +228,17 @@
|
||||
<p class="text-sm mb-2"><strong>Pack into Secured packet</strong> sends the built payload to the SCP80 tab for SPI/counter filling — insert the TAR the SD listens on (typically the OTASD TAR) there.</p>
|
||||
|
||||
|
||||
|
||||
<h3 id="response-parser" class="text-lg font-medium mb-2">2.8 Response parser</h3>
|
||||
<p class="mb-3">Decodes a raw command response: pick the command that was sent, enter the SW (e.g. <code class="font-mono text-sm">9000</code>) and the response data hex, then press <strong>Decode</strong>. The fields are also auto-filled with the last command’s status word and response data after a successful “Send to Card” (see <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>).</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1">
|
||||
<li><strong>Command</strong> — SIM/USIM group (SELECT, STATUS, READ/UPDATE, PIN ops, CAT commands like TERMINAL PROFILE/ENVELOPE/FETCH/TERMINAL RESPONSE, MANAGE CHANNEL, …) or RAM/GP group (INSTALL, LOAD, DELETE, GET/STORE DATA, auth, SCP commands).</li>
|
||||
<li><strong>SW decode</strong> — status words resolved against generic, UICC (TS 102 221), and GlobalPlatform maps, with context auto-detected.</li>
|
||||
<li><strong>Privilege decode</strong> — GET DATA / INSTALL response payloads decode the privilege bytes into human-readable flags.</li>
|
||||
<li><strong>Response data</strong> — raw hex rendered and interpreted per command (e.g. SELECT FCP templates).</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="scp80" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">3. SCP80 tab</h2>
|
||||
<p class="mb-3">The <strong>SCP80</strong> top-level tab groups the SCP80-related views. It is switched by three pills: <strong>Secured Packet</strong>, <strong>Cards</strong>, and <strong>RAM</strong>. Assembles SCP80 secured packets per ETSI TS 102 225.</p>
|
||||
@@ -301,21 +313,10 @@
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="response-parser" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">4. Response parser tab</h2>
|
||||
<p class="mb-3">Decodes a raw command response: pick the command that was sent, enter the SW (e.g. <code class="font-mono text-sm">9000</code>) and the response data hex, then press <strong>Decode</strong>. The fields are also auto-filled with the last command’s status word and response data after a successful “Send to Card” (see <a href="#secured-packet" class="text-blue-600 dark:text-blue-400 hover:underline">§3.1</a>).</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1">
|
||||
<li><strong>Command</strong> — SIM/USIM group (SELECT, STATUS, READ/UPDATE, PIN ops, CAT commands like TERMINAL PROFILE/ENVELOPE/FETCH/TERMINAL RESPONSE, MANAGE CHANNEL, …) or RAM/GP group (INSTALL, LOAD, DELETE, GET/STORE DATA, auth, SCP commands).</li>
|
||||
<li><strong>SW decode</strong> — status words resolved against generic, UICC (TS 102 221), and GlobalPlatform maps, with context auto-detected.</li>
|
||||
<li><strong>Privilege decode</strong> — GET DATA / INSTALL response payloads decode the privilege bytes into human-readable flags.</li>
|
||||
<li><strong>Response data</strong> — raw hex rendered and interpreted per command (e.g. SELECT FCP templates).</li>
|
||||
</ul>
|
||||
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">4. Card reader (pySim) tab</h2>
|
||||
<p class="mb-3">Connects to a local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> for live card operations: enter the server URL (default <code class="font-mono text-sm">http://127.0.0.1:8080</code>) and press <strong>Connect</strong>. The status area shows the reader/card state, and <strong>Equip card</strong> (re)initializes the card after insertion. Sub-tabs: <strong>File manager</strong>, <strong>Custom files</strong>, <strong>pySim command line</strong>, and <strong>Raw APDU</strong>. The <strong>Profiler</strong> and <strong>Phone simulator</strong> are separate top-level tabs.</p>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">5. Card reader (pySim) tab</h2>
|
||||
<p class="mb-3">Connects to a local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> for live card operations: enter the server URL (default <code class="font-mono text-sm">http://127.0.0.1:8080</code>) and press <strong>Connect</strong>. The status area shows the reader/card state, and <strong>Equip card</strong> (re)initializes the card after insertion. Sub-tabs: <strong>File manager</strong>, <strong>Custom files</strong>, <strong>Profiler</strong>, <strong>pySim command line</strong>, <strong>Raw APDU</strong>, and <strong>Proactive UICC</strong>.</p>
|
||||
|
||||
<h3 id="file-manager" class="text-lg font-medium mb-2">5.1 File manager</h3>
|
||||
<h3 id="file-manager" class="text-lg font-medium mb-2">4.1 File manager</h3>
|
||||
<p class="text-sm mb-2">The file system tree is displayed on the left; selecting a file opens its detail pane on the right.</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Read</strong> — reads the selected file (auto-detects transparent vs record files)</li>
|
||||
@@ -323,48 +324,55 @@
|
||||
<li><strong>Raw / Decoded</strong> — toggle between hex dump and pySim-decoded JSON</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="custom-files" class="text-lg font-medium mb-2">5.2 Custom files</h3>
|
||||
<h3 id="custom-files" class="text-lg font-medium mb-2">4.2 Custom files</h3>
|
||||
<p class="text-sm mb-3">Add files that pySim’s model does not cover: enter the full path (e.g. <code class="font-mono text-sm">3F00/7F20/6F46</code>) and an alias (e.g. <code class="font-mono text-sm">EF.SPN</code>), then press <strong>Add</strong>; added files appear in the File manager tree. The list persists in <code class="font-mono text-sm">localStorage</code> and can be shared with <strong>Export as JSON</strong> / <strong>Export to file</strong> and restored with <strong>Import from file</strong> / <strong>Paste & import</strong> / <strong>Import JSON from clipboard</strong>.</p>
|
||||
|
||||
<h3 id="pysim-cmdline" class="text-lg font-medium mb-2">5.3 pySim command line</h3>
|
||||
<h3 id="pysim-cmdline" class="text-lg font-medium mb-2">4.3 pySim command line</h3>
|
||||
<p class="text-sm mb-3">Execute any pySim-shell command with usage hints (300 ms) and autocomplete.</p>
|
||||
|
||||
<h3 id="raw-apdu" class="text-lg font-medium mb-2">5.4 Raw APDU</h3>
|
||||
<h3 id="raw-apdu" class="text-lg font-medium mb-2">4.4 Raw APDU</h3>
|
||||
<p class="text-sm mb-3">Send an arbitrary APDU and view the raw response.</p>
|
||||
|
||||
<h3 id="proactive-uicc" class="text-lg font-medium mb-2">5.5 Proactive UICC</h3>
|
||||
<p class="text-sm mb-3">Interacts with the Card Application Toolkit session: the STK menu, subscribed events, the proactive command log, the PROVIDE LOCAL INFORMATION data dictionary, and STATUS polling.</p>
|
||||
<h3 id="usage-scenarios" class="text-lg font-medium mb-2">4.5 Usage scenarios</h3>
|
||||
|
||||
<h4 id="stk-menu" class="font-medium mb-1">5.5.1 STK menu</h4>
|
||||
<p class="text-sm mb-3">When the card has issued a SET UP MENU command, a “STK menu” block appears at the top of this view with an emerald <strong>STK: <title></strong> button that opens the menu overlay (same as the card’s STK menu browser). If the card has not set up a menu, the block shows “No menu set by the card” instead. The menu state is refreshed each time the view is opened. User-interactive proactive commands always get a TERMINAL RESPONSE: the overlay pauses for your choice, and if you neither answer nor press <strong>Timeout</strong>, the server answers with a timeout result after the <code class="font-mono text-sm">--menu-timeout</code> seconds (default 60, <code class="font-mono text-sm">0</code> disables).</p>
|
||||
<h4 id="scenario-a" class="font-medium mb-1">Scenario A — Working with files not in pySim’s model (Custom files)</h4>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Obtain the FID of the target file (vendor documentation or ATR/file-system analysis; such files are often not in public specs).</li>
|
||||
<li>Open the <strong>Card reader</strong> tab → <strong>Custom files</strong> sub-tab.</li>
|
||||
<li>Enter the full path (e.g. <code class="font-mono text-sm">3F00/7F20/6F46</code>) and an alias (e.g. <code class="font-mono text-sm">EF.SPN</code>).</li>
|
||||
<li>Click <strong>Add</strong> — the file appears in the tree in italics (unverified).</li>
|
||||
<li>Click the file to verify existence; on success (<code class="font-mono text-sm">9000</code>) it behaves like a normal file.</li>
|
||||
<li>Read, edit and save hex data; toggle Raw/Decoded views.</li>
|
||||
<li>Export the custom-file list as JSON to share with other machines.</li>
|
||||
</ol>
|
||||
|
||||
<h4 id="subscribed-events" class="font-medium mb-1">5.5.2 Subscribed events (SET UP EVENT LIST)</h4>
|
||||
<p class="text-sm mb-2">The events the card monitors. Each event has a <strong>Send</strong> button that opens a form specific to the event type:</p>
|
||||
<h4 id="scenario-b" class="font-medium mb-1">Scenario B — Simulating a real network environment for SIM testing</h4>
|
||||
<p class="text-sm mb-1"><strong>B.1 Answer PROVIDE LOCAL INFORMATION (PLI)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Open <strong>Phone simulator</strong> → <strong>PROVIDE LOCAL INFORMATION response data</strong>.</li>
|
||||
<li>Use the decode/encode forms to set IMEI (<code class="font-mono text-sm">01</code>), Location Info (<code class="font-mono text-sm">00</code>), Access Technology (<code class="font-mono text-sm">06</code>), etc.</li>
|
||||
<li>Click <strong>Save</strong> — values persist server-side.</li>
|
||||
<li>Enable <strong>Polling</strong> (interval 30 s) so the card issues PLI periodically.</li>
|
||||
<li>The server injects the dictionary values into each TERMINAL RESPONSE.</li>
|
||||
<li>Verify in the proactive log: the PLI entry shows the decoded response.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.2 Simulate network actions via ENVELOPE (event download)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Check the <strong>subscribed events</strong> list (from SET UP EVENT LIST).</li>
|
||||
<li>Click <strong>Send</strong> on an event (e.g. Location Status) and fill the form; an <code class="font-mono text-sm">ENVELOPE(Event Download)</code> is sent.</li>
|
||||
<li>For <strong>Network Rejection</strong>, select registration type → location fields → access technology → rejection cause.</li>
|
||||
<li>The card may respond with a proactive command, which the chain handler logs and answers automatically.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.3 Verify the simulated environment</strong></p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>No-data events</strong> (User Activity, Idle Screen, Data Available, …) — one-click notification</li>
|
||||
<li><strong>Location Status</strong> — dropdown: Normal / Limited / No service (tag <code class="font-mono text-sm">9B</code>)</li>
|
||||
<li><strong>Access Technology Change</strong> — 13 RAT types (tag <code class="font-mono text-sm">BF</code>)</li>
|
||||
<li><strong>Network Rejection</strong> — full adaptive form: registration type (LU / GPRS / EPS / 5GS), location fields (MCC, MNC, LAC, RAC, TAC), access technology, and a 53-cause unified rejection cause dropdown covering EMM, GMM, 5GMM and LU causes</li>
|
||||
<li>The proactive command log shows the full round-trip (command + TERMINAL RESPONSE bytes).</li>
|
||||
<li>The STATUS button / auto-polling keep the CAT session alive (drain loop).</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Sending an event uses <code class="font-mono text-sm">ENVELOPE(Event Download)</code> per TS 102 223 / TS 131 111.</p>
|
||||
|
||||
<h4 id="proactive-log" class="font-medium mb-1">5.5.3 Proactive command log</h4>
|
||||
<p class="text-sm mb-2">Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Commands with response data show a <code class="font-mono text-sm">Response:</code> line with the TERMINAL RESPONSE bytes (boilerplate TLVs stripped); PROVIDE LOCAL INFORMATION responses are decoded using the PLI data dictionary decoders.</p>
|
||||
|
||||
<h4 id="pli-dict" class="font-medium mb-1">5.5.4 PROVIDE LOCAL INFORMATION data dictionary</h4>
|
||||
<p class="text-sm mb-2">Editable hex values for all 22 PLI qualifiers (TS 102 223 §8.6 + TS 131 111). Ten qualifiers have inline decode/encode forms:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>00</strong> Location Info (MCC, MNC, LAC/TAC, Cell ID)</li>
|
||||
<li><strong>01</strong> IMEI · <strong>03</strong> Date/Time/TZ · <strong>04</strong> Language · <strong>05</strong> Timing Advance</li>
|
||||
<li><strong>06</strong> Access Technology · <strong>08</strong> IMEISV · <strong>09</strong> Search Mode</li>
|
||||
<li><strong>0A</strong> Battery · <strong>0E</strong> Multiple Access Technologies</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Values persist server-side until restart. When the card issues PLI, the server injects the dictionary values into the TERMINAL RESPONSE.</p>
|
||||
|
||||
<h4 id="status-polling" class="font-medium mb-1">5.5.5 STATUS polling</h4>
|
||||
<p class="text-sm mb-3">A <strong>Send STATUS</strong> button issues a manual STATUS (F2). A <strong>Polling</strong> toggle enables background polling: after a configurable idle interval (server CLI <code class="font-mono text-sm">--poll-interval</code>, 1–255 s, default 30 s, <code class="font-mono text-sm">0</code> disables polling) the server sends STATUS and handles any pending proactive command. Polling stops and card state resets if the card is removed.</p>
|
||||
|
||||
<h3 id="profiler" class="text-lg font-medium mb-2">5.6 Profiler</h3>
|
||||
<section class="mb-10">
|
||||
<h2 id="profiler" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">5. Profiler</h2>
|
||||
<p class="text-sm mb-2">Verifies that a card matches a named <strong>profile</strong> — an ordered set of rules describing the expected file system and (optionally) file contents. Profiles are stored in <code class="font-mono text-sm">localStorage</code>.</p>
|
||||
<h4 class="font-medium mb-1">Profile list</h4>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
@@ -395,48 +403,49 @@
|
||||
<li><strong>Compare snapshots</strong> compares two snapshots offline, exactly like a profile check: pick the <em>master</em> snapshot and the <em>snapshot to check</em>, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="usage-scenarios" class="text-lg font-medium mb-2">5.7 Usage scenarios</h3>
|
||||
|
||||
<h4 id="scenario-a" class="font-medium mb-1">Scenario A — Working with files not in pySim’s model (Custom files)</h4>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Obtain the FID of the target file (vendor documentation or ATR/file-system analysis; such files are often not in public specs).</li>
|
||||
<li>Open the <strong>Card reader</strong> tab → <strong>Custom files</strong> sub-tab.</li>
|
||||
<li>Enter the full path (e.g. <code class="font-mono text-sm">3F00/7F20/6F46</code>) and an alias (e.g. <code class="font-mono text-sm">EF.SPN</code>).</li>
|
||||
<li>Click <strong>Add</strong> — the file appears in the tree in italics (unverified).</li>
|
||||
<li>Click the file to verify existence; on success (<code class="font-mono text-sm">9000</code>) it behaves like a normal file.</li>
|
||||
<li>Read, edit and save hex data; toggle Raw/Decoded views.</li>
|
||||
<li>Export the custom-file list as JSON to share with other machines.</li>
|
||||
</ol>
|
||||
|
||||
<h4 id="scenario-b" class="font-medium mb-1">Scenario B — Simulating a real network environment for SIM testing</h4>
|
||||
<p class="text-sm mb-1"><strong>B.1 Answer PROVIDE LOCAL INFORMATION (PLI)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Open <strong>Proactive UICC</strong> → <strong>PROVIDE LOCAL INFORMATION response data</strong>.</li>
|
||||
<li>Use the decode/encode forms to set IMEI (<code class="font-mono text-sm">01</code>), Location Info (<code class="font-mono text-sm">00</code>), Access Technology (<code class="font-mono text-sm">06</code>), etc.</li>
|
||||
<li>Click <strong>Save</strong> — values persist server-side.</li>
|
||||
<li>Enable <strong>Polling</strong> (interval 30 s) so the card issues PLI periodically.</li>
|
||||
<li>The server injects the dictionary values into each TERMINAL RESPONSE.</li>
|
||||
<li>Verify in the proactive log: the PLI entry shows the decoded response.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.2 Simulate network actions via ENVELOPE (event download)</strong></p>
|
||||
<ol class="list-decimal list-inside text-sm space-y-1 mb-3">
|
||||
<li>Check the <strong>subscribed events</strong> list (from SET UP EVENT LIST).</li>
|
||||
<li>Click <strong>Send</strong> on an event (e.g. Location Status) and fill the form; an <code class="font-mono text-sm">ENVELOPE(Event Download)</code> is sent.</li>
|
||||
<li>For <strong>Network Rejection</strong>, select registration type → location fields → access technology → rejection cause.</li>
|
||||
<li>The card may respond with a proactive command, which the chain handler logs and answers automatically.</li>
|
||||
</ol>
|
||||
<p class="text-sm mb-1"><strong>B.3 Verify the simulated environment</strong></p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li>The proactive command log shows the full round-trip (command + TERMINAL RESPONSE bytes).</li>
|
||||
<li>The STATUS button / auto-polling keep the CAT session alive (drain loop).</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Server installation</h2>
|
||||
<p class="mb-3">Live card operations (Card reader tab, Proactive UICC, OTA delivery) require the local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — a small HTTP server bundled with OTAMan that wraps pySim, talks to the reader over PC/SC or serial, and also serves the PWA itself (open <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
|
||||
<h2 id="proactive-uicc" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Phone simulator</h2>
|
||||
<p class="text-sm mb-3">Interacts with the Card Application Toolkit session: the STK menu, subscribed events, the proactive command log, the PROVIDE LOCAL INFORMATION data dictionary, and STATUS polling.</p>
|
||||
|
||||
<h3 id="prerequisites" class="text-lg font-medium mb-2">6.1 Prerequisites</h3>
|
||||
<h3 id="stk-menu" class="text-lg font-medium mb-2">STK menu</h3>
|
||||
<p class="text-sm mb-3">When the card has issued a SET UP MENU command, a “STK menu” block appears at the top of this view with an emerald <strong>STK: <title></strong> button that opens the menu overlay (same as the card’s STK menu browser). If the card has not set up a menu, the block shows “No menu set by the card” instead. The menu state is refreshed each time the view is opened. User-interactive proactive commands always get a TERMINAL RESPONSE: the overlay pauses for your choice, and if you neither answer nor press <strong>Timeout</strong>, the server answers with a timeout result after the <code class="font-mono text-sm">--menu-timeout</code> seconds (default 60, <code class="font-mono text-sm">0</code> disables).</p>
|
||||
|
||||
<h3 id="subscribed-events" class="text-lg font-medium mb-2">6.2 Subscribed events (SET UP EVENT LIST)</h3>
|
||||
<p class="text-sm mb-2">The events the card monitors. Each event has a <strong>Send</strong> button that opens a form specific to the event type:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>No-data events</strong> (User Activity, Idle Screen, Data Available, …) — one-click notification</li>
|
||||
<li><strong>Location Status</strong> — dropdown: Normal / Limited / No service (tag <code class="font-mono text-sm">9B</code>)</li>
|
||||
<li><strong>Access Technology Change</strong> — 13 RAT types (tag <code class="font-mono text-sm">BF</code>)</li>
|
||||
<li><strong>Network Rejection</strong> — full adaptive form: registration type (LU / GPRS / EPS / 5GS), location fields (MCC, MNC, LAC, RAC, TAC), access technology, and a 53-cause unified rejection cause dropdown covering EMM, GMM, 5GMM and LU causes</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Sending an event uses <code class="font-mono text-sm">ENVELOPE(Event Download)</code> per TS 102 223 / TS 131 111.</p>
|
||||
|
||||
<h3 id="proactive-log" class="text-lg font-medium mb-2">6.3 Proactive command log</h3>
|
||||
<p class="text-sm mb-2">Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Commands with response data show a <code class="font-mono text-sm">Response:</code> line with the TERMINAL RESPONSE bytes (boilerplate TLVs stripped); PROVIDE LOCAL INFORMATION responses are decoded using the PLI data dictionary decoders.</p>
|
||||
|
||||
<h3 id="pli-dict" class="text-lg font-medium mb-2">6.4 PROVIDE LOCAL INFORMATION data dictionary</h3>
|
||||
<p class="text-sm mb-2">Editable hex values for all 22 PLI qualifiers (TS 102 223 §8.6 + TS 131 111). Ten qualifiers have inline decode/encode forms:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>00</strong> Location Info (MCC, MNC, LAC/TAC, Cell ID)</li>
|
||||
<li><strong>01</strong> IMEI · <strong>03</strong> Date/Time/TZ · <strong>04</strong> Language · <strong>05</strong> Timing Advance</li>
|
||||
<li><strong>06</strong> Access Technology · <strong>08</strong> IMEISV · <strong>09</strong> Search Mode</li>
|
||||
<li><strong>0A</strong> Battery · <strong>0E</strong> Multiple Access Technologies</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Values persist server-side until restart. When the card issues PLI, the server injects the dictionary values into the TERMINAL RESPONSE.</p>
|
||||
|
||||
<h3 id="status-polling" class="text-lg font-medium mb-2">6.5 STATUS polling</h3>
|
||||
<p class="text-sm mb-3">A <strong>Send STATUS</strong> button issues a manual STATUS (F2). A <strong>Polling</strong> toggle enables background polling: after a configurable idle interval (server CLI <code class="font-mono text-sm">--poll-interval</code>, 1–255 s, default 30 s, <code class="font-mono text-sm">0</code> disables polling) the server sends STATUS and handles any pending proactive command. Polling stops and card state resets if the card is removed.</p>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">7. Server installation</h2>
|
||||
<p class="mb-3">Live card operations (Card reader tab, Phone simulator, OTA delivery) require the local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — a small HTTP server bundled with OTAMan that wraps pySim, talks to the reader over PC/SC or serial, and also serves the PWA itself (open <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
|
||||
|
||||
<h3 id="prerequisites" class="text-lg font-medium mb-2">7.1 Prerequisites</h3>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Python 3.8+</strong> with <code class="font-mono text-sm">pip</code></li>
|
||||
<li><strong>Git</strong></li>
|
||||
@@ -444,20 +453,20 @@
|
||||
<li><strong>Windows only</strong> — use <strong>Python 3.10–3.13</strong> (3.13 recommended): <code class="font-mono text-sm">pyscard</code> (the PC/SC driver wrapper) ships precompiled wheels for these versions. On Python 3.9 / 3.14 pip builds <code class="font-mono text-sm">pyscard</code> from source, which requires Microsoft C++ Build Tools (“Desktop development with C++”). The SMPP bridge (<code class="font-mono text-sm">smpp.twisted3</code>) is intentionally not installed on Windows, so no C++ Build Tools are needed for Python 3.10–3.13.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">6.2 Quick start — Linux / macOS</h3>
|
||||
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">7.2 Quick start — Linux / macOS</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
|
||||
cd otaman
|
||||
chmod +x setup.sh start.sh
|
||||
./setup.sh # creates .venv, installs pysim + server (run once)
|
||||
./start.sh # starts the server (serves PWA + API, auto-detects reader)</pre>
|
||||
|
||||
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">6.3 Quick start — Windows</h3>
|
||||
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">7.3 Quick start — Windows</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
|
||||
cd otaman
|
||||
setup.bat # creates .venv, installs pysim + server (run once)
|
||||
start.bat # starts the server (serves PWA + API)</pre>
|
||||
|
||||
<h3 id="helper-scripts" class="text-lg font-medium mb-2">6.4 Helper scripts</h3>
|
||||
<h3 id="helper-scripts" class="text-lg font-medium mb-2">7.4 Helper scripts</h3>
|
||||
<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">Script</th><th class="text-left py-1 px-2">Purpose</th></tr></thead>
|
||||
<tbody>
|
||||
@@ -466,7 +475,7 @@ start.bat # starts the server (serves PWA + API)</pre>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 id="reader-autodetect" class="text-lg font-medium mb-2">6.5 Reader auto-detection</h3>
|
||||
<h3 id="reader-autodetect" class="text-lg font-medium mb-2">7.5 Reader auto-detection</h3>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>PC/SC (Linux)</strong> — <code class="font-mono text-sm">start.sh</code> passes <code class="font-mono text-sm">-p 0</code> when the <code class="font-mono text-sm">pcscd</code> daemon is running</li>
|
||||
<li><strong>PC/SC (Windows)</strong> — <code class="font-mono text-sm">start.bat</code> always uses <code class="font-mono text-sm">-p 0</code> (PC/SC is built into Windows)</li>
|
||||
@@ -475,7 +484,7 @@ start.bat # starts the server (serves PWA + API)</pre>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">If no card is present, the Card reader tab shows “No card detected”. Insert the card and click <strong>Equip card</strong> to initialize it.</p>
|
||||
|
||||
<h3 id="manual-install" class="text-lg font-medium mb-2">6.6 Manual installation</h3>
|
||||
<h3 id="manual-install" class="text-lg font-medium mb-2">7.6 Manual installation</h3>
|
||||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3"># Create and activate a venv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate # Linux/macOS
|
||||
@@ -494,7 +503,7 @@ pysim-otaman-server --http-port 8080</pre>
|
||||
|
||||
|
||||
<section class="mb-10">
|
||||
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">7. Version compatibility</h2>
|
||||
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">8. Version compatibility</h2>
|
||||
<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 (OTAMan)</th><th class="text-left py-1 px-2">Server</th><th class="text-left py-1 px-2">Status</th></tr></thead>
|
||||
<tbody>
|
||||
|
||||
+116
-111
@@ -28,23 +28,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-1 mb-4 border-b border-gray-300 dark:border-slate-600">
|
||||
<button class="tab-btn active px-4 py-1.5 text-sm rounded-t bg-blue-600 dark:bg-blue-500 text-white dark:text-white" data-tab="c-apdu">C-APDU</button>
|
||||
<div class="flex flex-wrap gap-1 mb-4 border-b border-gray-300 dark:border-slate-600">
|
||||
<button class="tab-btn active px-4 py-1.5 text-sm rounded-t bg-blue-600 dark:bg-blue-500 text-white dark:text-white" data-tab="c-apdu">Remote APDU</button>
|
||||
<button class="tab-btn px-4 py-1.5 text-sm rounded-t bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300" data-tab="scp80">SCP80</button>
|
||||
<button class="tab-btn px-4 py-1.5 text-sm rounded-t bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300" data-tab="response" data-l10n="Response parser">Response parser</button>
|
||||
<button class="tab-btn px-4 py-1.5 text-sm rounded-t bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300" data-tab="pysim" data-l10n="Card reader">Card reader</button>
|
||||
<button class="tab-btn px-4 py-1.5 text-sm rounded-t bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300" data-tab="profiler" data-l10n="Profiler">Profiler</button>
|
||||
<button class="tab-btn px-4 py-1.5 text-sm rounded-t bg-gray-200 dark:bg-slate-700 hover:bg-gray-300 dark:hover:bg-slate-600 text-gray-700 dark:text-slate-300" data-tab="phone" data-l10n="Phone simulator">Phone simulator</button>
|
||||
</div>
|
||||
|
||||
<div id="tab-c-apdu" class="tab-content">
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-center gap-2 mb-3">
|
||||
<div class="flex flex-wrap justify-center gap-2 mb-3">
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-blue-600 text-white" data-sub="sim" onclick="cApduSwitchSubtab('sim')">SIM RFM</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="usim" onclick="cApduSwitchSubtab('usim')">USIM RFM</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="ber" onclick="cApduSwitchSubtab('ber')">Expanded Script</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="ram" onclick="cApduSwitchSubtab('ram')">RAM/GP</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="httpota" onclick="cApduSwitchSubtab('httpota')">HTTP OTA</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="parse" onclick="cApduSwitchSubtab('parse')" data-l10n="C-APDU Parser">C-APDU Parser</button>
|
||||
<button class="c-apdu-subtab px-4 py-1.5 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-sub="response" onclick="cApduSwitchSubtab('response')" data-l10n="Response parser">Response parser</button>
|
||||
</div>
|
||||
<div id="c-apdu-sub-sim">
|
||||
<div id="chain-sim-rows"></div>
|
||||
@@ -256,6 +258,76 @@
|
||||
<textarea id="hota-preview" rows="5" readonly oninput="hotaPreviewEdited()" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800 mb-3" placeholder="RAM-over-HTTP payload will appear here..."></textarea>
|
||||
<button onclick="packToSp('hota-preview')" id="hota-pack-btn" disabled class="mb-3 px-5 py-2.5 bg-emerald-600 text-white text-sm font-medium rounded hover:bg-emerald-700 disabled:opacity-40 disabled:cursor-not-allowed" data-l10n="Pack into Secured packet">Pack into Secured packet</button>
|
||||
</div>
|
||||
|
||||
<div id="c-apdu-sub-response" class="hidden">
|
||||
<div class="mb-3">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300" data-l10n="Command">Command</label>
|
||||
<select id="resp-cmd" onchange="updateRespCmd()" class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800">
|
||||
<option value="" data-l10n="— Select command —">— Select command —</option>
|
||||
<optgroup label="SIM / USIM">
|
||||
<option value="sim-select">SELECT</option>
|
||||
<option value="sim-status">STATUS</option>
|
||||
<option value="sim-read-binary">READ BINARY</option>
|
||||
<option value="sim-update-binary">UPDATE BINARY</option>
|
||||
<option value="sim-read-record">READ RECORD</option>
|
||||
<option value="sim-update-record">UPDATE RECORD</option>
|
||||
<option value="sim-verify">VERIFY PIN</option>
|
||||
<option value="sim-change">CHANGE PIN</option>
|
||||
<option value="sim-disable">DISABLE PIN</option>
|
||||
<option value="sim-enable">ENABLE PIN</option>
|
||||
<option value="sim-unblock">UNBLOCK PIN</option>
|
||||
<option value="sim-deactivate">DEACTIVATE FILE</option>
|
||||
<option value="sim-activate">ACTIVATE FILE</option>
|
||||
<option value="sim-challenge">GET CHALLENGE</option>
|
||||
<option value="sim-auth">AUTHENTICATE</option>
|
||||
<option value="sim-profile">TERMINAL PROFILE</option>
|
||||
<option value="sim-envelope">ENVELOPE</option>
|
||||
<option value="sim-fetch">FETCH</option>
|
||||
<option value="sim-terminal-rsp">TERMINAL RESPONSE</option>
|
||||
<option value="sim-manage-channel">MANAGE CHANNEL</option>
|
||||
<option value="sim-msc">MANAGE SECURE CHANNEL</option>
|
||||
<option value="sim-retrieve">RETRIEVE DATA</option>
|
||||
<option value="sim-set-data">SET DATA</option>
|
||||
<option value="sim-suspend">SUSPEND UICC</option>
|
||||
</optgroup>
|
||||
<optgroup label="RAM (GlobalPlatform)">
|
||||
<option value="ram-install-load">INSTALL [for load]</option>
|
||||
<option value="ram-install-install">INSTALL [for install]</option>
|
||||
<option value="ram-install-make-sel">INSTALL [for make selectable]</option>
|
||||
<option value="ram-install-reg-update">INSTALL [for registry update]</option>
|
||||
<option value="ram-install-extradition">INSTALL [for extradition]</option>
|
||||
<option value="ram-load">LOAD</option>
|
||||
<option value="ram-delete">DELETE</option>
|
||||
<option value="ram-get-status">GET STATUS</option>
|
||||
<option value="ram-get-data">GET DATA</option>
|
||||
<option value="ram-store-data">STORE DATA</option>
|
||||
<option value="ram-set-status">SET STATUS</option>
|
||||
<option value="ram-ext-auth">EXTERNAL AUTHENTICATE</option>
|
||||
<option value="ram-int-auth">INTERNAL AUTHENTICATE</option>
|
||||
<option value="ram-select">SELECT</option>
|
||||
<option value="ram-init-update">INITIALIZE UPDATE</option>
|
||||
<option value="ram-put-key">PUT KEY</option>
|
||||
<option value="ram-manage-channel">MANAGE CHANNEL</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-3 mb-3">
|
||||
<div class="w-24">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300">SW</label>
|
||||
<input id="resp-sw" class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800" maxlength="4" placeholder="9000">
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300">Context</label>
|
||||
<input id="resp-ctx" readonly class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800" value="generic">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300" data-l10n="Response data (hex)">Response data (hex)</label>
|
||||
<textarea id="resp-data" rows="3" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800" placeholder="E3 1A 4F 08 A0 00 00 00 03 00 00 00 9F 70 01 07 C5 03 80 00 00"></textarea>
|
||||
</div>
|
||||
<button onclick="decodeResponse()" class="mb-3 px-5 py-2.5 bg-blue-600 text-white text-sm font-medium rounded hover:bg-blue-700" data-l10n="Decode">Decode</button>
|
||||
<div id="resp-output" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800 whitespace-pre-wrap" style="min-height:100px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/4">
|
||||
<div class="border border-gray-300 dark:border-slate-600 rounded p-3 bg-gray-50 dark:bg-slate-800">
|
||||
@@ -661,75 +733,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-response" class="tab-content hidden">
|
||||
<div class="mb-3">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300" data-l10n="Command">Command</label>
|
||||
<select id="resp-cmd" onchange="updateRespCmd()" class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800">
|
||||
<option value="" data-l10n="— Select command —">— Select command —</option>
|
||||
<optgroup label="SIM / USIM">
|
||||
<option value="sim-select">SELECT</option>
|
||||
<option value="sim-status">STATUS</option>
|
||||
<option value="sim-read-binary">READ BINARY</option>
|
||||
<option value="sim-update-binary">UPDATE BINARY</option>
|
||||
<option value="sim-read-record">READ RECORD</option>
|
||||
<option value="sim-update-record">UPDATE RECORD</option>
|
||||
<option value="sim-verify">VERIFY PIN</option>
|
||||
<option value="sim-change">CHANGE PIN</option>
|
||||
<option value="sim-disable">DISABLE PIN</option>
|
||||
<option value="sim-enable">ENABLE PIN</option>
|
||||
<option value="sim-unblock">UNBLOCK PIN</option>
|
||||
<option value="sim-deactivate">DEACTIVATE FILE</option>
|
||||
<option value="sim-activate">ACTIVATE FILE</option>
|
||||
<option value="sim-challenge">GET CHALLENGE</option>
|
||||
<option value="sim-auth">AUTHENTICATE</option>
|
||||
<option value="sim-profile">TERMINAL PROFILE</option>
|
||||
<option value="sim-envelope">ENVELOPE</option>
|
||||
<option value="sim-fetch">FETCH</option>
|
||||
<option value="sim-terminal-rsp">TERMINAL RESPONSE</option>
|
||||
<option value="sim-manage-channel">MANAGE CHANNEL</option>
|
||||
<option value="sim-msc">MANAGE SECURE CHANNEL</option>
|
||||
<option value="sim-retrieve">RETRIEVE DATA</option>
|
||||
<option value="sim-set-data">SET DATA</option>
|
||||
<option value="sim-suspend">SUSPEND UICC</option>
|
||||
</optgroup>
|
||||
<optgroup label="RAM (GlobalPlatform)">
|
||||
<option value="ram-install-load">INSTALL [for load]</option>
|
||||
<option value="ram-install-install">INSTALL [for install]</option>
|
||||
<option value="ram-install-make-sel">INSTALL [for make selectable]</option>
|
||||
<option value="ram-install-reg-update">INSTALL [for registry update]</option>
|
||||
<option value="ram-install-extradition">INSTALL [for extradition]</option>
|
||||
<option value="ram-load">LOAD</option>
|
||||
<option value="ram-delete">DELETE</option>
|
||||
<option value="ram-get-status">GET STATUS</option>
|
||||
<option value="ram-get-data">GET DATA</option>
|
||||
<option value="ram-store-data">STORE DATA</option>
|
||||
<option value="ram-set-status">SET STATUS</option>
|
||||
<option value="ram-ext-auth">EXTERNAL AUTHENTICATE</option>
|
||||
<option value="ram-int-auth">INTERNAL AUTHENTICATE</option>
|
||||
<option value="ram-select">SELECT</option>
|
||||
<option value="ram-init-update">INITIALIZE UPDATE</option>
|
||||
<option value="ram-put-key">PUT KEY</option>
|
||||
<option value="ram-manage-channel">MANAGE CHANNEL</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-3 mb-3">
|
||||
<div class="w-24">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300">SW</label>
|
||||
<input id="resp-sw" class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800" maxlength="4" placeholder="9000">
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300">Context</label>
|
||||
<input id="resp-ctx" readonly class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800" value="generic">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="block mb-1 text-sm font-medium text-gray-700 dark:text-slate-300" data-l10n="Response data (hex)">Response data (hex)</label>
|
||||
<textarea id="resp-data" rows="3" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 dark:bg-slate-800" placeholder="E3 1A 4F 08 A0 00 00 00 03 00 00 00 9F 70 01 07 C5 03 80 00 00"></textarea>
|
||||
</div>
|
||||
<button onclick="decodeResponse()" class="mb-3 px-5 py-2.5 bg-blue-600 text-white text-sm font-medium rounded hover:bg-blue-700" data-l10n="Decode">Decode</button>
|
||||
<div id="resp-output" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800 whitespace-pre-wrap" style="min-height:100px"></div>
|
||||
</div>
|
||||
|
||||
<div id="tab-pysim" class="tab-content hidden">
|
||||
<div id="pysim-connect-row" class="mb-3">
|
||||
@@ -762,10 +765,8 @@
|
||||
<div class="flex gap-1 mb-2">
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-blue-600 text-white" data-pysim-sub="files" data-l10n="File manager">File manager</button>
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="custom" data-l10n="Custom files">Custom files</button>
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="profiler" data-l10n="Profiler">Profiler</button>
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="cmd" data-l10n="pySim command line">pySim command line</button>
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="apdu" data-l10n="Raw APDU">Raw APDU</button>
|
||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="proactive">Proactive UICC</button>
|
||||
</div>
|
||||
|
||||
<div id="pysim-sub-files">
|
||||
@@ -811,7 +812,33 @@
|
||||
<textarea id="pysim-cf-io" class="hidden mt-2 w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1.5 bg-gray-100 dark:bg-slate-800" rows="6"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="pysim-sub-profiler" class="hidden">
|
||||
|
||||
<div id="pysim-sub-cmd" class="hidden">
|
||||
<div class="flex gap-2 items-center mb-2">
|
||||
<div class="relative flex-1">
|
||||
<input id="pysim-cmd" class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 dark:bg-slate-800" placeholder="select MF" autocomplete="off" onkeydown="pysimCmdKeydown(event)" oninput="pysimCmdInput()">
|
||||
<div id="pysim-cmd-suggest" class="hidden absolute bottom-full left-0 right-0 mb-1 max-h-32 overflow-y-auto border border-gray-300 dark:border-slate-600 rounded bg-white dark:bg-slate-800 text-xs font-mono shadow-lg z-10"></div>
|
||||
</div>
|
||||
<button onclick="pysimExec()" class="px-4 py-2.5 bg-emerald-600 text-white text-sm font-medium rounded hover:bg-emerald-700" data-l10n="Execute">Execute</button>
|
||||
</div>
|
||||
<div id="pysim-cmd-hint" class="hidden mb-2 text-xs font-mono text-gray-500 dark:text-slate-400 border border-gray-200 dark:border-slate-700 rounded p-2 bg-gray-50 dark:bg-slate-800 whitespace-pre-wrap"></div>
|
||||
<textarea id="pysim-cmd-output" rows="10" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 bg-gray-100 dark:bg-slate-800"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="pysim-sub-apdu" class="hidden">
|
||||
<div class="flex gap-2 items-center mb-2">
|
||||
<input id="pysim-apdu" class="font-mono flex-1 border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 dark:bg-slate-800" placeholder="00A4040000" onkeydown="if(event.key==='Enter')pysimApdu()">
|
||||
<button onclick="pysimApdu()" class="px-4 py-2.5 bg-emerald-600 text-white text-sm font-medium rounded hover:bg-emerald-700" data-l10n="Send">Send</button>
|
||||
</div>
|
||||
<textarea id="pysim-apdu-output" rows="5" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 bg-gray-100 dark:bg-slate-800"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="tab-profiler" class="tab-content hidden">
|
||||
<div id="profiler-list">
|
||||
<div class="flex gap-1 mb-3 border-b border-gray-300 dark:border-slate-600">
|
||||
<button class="profiler-list-tab px-4 py-1.5 text-sm rounded-t bg-blue-600 dark:bg-blue-500 text-white dark:text-white" data-list-tab="profiles" onclick="profilerListSwitch('profiles')" data-l10n="Profiles">Profiles</button>
|
||||
@@ -869,26 +896,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pysim-sub-cmd" class="hidden">
|
||||
<div class="flex gap-2 items-center mb-2">
|
||||
<div class="relative flex-1">
|
||||
<input id="pysim-cmd" class="font-mono w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 dark:bg-slate-800" placeholder="select MF" autocomplete="off" onkeydown="pysimCmdKeydown(event)" oninput="pysimCmdInput()">
|
||||
<div id="pysim-cmd-suggest" class="hidden absolute bottom-full left-0 right-0 mb-1 max-h-32 overflow-y-auto border border-gray-300 dark:border-slate-600 rounded bg-white dark:bg-slate-800 text-xs font-mono shadow-lg z-10"></div>
|
||||
</div>
|
||||
<button onclick="pysimExec()" class="px-4 py-2.5 bg-emerald-600 text-white text-sm font-medium rounded hover:bg-emerald-700" data-l10n="Execute">Execute</button>
|
||||
</div>
|
||||
<div id="pysim-cmd-hint" class="hidden mb-2 text-xs font-mono text-gray-500 dark:text-slate-400 border border-gray-200 dark:border-slate-700 rounded p-2 bg-gray-50 dark:bg-slate-800 whitespace-pre-wrap"></div>
|
||||
<textarea id="pysim-cmd-output" rows="10" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 bg-gray-100 dark:bg-slate-800"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="pysim-sub-apdu" class="hidden">
|
||||
<div class="flex gap-2 items-center mb-2">
|
||||
<input id="pysim-apdu" class="font-mono flex-1 border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 dark:bg-slate-800" placeholder="00A4040000" onkeydown="if(event.key==='Enter')pysimApdu()">
|
||||
<button onclick="pysimApdu()" class="px-4 py-2.5 bg-emerald-600 text-white text-sm font-medium rounded hover:bg-emerald-700" data-l10n="Send">Send</button>
|
||||
</div>
|
||||
<textarea id="pysim-apdu-output" rows="5" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-2.5 bg-gray-100 dark:bg-slate-800"></textarea>
|
||||
</div>
|
||||
<div id="pysim-sub-proactive" class="hidden">
|
||||
<div id="tab-phone" class="tab-content hidden">
|
||||
<div class="flex gap-6">
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-gray-500 dark:text-slate-400" data-l10n="STK menu">STK menu</div>
|
||||
@@ -933,7 +941,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="profiler-scan-modal" class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-lg p-6 max-w-sm w-full mx-4 max-h-[85vh] overflow-auto">
|
||||
@@ -1007,8 +1014,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ===== Tab switching =====
|
||||
function switchTab(name) {
|
||||
@@ -1023,8 +1028,9 @@ function switchTab(name) {
|
||||
active.classList.add('bg-blue-600', 'text-white', 'dark:bg-blue-500', 'dark:text-white');
|
||||
if (name === 'c-apdu') cApduSwitchSubtab('sim');
|
||||
else if (name === 'scp80') setHelpAnchor(scp80HelpAnchor);
|
||||
else if (name === 'response') setHelpAnchor('response-parser');
|
||||
else if (name === 'pysim') setHelpAnchor(pysimHelpAnchor);
|
||||
else if (name === 'profiler') { profilerSetView('list'); setHelpAnchor('profiler'); }
|
||||
else if (name === 'phone') { stkCheckMenu(); pysimEventsRender(); pysimProactiveLogRender(); pysimPliRender(); pysimPollStatusInit(); setHelpAnchor('proactive-uicc'); }
|
||||
}
|
||||
|
||||
let scp80HelpAnchor = 'secured-packet';
|
||||
@@ -1057,11 +1063,11 @@ function cApduSwitchSubtab(name) {
|
||||
btn.classList.toggle('text-gray-700', !active);
|
||||
btn.classList.toggle('dark:text-slate-300', !active);
|
||||
});
|
||||
['sim', 'usim', 'ber', 'ram', 'parse', 'httpota'].forEach(s => {
|
||||
['sim', 'usim', 'ber', 'ram', 'parse', 'httpota', 'response'].forEach(s => {
|
||||
const el = document.getElementById('c-apdu-sub-' + s);
|
||||
if (el) el.classList.toggle('hidden', s !== name);
|
||||
});
|
||||
setHelpAnchor({sim:'sim-rfm', usim:'usim-rfm', ber:'ber-tlv', ram:'ram-gp', parse:'c-apdu-parser', httpota:'http-ota'}[name] || 'c-apdu');
|
||||
setHelpAnchor({sim:'sim-rfm', usim:'usim-rfm', ber:'ber-tlv', ram:'ram-gp', parse:'c-apdu-parser', httpota:'http-ota', response:'response-parser'}[name] || 'c-apdu');
|
||||
}
|
||||
|
||||
// ===== HTTP OTA constructor =====
|
||||
@@ -4602,7 +4608,8 @@ async function pysimSendOta() {
|
||||
if (data.por && data.por.decoded) {
|
||||
document.getElementById('resp-sw').value = data.por.decoded.last_status_word || '';
|
||||
document.getElementById('resp-data').value = data.por.decoded.last_response_data || '';
|
||||
switchTab('response');
|
||||
switchTab('c-apdu');
|
||||
cApduSwitchSubtab('response');
|
||||
}
|
||||
let msg = 'OTA sent. SW: ' + data.sw;
|
||||
const por = data.por;
|
||||
@@ -5271,7 +5278,7 @@ async function stkCheckMenu() {
|
||||
}
|
||||
|
||||
function stkSetPillsDisabled(disabled) {
|
||||
document.querySelectorAll('.pysim-subtab').forEach(b => {
|
||||
document.querySelectorAll('.tab-btn, .pysim-subtab').forEach(b => {
|
||||
b.disabled = disabled;
|
||||
b.classList.toggle('opacity-50', disabled);
|
||||
});
|
||||
@@ -5579,13 +5586,11 @@ function pysimSwitchSubtab(name) {
|
||||
btn.classList.toggle('text-gray-700', !isActive);
|
||||
btn.classList.toggle('dark:text-slate-300', !isActive);
|
||||
});
|
||||
['cmd', 'apdu', 'files', 'custom', 'proactive', 'profiler'].forEach(s => {
|
||||
['cmd', 'apdu', 'files', 'custom'].forEach(s => {
|
||||
document.getElementById('pysim-sub-' + s).classList.toggle('hidden', s !== name);
|
||||
});
|
||||
if (name === 'custom') pysimCustomRender();
|
||||
if (name === 'proactive') { stkCheckMenu(); pysimEventsRender(); pysimProactiveLogRender(); pysimPliRender(); pysimPollStatusInit(); }
|
||||
if (name === 'profiler') profilerSetView('list');
|
||||
pysimHelpAnchor = {cmd:'pysim-cmdline', apdu:'raw-apdu', files:'file-manager', custom:'custom-files', proactive:'proactive-uicc', profiler:'profiler'}[name] || 'card-reader';
|
||||
pysimHelpAnchor = {cmd:'pysim-cmdline', apdu:'raw-apdu', files:'file-manager', custom:'custom-files'}[name] || 'card-reader';
|
||||
setHelpAnchor(pysimHelpAnchor);
|
||||
}
|
||||
|
||||
@@ -8796,7 +8801,7 @@ const LANG_RU = {
|
||||
'Verifying against pySim reference...': 'Сверка с эталоном pySim...',
|
||||
'Verify vs pySim': 'Проверить в pySim',
|
||||
'Send to Card': 'Отправить на карту',
|
||||
'Proactive UICC': 'Проактивный UICC',
|
||||
'Phone simulator': 'Симулятор телефона',
|
||||
'Events that the card monitors (SET UP EVENT LIST):': 'События, отслеживаемые картой (SET UP EVENT LIST):',
|
||||
'No events configured.': 'Нет настроенных событий.',
|
||||
'Fetched proactive commands:': 'Извлечённые проактивные команды:',
|
||||
@@ -8974,7 +8979,7 @@ function isViewVisible(id) {
|
||||
// Dynamic views build their labels with t() at render time; re-render the
|
||||
// visible ones after a language switch so they don't stay in the old language.
|
||||
function refreshDynamicI18n() {
|
||||
if (isViewVisible('pysim-sub-profiler')) {
|
||||
if (isViewVisible('tab-profiler')) {
|
||||
if (profilerView === 'list') { profilerRenderList(); profilerRenderSnapshotList(); }
|
||||
else if (profilerView === 'editor') profilerRenderEditor();
|
||||
else if (profilerView === 'results') profilerRenderResultsView();
|
||||
@@ -8982,7 +8987,7 @@ function refreshDynamicI18n() {
|
||||
}
|
||||
if (isViewVisible('profiler-scan-modal')) profilerScanRefreshOptions();
|
||||
if (isViewVisible('scp80-sub-cards')) cardsRender();
|
||||
if (isViewVisible('pysim-sub-proactive')) {
|
||||
if (isViewVisible('tab-phone')) {
|
||||
pysimEventsRender();
|
||||
pysimProactiveLogRender();
|
||||
pysimPliRender();
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'otaman-v92';
|
||||
const CACHE = 'otaman-v93';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -10,3 +10,19 @@ const closes = (html.match(/<\/div>/g) || []).length;
|
||||
test('HTML <div> tags are balanced', () => {
|
||||
assert.strictEqual(opens, closes, `Unbalanced divs: ${opens} opens vs ${closes} closes`);
|
||||
});
|
||||
|
||||
test('top-level tabs match the rearranged views', () => {
|
||||
const tabs = [...html.matchAll(/class="tab-btn[^"]*" data-tab="([^"]+)"/g)].map(m => m[1]);
|
||||
assert.deepStrictEqual(tabs, ['c-apdu', 'scp80', 'pysim', 'profiler', 'phone']);
|
||||
assert.match(html, /data-tab="c-apdu">Remote APDU</);
|
||||
});
|
||||
|
||||
test('response parser is a Remote APDU pill', () => {
|
||||
assert.match(html, /data-sub="response" onclick="cApduSwitchSubtab\('response'\)"/);
|
||||
assert.ok(html.includes('id="c-apdu-sub-response"'));
|
||||
});
|
||||
|
||||
test('profiler and phone simulator are top-level tab contents', () => {
|
||||
assert.ok(html.includes('id="tab-profiler" class="tab-content hidden"'));
|
||||
assert.ok(html.includes('id="tab-phone" class="tab-content hidden"'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user