feat: Push commands pill — GP administration trigger + TS 102 226 §9 pushes (v3.5.0)

The former HTTP OTA pill becomes "Push commands" (RU: Пуш/триггер) and groups
everything that makes the card dial out:

- Administrative session (GP Amd B §4.7): the existing Trigger/Store UI,
  unchanged, as the first section (most used).
- BIP / CAT_TP trigger (§9): request 01 BIP channel opening (optional OPEN
  CHANNEL COMPREHENSION-TLVs) or 02 CAT_TP link (destination port, optional
  max SDU / identification data).
- TCP trigger (§9): request 03 TCP connection (bearer, transport level with
  protocol type 02, destination address 21/57/F0, NAA/APN, extra TLVs) or
  04 identification packet (optional data, ICCID when absent — sent over an
  already open channel).

The guided sections reuse chainPushData (the RAM/GP chain PUSH encoder) and
preview the C-APDU; mandatory parameters (02 port, 03 port + address) are
enforced there while the chain row stays lenient.  Actions: Pack into Secured
packet (no auto-TAR — a §9 PUSH goes to the target application) and
→ Expanded Script (22 Command TLV import; berAppendApdu extracted from
loadExpandedScript).  Each §9 request has a note separating it from the
administration session.  Chain-builder PUSH command and the expanded-script
C-APDU picker are unchanged.

Tests: pushSectionApdu vectors in ts102226.test.js; help 2.7 EN/RU
restructured, READMEs, AGENTS; sw cache -> simple-v246.
This commit is contained in:
2026-09-23 08:57:59 +03:00
parent cab54a5504
commit f17e7cb8da
9 changed files with 250 additions and 18 deletions
+12 -1
View File
@@ -230,7 +230,10 @@
<h3 id="c-apdu-parser" class="text-lg font-medium mb-2">2.6 Разбор C-APDU</h3>
<p class="text-sm mb-3">Вставка raw APDU hex и отображение сворачиваемого дерева. Автоматически определяет контейнер: <strong>Expanded Script</strong> (начало <code class="font-mono text-sm">AA</code> или <code class="font-mono text-sm">AE80</code>, декодируется по ETSI TS 102 226 &sect;5.2.1) или <strong>Compact C-APDU chain</strong> (последовательность C-APDU ISO 7816). Каждый узел показывает метку, hex и краткое описание; родительские узлы раскрываются в подэлементы.</p>
<h3 id="http-ota" class="text-lg font-medium mb-2">2.7 HTTP OTA</h3>
<h3 id="push-commands" class="text-lg font-medium mb-2">2.7 Пуш/триггер</h3>
<p class="text-sm mb-3">Объединяет команды, заставляющие карту выйти в сеть: триггер административной сессии HTTP (GlobalPlatform) с режимом записи параметров и варианты команды PUSH из ETSI TS 102 226 &sect;9 (открытие BIP-канала, соединение CAT_TP, TCP-соединение, пакет идентификации). Они из разных спецификаций и доставляются по-разному &mdash; триггер администрирования это TLV-сообщение для Security Domain, а PUSH &sect;9 это C-APDU (<code class="font-mono text-sm">80 EC 01 P2</code>) для приложения с поддержкой BIP и/или CAT_TP.</p>
<h4 id="http-ota" class="font-medium mb-1">Сессия администрирования (HTTP OTA)</h4>
<p class="text-sm mb-3">Сборка payload&rsquo;ов Remote Application Management over HTTP по GlobalPlatform <strong>GPC v2.2 Amendment B v1.1</strong> (&sect;4.7). Два режима:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>Триггер (Push SMS)</strong> — параметры запуска административной сессии (<code class="font-mono text-sm">81 &gt; 83 &gt; 84/[85]/[86]/89</code>, таблица 4-3). Сообщение, которое просит Security Domain карты исходящим запросом начать HTTP-сессию.</li>
@@ -249,6 +252,14 @@
<p class="text-sm mb-2">Флажок <strong>Обёртка в Command Scripting template ('AA')</strong> оборачивает всю команду <code class="font-mono text-sm">81</code> в формат Expanded Remote Application data с определённой длиной (<code class="font-mono text-sm">AA</code>, ETSI TS 102 226 &sect;5.2.1) для TAR-ов, обрабатывающих расширенный формат (RAM-over-HTTP &sect;4.7).</p>
<p class="text-sm mb-2"><strong>Упаковать в Secured packet</strong> отправляет готовый payload на вкладку SCP80 для заполнения SPI/счётчика — там укажите TAR, который слушает SD (обычно TAR OTASD).</p>
<h4 id="push-ts102226" class="font-medium mb-1 mt-3">PUSH по TS 102 226 §9</h4>
<p class="text-sm mb-2">Два раздела собирают запросы &sect;9 тем же кодировщиком, что и строка <strong>PUSH</strong> в цепочке RAM/GP, показывают предпросмотр C-APDU и предлагают <strong>&laquo;Упаковать в Secured packet&raquo;</strong> (TAR остаётся ручным: запрос адресован целевому приложению, а не Security Domain) и <strong>&rarr; Расширенный скрипт</strong> (оборачивает APDU в Command TLV <code class="font-mono text-sm">22</code> и добавляет его в конструктор Expanded Script).</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>BIP / CAT_TP</strong> &mdash; запрос <code class="font-mono text-sm">01</code> открытие BIP-канала (необязательные COMPREHENSION-TLV OPEN CHANNEL; при пустом значении действуют умолчания приложения) или <code class="font-mono text-sm">02</code> установление соединения CAT_TP (порт назначения в transport level <code class="font-mono text-sm">3C</code> с типом протокола 00, необязательные buffer size <code class="font-mono text-sm">39</code> / данные идентификации <code class="font-mono text-sm">36</code>).</li>
<li><strong>TCP</strong> &mdash; запрос <code class="font-mono text-sm">03</code> TCP-соединение (bearer <code class="font-mono text-sm">35</code>, transport level <code class="font-mono text-sm">3C</code> с типом протокола 02 = TCP-клиент, удалённое соединение, адрес назначения <code class="font-mono text-sm">3E</code> с типом <code class="font-mono text-sm">21</code> IPv4 / <code class="font-mono text-sm">57</code> IPv6 / <code class="font-mono text-sm">F0</code> FQDN, NAA/APN <code class="font-mono text-sm">47</code>) или <code class="font-mono text-sm">04</code> пакет идентификации (необязательные данные, при отсутствии используется ICCID &mdash; отправляется по уже открытому TCP-каналу, не триггер).</li>
<li>В управляемых разделах обязательные параметры проверяются: для <code class="font-mono text-sm">02</code> нужен порт назначения, для <code class="font-mono text-sm">03</code> &mdash; порт и адрес назначения. Строка PUSH в цепочке RAM/GP остаётся нестрогой для свободного составления скриптов.</li>
</ul>
<h3 id="response-parser" class="text-lg font-medium mb-2">2.8 &laquo;Парсер ответов&raquo;</h3>
+12 -1
View File
@@ -229,7 +229,10 @@
<h3 id="c-apdu-parser" class="text-lg font-medium mb-2">2.6 C-APDU Parser</h3>
<p class="text-sm mb-3">Pastes raw APDU hex and renders a collapsible tree. It auto-detects the container: an <strong>Expanded Script</strong> (leading <code class="font-mono text-sm">AA</code> or <code class="font-mono text-sm">AE80</code>, decoded per ETSI TS 102 226 &sect;5.2.1) or a <strong>Compact C-APDU chain</strong> (a sequence of ISO 7816 C-APDUs). Each node shows its label, hex and a short description; parent nodes expand to reveal their sub-elements.</p>
<h3 id="http-ota" class="text-lg font-medium mb-2">2.7 HTTP OTA</h3>
<h3 id="push-commands" class="text-lg font-medium mb-2">2.7 Push commands</h3>
<p class="text-sm mb-3">Groups the commands that make the card dial out: the GlobalPlatform HTTP administration session trigger (and its Store mode) plus the ETSI TS 102 226 &sect;9 PUSH variants (BIP channel opening, CAT_TP link, TCP connection, identification packet). They come from different specs and are delivered differently &mdash; the administration trigger is a TLV message for the Security Domain, a &sect;9 PUSH is a C-APDU (<code class="font-mono text-sm">80 EC 01 P2</code>) for an application that supports BIP and/or CAT_TP.</p>
<h4 id="http-ota" class="font-medium mb-1">Administration session (HTTP OTA)</h4>
<p class="text-sm mb-3">Builds the Remote Application Management over HTTP payloads defined in GlobalPlatform <strong>GPC v2.2 Amendment B v1.1</strong> (&sect;4.7). Two modes:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>Trigger (Push SMS)</strong> — administration session triggering parameters (<code class="font-mono text-sm">81 &gt; 83 &gt; 84/[85]/[86]/89</code>, Table 4-3). This is the message that asks the card's Security Domain to dial out and start an HTTP session.</li>
@@ -248,6 +251,14 @@
<p class="text-sm mb-2"><strong>Command Scripting template</strong> (checkbox) wraps the whole <code class="font-mono text-sm">81</code> triggering command in the definite-length Expanded Remote Application data format (<code class="font-mono text-sm">AA</code>, ETSI TS 102 226 &sect;5.2.1) for TARs that process the expanded format (RAM-over-HTTP &sect;4.7).</p>
<p class="text-sm mb-2"><strong>Pack into Secured packet</strong> sends the built payload to the SCP80 tab for SPI/counter filling &mdash; insert the TAR the SD listens on (typically the OTASD TAR) there.</p>
<h4 id="push-ts102226" class="font-medium mb-1 mt-3">TS 102 226 §9 PUSH</h4>
<p class="text-sm mb-2">Two guided sections build the &sect;9 requests with the same encoder as the RAM/GP chain's <strong>PUSH</strong> row, preview the C-APDU, and offer <strong>Pack into Secured packet</strong> (the TAR stays manual: the request is addressed to the target application, not the Security Domain) and <strong>&rarr; Expanded Script</strong> (wraps the APDU in the <code class="font-mono text-sm">22</code> Command TLV and appends it to the Expanded Script builder).</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>BIP / CAT_TP</strong> &mdash; request <code class="font-mono text-sm">01</code> BIP channel opening (optional OPEN CHANNEL COMPREHENSION-TLVs; the application defaults apply when empty) or <code class="font-mono text-sm">02</code> CAT_TP link establishment (destination port in transport level <code class="font-mono text-sm">3C</code> with protocol type 00, optional buffer size <code class="font-mono text-sm">39</code> / identification data <code class="font-mono text-sm">36</code>).</li>
<li><strong>TCP</strong> &mdash; request <code class="font-mono text-sm">03</code> TCP connection (bearer <code class="font-mono text-sm">35</code>, transport level <code class="font-mono text-sm">3C</code> with protocol type 02 = TCP client remote, destination address <code class="font-mono text-sm">3E</code> with type <code class="font-mono text-sm">21</code> IPv4 / <code class="font-mono text-sm">57</code> IPv6 / <code class="font-mono text-sm">F0</code> FQDN, NAA/APN <code class="font-mono text-sm">47</code>) or <code class="font-mono text-sm">04</code> identification packet (optional data, ICCID when absent &mdash; sent over an already open TCP channel, not a trigger).</li>
<li>Mandatory parameters are enforced in the guided sections: <code class="font-mono text-sm">02</code> needs the destination port, <code class="font-mono text-sm">03</code> the port and the destination address. The RAM/GP chain row keeps the lenient behaviour for free-form scripting.</li>
</ul>
<h3 id="response-parser" class="text-lg font-medium mb-2">2.8 Response parser</h3>
+177 -8
View File
@@ -54,7 +54,7 @@
<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="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="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="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="httpota" onclick="cApduSwitchSubtab('httpota')" data-l10n="Push commands">Push commands</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>
@@ -182,6 +182,7 @@
</div>
<div id="c-apdu-sub-httpota" class="hidden">
<h3 class="text-base font-medium mb-3" data-l10n="Administrative session — administration trigger (HTTP OTA)">Administrative session — administration trigger (HTTP OTA)</h3>
<div class="flex gap-2 mb-3">
<button id="hota-mode-trigger" onclick="hotaSetMode('trigger')" class="px-4 py-1.5 text-sm rounded-full bg-blue-600 text-white" data-l10n="Trigger (Push SMS)">Trigger (Push SMS)</button>
<button id="hota-mode-store" onclick="hotaSetMode('store')" class="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-l10n="Store (SD admin params)">Store (SD admin params)</button>
@@ -293,6 +294,71 @@
<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>
<h3 class="text-base font-medium mt-4 mb-2" data-l10n="BIP / CAT_TP trigger (TS 102 226 §9)">BIP / CAT_TP trigger (TS 102 226 §9)</h3>
<p class="text-xs text-gray-500 dark:text-slate-400 mb-2" data-l10n="Not the administration session — a C-APDU for an app that supports BIP/CAT_TP">Not the administration session — a C-APDU for an app that supports BIP/CAT_TP</p>
<div class="border border-gray-300 dark:border-slate-600 rounded p-3 bg-gray-50 dark:bg-slate-800 mb-3">
<div class="mb-2">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Request">Request</label>
<select id="push-bip-request" onchange="pushUpdate('bip')" class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800 font-mono">
<option value="01" data-l10n="BIP channel opening (OPEN CHANNEL)">BIP channel opening (OPEN CHANNEL)</option>
<option value="02" data-l10n="CAT_TP link establishment">CAT_TP link establishment</option>
</select>
</div>
<div data-push-bip="01">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="OPEN CHANNEL COMPREHENSION-TLVs (hex, optional)">OPEN CHANNEL COMPREHENSION-TLVs (hex, optional)</label>
<textarea id="push-bip-data" rows="2" oninput="pushUpdate('bip')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800 mb-2" placeholder="82102081..."></textarea>
</div>
<div data-push-bip="02" class="grid grid-cols-3 gap-2 mb-2">
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Destination port (hex)">Destination port (hex)</label>
<input id="push-bip-port" oninput="pushUpdate('bip')" maxlength="4" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800" placeholder="1F90"></div>
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Max SDU (hex, optional)">Max SDU (hex, optional)</label>
<input id="push-bip-sdu" oninput="pushUpdate('bip')" maxlength="4" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800"></div>
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Identification data (hex, optional)">Identification data (hex, optional)</label>
<input id="push-bip-ident" oninput="pushUpdate('bip')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800"></div>
</div>
<div data-push-bip="02">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Extra TLVs (hex, optional)">Extra TLVs (hex, optional)</label>
<input id="push-bip-extra" oninput="pushUpdate('bip')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">
</div>
</div>
<textarea id="push-bip-preview" rows="2" readonly 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-2" placeholder="C-APDU (TS 102 226 §9)"></textarea>
<button onclick="packToSp('push-bip-preview')" id="push-bip-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>
<button onclick="pushToExpanded('push-bip-preview')" id="push-bip-exp-btn" disabled class="mb-3 px-5 py-2.5 bg-violet-600 text-white text-sm font-medium rounded hover:bg-violet-700 disabled:opacity-40 disabled:cursor-not-allowed" data-l10n="→ Expanded Script">→ Expanded Script</button>
<h3 class="text-base font-medium mt-4 mb-2" data-l10n="TCP trigger (TS 102 226 §9)">TCP trigger (TS 102 226 §9)</h3>
<p class="text-xs text-gray-500 dark:text-slate-400 mb-2" data-l10n="Not the administration session — a C-APDU for an app that supports BIP/CAT_TP">Not the administration session — a C-APDU for an app that supports BIP/CAT_TP</p>
<div class="border border-gray-300 dark:border-slate-600 rounded p-3 bg-gray-50 dark:bg-slate-800 mb-3">
<div class="mb-2">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Request">Request</label>
<select id="push-tcp-request" onchange="pushUpdate('tcp')" class="w-full border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800 font-mono">
<option value="03" data-l10n="TCP connection">TCP connection</option>
<option value="04" data-l10n="Identification packet">Identification packet</option>
</select>
</div>
<div data-push-tcp="03" class="grid grid-cols-2 gap-2 mb-2">
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Bearer description (hex, empty = 350103)">Bearer description (hex, empty = 350103)</label>
<input id="push-tcp-bearer" oninput="pushUpdate('tcp')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800" placeholder="350103"></div>
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Destination port (hex)">Destination port (hex)</label>
<input id="push-tcp-port" oninput="pushUpdate('tcp')" maxlength="4" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800" placeholder="0050"></div>
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Destination address value (hex: 21 IPv4 / 57 IPv6 / F0 FQDN)">Destination address value (hex: 21 IPv4 / 57 IPv6 / F0 FQDN)</label>
<input id="push-tcp-addr" oninput="pushUpdate('tcp')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800" placeholder="210A000001"></div>
<div><label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="NAA / APN (text)">NAA / APN (text)</label>
<input id="push-tcp-apn" oninput="pushUpdate('tcp')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800" placeholder="internet"></div>
</div>
<div data-push-tcp="04" class="mb-2">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Identification data (hex, optional; ICCID when empty)">Identification data (hex, optional; ICCID when empty)</label>
<input id="push-tcp-ident" oninput="pushUpdate('tcp')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">
<div class="text-xs text-gray-500 dark:text-slate-400 mt-1" data-l10n="The identification packet is sent over an already open TCP channel (not a trigger).">The identification packet is sent over an already open TCP channel (not a trigger).</div>
</div>
<div data-push-tcp="03">
<label class="block mb-1 text-xs font-medium text-gray-500 dark:text-slate-400" data-l10n="Extra TLVs (hex, optional)">Extra TLVs (hex, optional)</label>
<input id="push-tcp-extra" oninput="pushUpdate('tcp')" class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">
</div>
</div>
<textarea id="push-tcp-preview" rows="2" readonly 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-2" placeholder="C-APDU (TS 102 226 §9)"></textarea>
<button onclick="packToSp('push-tcp-preview')" id="push-tcp-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>
<button onclick="pushToExpanded('push-tcp-preview')" id="push-tcp-exp-btn" disabled class="mb-3 px-5 py-2.5 bg-violet-600 text-white text-sm font-medium rounded hover:bg-violet-700 disabled:opacity-40 disabled:cursor-not-allowed" data-l10n="→ Expanded Script">→ Expanded Script</button>
</div>
<div id="c-apdu-sub-response" class="hidden">
@@ -1451,7 +1517,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
const SIMPLE_VERSION = '3.4.0';
const SIMPLE_VERSION = '3.5.0';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -2039,8 +2105,87 @@ function hotaPreviewEdited() {
const hex = document.getElementById('hota-preview').value;
document.getElementById('hota-pack-btn').disabled = !hex;
}
// ===== Push command sections (TS 102 226 9) =====
// The BIP/CAT_TP and TCP sections feed the same encoder as the RAM/GP chain's
// PUSH command (chainPushData, vector-tested in ts102226.test.js) and preview
// the resulting C-APDU. A §9 PUSH is addressed to the application's TAR, so
// packing does not auto-select a TAR (unlike the RFM/RAM chain previews).
function pushReadFields(section) {
const v = id => { const el = document.getElementById(id); return el ? el.value : ''; };
if (section === 'bip') {
return {
request: v('push-bip-request') || '01',
pushData: v('push-bip-data'),
pushPort: v('push-bip-port'),
pushSdu: v('push-bip-sdu'),
pushIdent: v('push-bip-ident'),
pushExtra: v('push-bip-extra'),
};
}
return {
request: v('push-tcp-request') || '03',
pushBearer: v('push-tcp-bearer'),
pushPort: v('push-tcp-port'),
pushAddr: v('push-tcp-addr'),
pushApn: v('push-tcp-apn'),
pushIdent: v('push-tcp-ident'),
pushExtra: v('push-tcp-extra'),
};
}
// C-APDU for a §9 push from the section's field values; empty when a mandatory
// parameter is missing (TS 102 226 9.2.2/9.2.3: CAT_TP and TCP need the
// destination port, TCP also the destination address). P2=01/04 may carry no
// data (the application defaults / ICCID are used). The RAM/GP chain row
// stays lenient so scripts can be composed freely.
function pushSectionApdu(section, f) {
const request = f.request || (section === 'bip' ? '01' : '03');
const hx = s => (s || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase();
if (request === '02' && hx(f.pushPort).length !== 4) return '';
if (request === '03' && (hx(f.pushPort).length !== 4 || !hx(f.pushAddr))) return '';
const data = chainPushData(Object.assign({}, f, { pushType: request }));
if (!data) return buildApdu(0x80, 0xEC, 0x01, parseInt(request, 16), null, null);
return buildApdu(0x80, 0xEC, 0x01, parseInt(request, 16), data.length / 2, data);
}
function pushToggleFields(section) {
const sel = document.getElementById('push-' + section + '-request');
const request = sel ? sel.value : '';
document.querySelectorAll('[data-push-' + section + ']').forEach(el => {
const when = (el.getAttribute('data-push-' + section) || '').split(',');
el.classList.toggle('hidden', when.indexOf(request) < 0);
});
}
function pushUpdate(section) {
pushToggleFields(section);
const apdu = pushSectionApdu(section, pushReadFields(section));
const preview = document.getElementById('push-' + section + '-preview');
if (preview) preview.value = apdu;
['pack', 'exp'].forEach(kind => {
const btn = document.getElementById('push-' + section + '-' + kind + '-btn');
if (btn) btn.disabled = !apdu;
});
}
// Import the §9 C-APDU into the Expanded Script builder as a 22 command TLV.
function pushToExpanded(sourceId) {
const hex = (document.getElementById(sourceId) || {}).value || '';
if (!hex) return;
cApduSwitchSubtab('ber');
berAppendApdu(hex);
genBerTlv();
}
function pushInit() {
['bip', 'tcp'].forEach(pushUpdate);
}
pushInit();
document.querySelectorAll('#c-apdu-sub-httpota input, #c-apdu-sub-httpota select').forEach(el => {
if (el.type === 'checkbox') return;
if ((el.id || '').indexOf('push-') === 0) return; // §9 sections update themselves
el.addEventListener('input', hotaUpdatePreview);
});
@@ -3893,17 +4038,20 @@ function chainRamApduList() {
return chainApduList('chain-ram');
}
// Append one raw APDU as a C-APDU row of the Expanded Script builder.
function berAppendApdu(hex) {
const row = addBerRow('c-apdu');
const input = row.querySelector('.ber-hex');
if (input) input.value = hex;
updateBerRow(row.querySelector('.ber-type'));
}
// Import a built chain into the Expanded Script builder as C-APDU rows.
function loadExpandedScript(chainId) {
const apdus = chainApduList(chainId);
if (!apdus.length) return;
cApduSwitchSubtab('ber');
for (const hex of apdus) {
const row = addBerRow('c-apdu');
const input = row.querySelector('.ber-hex');
if (input) input.value = hex;
updateBerRow(row.querySelector('.ber-type'));
}
apdus.forEach(berAppendApdu);
genBerTlv();
}
@@ -14735,6 +14883,27 @@ const LANG_RU = {
'actual': 'фактически',
'HTTP OTA': 'HTTP OTA',
'Trigger (Push SMS)': 'Триггер (Push SMS)',
'Push commands': 'Пуш/триггер',
'Administrative session — administration trigger (HTTP OTA)': 'Сессия администрирования — триггер (HTTP OTA)',
'BIP / CAT_TP trigger (TS 102 226 §9)': 'Триггер BIP / CAT_TP (TS 102 226 §9)',
'TCP trigger (TS 102 226 §9)': 'Триггер TCP (TS 102 226 §9)',
'Not the administration session — a C-APDU for an app that supports BIP/CAT_TP': 'C-APDU для приложения с поддержкой BIP/CAT_TP (не admin session!)',
'Request': 'Запрос',
'BIP channel opening (OPEN CHANNEL)': 'Открытие BIP-канала (OPEN CHANNEL)',
'CAT_TP link establishment': 'Установление соединения CAT_TP',
'TCP connection': 'TCP-соединение',
'Identification packet': 'Пакет идентификации',
'OPEN CHANNEL COMPREHENSION-TLVs (hex, optional)': 'COMPREHENSION-TLV OPEN CHANNEL (hex, необязательно)',
'Destination port (hex)': 'Порт назначения (hex)',
'Max SDU (hex, optional)': 'Макс. SDU (hex, необязательно)',
'Identification data (hex, optional)': 'Данные идентификации (hex, необязательно)',
'Identification data (hex, optional; ICCID when empty)': 'Данные идентификации (hex, необязательно; при пустом — ICCID)',
'Extra TLVs (hex, optional)': 'Дополнительные TLV (hex, необязательно)',
'Bearer description (hex, empty = 350103)': 'Описание bearer (hex, пусто = 350103)',
'Destination address value (hex: 21 IPv4 / 57 IPv6 / F0 FQDN)': 'Значение адреса назначения (hex: 21 IPv4 / 57 IPv6 / F0 FQDN)',
'NAA / APN (text)': 'NAA / APN (текст)',
'The identification packet is sent over an already open TCP channel (not a trigger).': 'Пакет идентификации отправляется по уже открытому TCP-каналу (не триггер).',
'C-APDU (TS 102 226 §9)': 'C-APDU (TS 102 226 §9)',
'Store (SD admin params)': 'Store (параметры SD)',
'SD params tag': 'Tag параметров SD',
"Connection parameters ('84')": "Параметры соединения ('84')",
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v245';
const CACHE = 'simple-v246';
const URLS = [
'index.html',
'help.html',
+19 -1
View File
@@ -31,7 +31,7 @@ function extractFunc(src, name) {
const FNS = ['berLenStr', 'buildApdu', 'buildSelect', 'escHtml', 'esc', 'chainInit',
'chainKind', 'chainIsEmbedded', 'chainCommands', 'chainBuildRowHex',
'chainSimBuildRowHex', 'chainRamBuildRowHex', 'chainPushData', '_hotaAsciiHex',
'chainApduList', 'chainBuildFcp'];
'chainApduList', 'chainBuildFcp', 'pushSectionApdu'];
let code = '';
for (const f of FNS) code += extractFunc(html, f) + '\n';
for (const c of ['CHAIN_CMDS_SIM', 'CHAIN_CMDS_USIM', 'CHAIN_CMDS_RAM']) {
@@ -227,6 +227,24 @@ test('CREATE FILE FCP template skeleton (TS 102 222 table 4)', () => {
assert.strictEqual(r4.fields.fcp, '620883026F0780020010');
});
test('pushSectionApdu builds the guided §9 push commands (Remote APDU pill)', () => {
// BIP channel opening: OPEN CHANNEL TLVs are optional.
assert.strictEqual(pushSectionApdu('bip', { request: '01', pushData: '350103' }), '80EC010103350103');
assert.strictEqual(pushSectionApdu('bip', { request: '01', pushData: '' }), '80EC0101');
// CAT_TP: the destination port is mandatory (9.2.2).
assert.strictEqual(pushSectionApdu('bip', { request: '02', pushPort: '1F90' }), '80EC0102053C03001F90');
assert.strictEqual(pushSectionApdu('bip', { request: '02', pushPort: '' }), '');
// TCP: port and destination address are mandatory (9.2.3).
assert.strictEqual(
pushSectionApdu('tcp', { request: '03', pushPort: '0050', pushAddr: '210A000001', pushApn: 'internet' }),
'80EC010319' + '3501033C030200503E05210A0000014708696E7465726E6574');
assert.strictEqual(pushSectionApdu('tcp', { request: '03', pushPort: '0050' }), '');
assert.strictEqual(pushSectionApdu('tcp', { request: '03', pushAddr: '210A000001' }), '');
// Identification packet: optional data, ICCID is used when absent (9.2.4).
assert.strictEqual(pushSectionApdu('tcp', { request: '04', pushIdent: '0102' }), '80EC01040436020102');
assert.strictEqual(pushSectionApdu('tcp', { request: '04', pushIdent: '' }), '80EC0104');
});
test('chainApduList drops GET RESPONSE and splits multi-APDU rows', () => {
_chains['ber-usim-1'] = { rows: [
row('read-binary', { offset: '0000', le: '00' }),