71da10b637
New read-only capture entity stored in localStorage 'otaman_snapshots': - profiler list view is now two labelled columns: Profiles and Card snapshots; each snapshot row shows name, decoded ICCID, date, file count and Open / Export / Delete buttons - New snapshot reuses the scan modal in a snapshot mode (name only, no ignore/mask/FCP options) via profilerScanCard(..., 'snapshot') -> profilerBuildSnapshotFile: metadata + raw FCI + exact contents for every readable file (no ignore list, no masking). The scan returns to the list afterwards. - ICCID is decoded from EF.ICCID (2FE2) with decIccid() (nibble-swapped E.118 digits, F pad per TS 102 221 13.2) and stored as an immutable snapshot field, shown next to the name in the list and the view. - Open shows all captured data read-only (attributes, raw FCI + decoded FCI, contents or 'Not captured'); only the name is editable/saveable. - Import/Export/Delete + profilerValidateSnapshot; quota-safe save. Tests: decIccid, profilerSnapshotIccid, validation, snapshot builder (exact contents/no mask/unreadable), snapshot-mode scan with ICCID. Docs synced (help/help-ru). SW cache v79 -> v80.
549 lines
61 KiB
HTML
549 lines
61 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>OTAMan — Help</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body class="bg-neutral-50 dark:bg-slate-900 text-gray-800 dark:text-slate-200">
|
||
|
||
<div class="flex">
|
||
|
||
<aside class="sticky top-0 h-screen overflow-y-auto w-72 shrink-0 border-r border-gray-300 dark:border-slate-700 px-4 py-6">
|
||
<a href="index.html" class="block text-xs text-gray-400 hover:text-gray-600 dark:text-slate-500 dark:hover:text-slate-300 mb-4">← Back to app</a>
|
||
<nav id="toc" class="text-sm"></nav>
|
||
</aside>
|
||
|
||
<main class="flex-1 min-w-0 px-6 py-8">
|
||
<h1 class="text-2xl font-bold text-heading mb-6">OTAMan <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Documentation</span></h1>
|
||
<section class="mb-10">
|
||
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Overview</h2>
|
||
<p class="mb-3">OTAMan is a Progressive Web App (PWA) for building APDU commands, assembling SCP80 secured packets, browsing the SIM Toolkit (STK) menu, and simulating a real network environment against a SIM/USIM/UICC card via a PC/SC reader.</p>
|
||
<p class="mb-3">The application is a single static <code class="font-mono text-sm">index.html</code> file. All computation runs in the browser; the card is accessed through a local HTTP server (<code class="font-mono text-sm">pysim-otaman-server</code>) that wraps the <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a> library.</p>
|
||
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Browser (OTAMan PWA) → HTTP :8080 → pysim-otaman-server → pySim → PC/SC → card reader → UICC/SIM</pre>
|
||
<p class="mb-3">Standards referenced across the application:</p>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li>ETSI TS 102 221 — UICC-Terminal interface (CLA 00, USIM files)</li>
|
||
<li>ETSI TS 102 222 — Administrative commands</li>
|
||
<li>ETSI TS 102 223 — Card Application Toolkit (CAT, proactive commands)</li>
|
||
<li>ETSI TS 102 225 — Secured packet structure for (U)SIM toolkit</li>
|
||
<li>ETSI TS 102 226 — Remote APDU structure for UICC based applications</li>
|
||
<li>ETSI TS 151 011 — SIM-ME interface (CLA A0)</li>
|
||
<li>3GPP TS 131 111 — USIM Application Toolkit (USAT)</li>
|
||
<li>3GPP TS 31.102 — USIM application characteristics</li>
|
||
<li>3GPP TS 23.038 — GSM 7-bit alphabet and DCS</li>
|
||
<li>3GPP TS 24.008 / 24.301 / 24.501 — NAS cause codes</li>
|
||
<li>GlobalPlatform Card Specification v2.3.1</li>
|
||
<li>GlobalPlatform GPC v2.2 Amendment B v1.1 — Remote Application Management over HTTP</li>
|
||
<li>ISO/IEC 7816-4 — commands for interchange</li>
|
||
<li>ISO/IEC 9797-1 — MAC algorithms</li>
|
||
</ul>
|
||
|
||
<h3 id="interface" class="text-lg font-medium mb-2">1.1 Interface</h3>
|
||
<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>
|
||
</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>
|
||
|
||
<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>
|
||
<p class="mb-2">Commands are assembled as a <strong>chain</strong>: press a command button (e.g. <code class="font-mono text-sm">+ SELECT</code>) to append a row, fill that row’s fields, and the chain preview (above the pack button) updates automatically. Add a <strong>GET RESPONSE</strong> row to fetch data following a SELECT. Press <strong>Pack into Secured packet</strong> to wrap the whole chain into an SCP80 packet.</p>
|
||
<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">Command</th><th class="text-left py-1 px-2">INS</th><th class="text-left py-1 px-2">Description</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">SELECT</td><td class="py-1 px-2 font-mono">A4</td><td class="py-1 px-2">Select EF/DF by FID, path, AID or chain</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">UPDATE RECORD</td><td class="py-1 px-2 font-mono">DC</td><td class="py-1 px-2">Update a record in a record-oriented EF</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">UPDATE BINARY</td><td class="py-1 px-2 font-mono">D6</td><td class="py-1 px-2">Update binary content at an offset</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">READ RECORD</td><td class="py-1 px-2 font-mono">B2</td><td class="py-1 px-2">Read a record</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">READ BINARY</td><td class="py-1 px-2 font-mono">B0</td><td class="py-1 px-2">Read binary content</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">ERASE BINARY</td><td class="py-1 px-2 font-mono">0E</td><td class="py-1 px-2">Erase binary at an offset</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">ACTIVATE FILE</td><td class="py-1 px-2 font-mono">44</td><td class="py-1 px-2">Activate a file</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">DEACTIVATE FILE</td><td class="py-1 px-2 font-mono">04</td><td class="py-1 px-2">Deactivate a file</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">VERIFY PIN</td><td class="py-1 px-2 font-mono">20</td><td class="py-1 px-2">Verify PIN1 or PIN2</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CHANGE PIN</td><td class="py-1 px-2 font-mono">24</td><td class="py-1 px-2">Change PIN1 or PIN2</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">DISABLE PIN</td><td class="py-1 px-2 font-mono">26</td><td class="py-1 px-2">Disable a PIN</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">ENABLE PIN</td><td class="py-1 px-2 font-mono">28</td><td class="py-1 px-2">Enable a PIN</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">UNBLOCK PIN</td><td class="py-1 px-2 font-mono">2C</td><td class="py-1 px-2">Unblock a PIN with PUK</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">GET RESPONSE</td><td class="py-1 px-2 font-mono">C0</td><td class="py-1 px-2">Fetch data indicated by a preceding <code class="font-mono text-sm">61XX</code>/<code class="font-mono text-sm">9FXX</code> status word</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h4 id="sim-select-methods" class="font-medium mb-1">SELECT methods</h4>
|
||
<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">Method</th><th class="text-left py-1 px-2">P1</th><th class="text-left py-1 px-2">P2</th><th class="text-left py-1 px-2">Input</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">By FID</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">2-byte FID (4 hex)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">By full path from MF</td><td class="py-1 px-2 font-mono">08</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">Full path hex from MF</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">By DF name / AID</td><td class="py-1 px-2 font-mono">04</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">AID (application ID)</td></tr>
|
||
<tr><td class="py-1 px-2">Chain</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">Comma-separated FIDs; a <code class="font-mono text-sm">C0</code> (or <code class="font-mono text-sm">C0:NN</code>) token inserts a GET RESPONSE hop</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="text-sm mb-3">For record commands, the P2 mode is <strong>Absolute (04)</strong>, <strong>Next (02)</strong>, or <strong>Previous (03)</strong>. When a Case 4 command is immediately followed by a GET RESPONSE row, the chain builder strips its trailing Le byte automatically (ETSI TS 102 226 §5.1.1). A conversion panel is embedded in the right column (IMSI, MSISDN, ICCID, SPN, PLMN, nibble swap). See <a href="#conversion" class="text-blue-600 dark:text-blue-400 hover:underline">§2.5</a>.</p>
|
||
|
||
<h3 id="usim-rfm" class="text-lg font-medium mb-2">2.2 USIM RFM</h3>
|
||
<p class="mb-2">CLA = <code class="font-mono text-sm">00</code> (ETSI TS 102 221). Same chain builder and command set as SIM. Differences:</p>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li><strong>SELECT</strong> requests FCP by default (P2=<code class="font-mono text-sm">04</code>) and appends Le=<code class="font-mono text-sm">00</code>; a <strong>Silent (P2=0C)</strong> checkbox selects without requesting FCP (no Le, no response data).</li>
|
||
<li><strong>By path</strong> offers <strong>from MF</strong> (P1=<code class="font-mono text-sm">08</code>) or <strong>from current DF</strong> (P1=<code class="font-mono text-sm">09</code>).</li>
|
||
<li>Each SELECT hop in a chain requests FCP unless marked silent.</li>
|
||
</ul>
|
||
|
||
<h3 id="ber-tlv" class="text-lg font-medium mb-2">2.3 Expanded Script</h3>
|
||
<p class="mb-2">Builds the Expanded Remote Application data format per ETSI TS 102 226 §5.2.1.</p>
|
||
<h4 id="ber-format" class="font-medium mb-1">Format</h4>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li><strong>Definite (AA)</strong>: <code class="font-mono text-sm">AA</code> + length + Command TLVs</li>
|
||
<li><strong>Indefinite (AE)</strong>: <code class="font-mono text-sm">AE</code> + <code class="font-mono text-sm">80</code> + Command TLVs + <code class="font-mono text-sm">00 00</code></li>
|
||
</ul>
|
||
<h4 id="ber-command-tlvs" class="font-medium mb-1">Command TLVs</h4>
|
||
<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">Type</th><th class="text-left py-1 px-2">Tag</th><th class="text-left py-1 px-2">Description</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">C-APDU</td><td class="py-1 px-2 font-mono">22</td><td class="py-1 px-2">Raw APDU hex</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Immediate Action</td><td class="py-1 px-2 font-mono">81</td><td class="py-1 px-2">Proactive command or action indicator</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Error Action</td><td class="py-1 px-2 font-mono">82</td><td class="py-1 px-2">Conditional error recovery with action indicator or proactive command</td></tr>
|
||
<tr><td class="py-1 px-2">Script Chaining</td><td class="py-1 px-2 font-mono">83</td><td class="py-1 px-2">Multi-packet script execution with First/Intermediary/Last flags</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="text-sm mb-3">The Immediate Action builder offers an action indicator (<code class="font-mono text-sm">81</code>/<code class="font-mono text-sm">82</code>), a structured proactive command builder (REFRESH, DISPLAY TEXT, PLAY TONE with auto-generated COMPREHENSION-TLV objects), or a freeform hex input.</p>
|
||
|
||
<h4 id="ber-error-action" class="font-medium mb-2 text-base">Error Action TLV (Tag 82)</h4>
|
||
<p class="text-sm mb-2">Error recovery per TS 102 226 §5.2.1.3 — one of four forms:</p>
|
||
<ul class="text-sm list-disc pl-5 mb-2">
|
||
<li><strong>Proactive command:</strong> COMPREHENSION-TLV set with DISPLAY TEXT or PLAY TONE (only these two are allowed in an Error Action, TS 102 226 Table 5.9)</li>
|
||
<li><strong>No action:</strong> <code class="font-mono text-sm">82 00</code></li>
|
||
<li><strong>Reference to EFRMA record:</strong> <code class="font-mono text-sm">82 01 <ref></code> with record number <code class="font-mono text-sm">01</code>–<code class="font-mono text-sm">7F</code></li>
|
||
<li><strong>Custom hex:</strong> arbitrary TLV value</li>
|
||
</ul>
|
||
|
||
<h4 id="ber-script-chaining" class="font-medium mb-2 text-base">Script Chaining TLV (Tag 83)</h4>
|
||
<p class="text-sm mb-2">Multi-packet script execution with context preservation:</p>
|
||
<ul class="text-sm list-disc pl-5 mb-2">
|
||
<li><strong>Chaining Flags:</strong> <code class="font-mono text-sm">01</code> first script (delete chaining info on reset), <code class="font-mono text-sm">11</code> first script (keep chaining info across reset, RFM only), <code class="font-mono text-sm">02</code> subsequent script (more to follow), <code class="font-mono text-sm">03</code> subsequent script (last)</li>
|
||
<li><strong>Script ID:</strong> Correlation identifier across packets (1-4 bytes hex, auto-increment hints provided)</li>
|
||
<li><strong>Additional Data:</strong> Extended chaining information (optional hex)</li>
|
||
<li><strong>Context Preservation:</strong> UICC keeps security/transaction state open across chained scripts</li>
|
||
</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>
|
||
|
||
<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>
|
||
<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">Command</th><th class="text-left py-1 px-2">INS</th><th class="text-left py-1 px-2">P1</th><th class="text-left py-1 px-2">Description</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INSTALL [for load]</td><td class="py-1 px-2 font-mono">E6</td><td class="py-1 px-2 font-mono">02</td><td class="py-1 px-2">Register a load file for loading</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INSTALL [for install]</td><td class="py-1 px-2 font-mono">E6</td><td class="py-1 px-2 font-mono">0C</td><td class="py-1 px-2">Install an application or SD</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INSTALL [make selectable]</td><td class="py-1 px-2 font-mono">E6</td><td class="py-1 px-2 font-mono">08</td><td class="py-1 px-2">Make an application selectable</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INSTALL [registry update]</td><td class="py-1 px-2 font-mono">E6</td><td class="py-1 px-2 font-mono">40</td><td class="py-1 px-2">Update registry entries</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INSTALL [extradition]</td><td class="py-1 px-2 font-mono">E6</td><td class="py-1 px-2 font-mono">10</td><td class="py-1 px-2">Extradition between SDs</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">LOAD</td><td class="py-1 px-2 font-mono">E8</td><td class="py-1 px-2 font-mono">80</td><td class="py-1 px-2">Load executable code block (P1=80 last block, block number in P2)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">DELETE</td><td class="py-1 px-2 font-mono">E4</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">Delete application or SD (P2=00 AID only / 80 AID + related objects)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">GET STATUS</td><td class="py-1 px-2 font-mono">F2</td><td class="py-1 px-2 font-mono">80/40/20/10</td><td class="py-1 px-2">Get card status</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">GET DATA</td><td class="py-1 px-2 font-mono">CA</td><td class="py-1 px-2 font-mono">tag</td><td class="py-1 px-2">Read card data objects</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">STORE DATA</td><td class="py-1 px-2 font-mono">E2</td><td class="py-1 px-2 font-mono">00/40/80/C0/E0</td><td class="py-1 px-2">Store data (key, certificate, …)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">SET STATUS</td><td class="py-1 px-2 font-mono">F0</td><td class="py-1 px-2 font-mono">80/40/60</td><td class="py-1 px-2">Lifecycle state management</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">EXTERNAL AUTHENTICATE</td><td class="py-1 px-2 font-mono">82</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">SCP host authentication</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">INTERNAL AUTHENTICATE</td><td class="py-1 px-2 font-mono">88</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">Card challenge-response</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">GET RESPONSE</td><td class="py-1 px-2 font-mono">C0</td><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">Fetch data after a <code class="font-mono text-sm">61XX</code> status (Le configurable)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h4 id="ram-privileges" class="font-medium mb-1">Privileges (INSTALL [for install])</h4>
|
||
<p class="text-sm mb-2">Three privilege bytes (GP spec Tables 11-7/8/9), encoded as a length-value field inside the INSTALL data:</p>
|
||
<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">Bit</th><th class="text-left py-1 px-2">Byte 1</th><th class="text-left py-1 px-2">Byte 2</th><th class="text-left py-1 px-2">Byte 3</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b8</td><td class="py-1 px-2">Security Domain</td><td class="py-1 px-2">Trusted Path</td><td class="py-1 px-2">Receipt Generation</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b7</td><td class="py-1 px-2">DAP Verification</td><td class="py-1 px-2">Authorized Management</td><td class="py-1 px-2"></td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b6</td><td class="py-1 px-2">Delegated Management</td><td class="py-1 px-2">Token Verification</td><td class="py-1 px-2"></td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b5</td><td class="py-1 px-2">Card Lock</td><td class="py-1 px-2">Global Delete</td><td class="py-1 px-2"></td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b4</td><td class="py-1 px-2">Card Terminate</td><td class="py-1 px-2">Global Lock</td><td class="py-1 px-2"></td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b3</td><td class="py-1 px-2">Card Reset</td><td class="py-1 px-2">Global Registry</td><td class="py-1 px-2"></td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">b2</td><td class="py-1 px-2">CVM Management</td><td class="py-1 px-2">Final Application</td><td class="py-1 px-2"></td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">b1</td><td class="py-1 px-2">Mandated DAP Verification</td><td class="py-1 px-2"></td><td class="py-1 px-2"></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h4 id="ram-msl" class="font-medium mb-1">MSL (Minimum Security Level) — SPI1 byte per TS 102 225</h4>
|
||
<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">Value</th><th class="text-left py-1 px-2">Meaning</th></tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">00</td><td class="py-1 px-2">No check</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">11</td><td class="py-1 px-2">RC/CC/DS</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">12</td><td class="py-1 px-2">RC/DS/CC</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">15</td><td class="py-1 px-2">RC/DS/CC + MAC</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">16</td><td class="py-1 px-2">RC/DS/CC + MAC + Cipher</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">19</td><td class="py-1 px-2">RC/DS/CC + MAC + Cipher + DS</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="text-sm mb-3">Refer to GlobalPlatform v2.3.1 and ETSI TS 102 226 §8.2.1.3.2 for the full GET STATUS P1/P2, GET DATA tag, DELETE P1, STORE DATA P1, and SET STATUS tables.</p>
|
||
|
||
<h3 id="conversion" class="text-lg font-medium mb-2">2.5 Conversion (SIM/USIM sidebars)</h3>
|
||
<ul class="list-disc list-inside text-sm space-y-1">
|
||
<li><strong>IMSI → EF.IMSI</strong> — 15-digit IMSI to 9-byte format (TS 31.102 §4.2.3).</li>
|
||
<li><strong>MSISDN → BCD</strong> — strip <code class="font-mono text-sm">+</code>, pad odd length with <code class="font-mono text-sm">f</code>, swap nibbles.</li>
|
||
<li><strong>ICCID → hex</strong> — nibble-swap the ICCID string.</li>
|
||
<li><strong>Provider Name → SPN</strong> — GSM 7-bit packed, UCS2 non-BMP, or UCS2 BMP (TS 31.102 §4.2.5, TS 23.038).</li>
|
||
<li><strong>PLMN → EF_PLMNsel / PLMNwAcT</strong> — 3-byte BCD + optional access technology selector.</li>
|
||
<li><strong>Nibble swap</strong> — swap nibble pairs of an even-length hex string.</li>
|
||
</ul>
|
||
|
||
<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 §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>
|
||
<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> (§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 > 83 > 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>
|
||
<li><strong>Store (SD admin params)</strong> — writes the same parameters as card (Security Domain) data via <strong>STORE DATA in TLV mode</strong> (<code class="font-mono text-sm">80 E2 90 00</code>, P1=90 = last block + BER-TLV per GP v2.2 Amendment B v1.1.3), wrapped in tag <code class="font-mono text-sm">85</code> (or <code class="font-mono text-sm">A5</code>) per Table 4-4.</li>
|
||
</ul>
|
||
<p class="text-sm mb-2">Sections mirror the spec tables:</p>
|
||
<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">Section</th><th class="text-left py-1 px-2">Tag</th><th class="text-left py-1 px-2">Contents</th></tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Connection parameters</td><td class="py-1 px-2 font-mono">84</td><td class="py-1 px-2">Any COMPREHENSION-TLV needed to open the TCP connection (OPEN CHANNEL per TS 102 223): Device Identities <code class="font-mono text-sm">02</code>, Alpha <code class="font-mono text-sm">80</code>, Bearer <code class="font-mono text-sm">01</code>, vendor TLVs. Row editor + presets, editable hex.</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Security parameters</td><td class="py-1 px-2 font-mono">85</td><td class="py-1 px-2">Table 4-6: LV PSK Identity (text), LV Key version/KID. Identifies the PSK TLS key (RFC 4279).</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Retry policy</td><td class="py-1 px-2 font-mono">86</td><td class="py-1 px-2">Table 4-7: retry counter (2 bytes, e.g. <code class="font-mono text-sm">B000</code>), retry waiting delay as the TS 102 223 timer TLV (<code class="font-mono text-sm">25 03 HH MM SS</code>), optional vendor-specific report-failure TLV.</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">HTTP POST</td><td class="py-1 px-2 font-mono">89</td><td class="py-1 px-2">Tables 4-8/9/10: Host header (<code class="font-mono text-sm">8A</code>), X-Admin-From agent ID (<code class="font-mono text-sm">8B</code>), URI (<code class="font-mono text-sm">8C</code>) — text converted to octets.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<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 §5.2.1) for TARs that process the expanded format (RAM-over-HTTP §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 — insert the TAR the SD listens on (typically the OTASD TAR) there.</p>
|
||
|
||
|
||
<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>
|
||
|
||
<h3 id="secured-packet" class="text-lg font-medium mb-2">3.1 Secured Packet</h3>
|
||
<p class="mb-2">Builds SCP80 secured packets per ETSI TS 102 225.</p>
|
||
<h4 id="packet-structure" class="font-medium mb-1">Packet structure</h4>
|
||
<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">Field</th><th class="text-left py-1 px-2">Size</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CPI</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Command Packet Identifier (02)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CPL</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Command Packet Length</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CHI</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Command Header Identifier (01)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CHL</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Command Header Length</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">SPI</td><td class="py-1 px-2">2</td><td class="py-1 px-2">Security Parameter Indicator</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">KIc</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Key Identifier for ciphering</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">KID</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Key Identifier for MAC</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">TAR</td><td class="py-1 px-2">3</td><td class="py-1 px-2">Toolkit Application Reference</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">CNTR</td><td class="py-1 px-2">5</td><td class="py-1 px-2">Replay counter</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">PCNTR</td><td class="py-1 px-2">1</td><td class="py-1 px-2">Padding counter</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">RC/CC/DS</td><td class="py-1 px-2">8</td><td class="py-1 px-2">Cryptographic Checksum / MAC</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">Secured Data</td><td class="py-1 px-2">var.</td><td class="py-1 px-2">Padded APDU (encrypted if required)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h4 id="packet-crypto" class="font-medium mb-1">Crypto</h4>
|
||
<ul class="list-disc list-inside text-sm space-y-1">
|
||
<li><strong>3DES-CBC</strong> encryption (zero ICV), 8/16/24-byte keys — deprecated since Rel-18, still supported for backwards compatibility</li>
|
||
<li><strong>AES-CBC</strong> encryption (zero ICV, zero-padded to 16), 16/24/32-byte keys (TS 102 225 §5.1.2, KIc <code class="font-mono text-sm">x2</code>)</li>
|
||
<li><strong>Retail MAC</strong> (ISO 9797-1 MAC algorithm 3) for the DES/3DES cryptographic checksum</li>
|
||
<li><strong>AES-CMAC</strong> (NIST SP 800-38B, truncated to 8 octets) for the AES cryptographic checksum (TS 102 225 §5.1.3.1, KID <code class="font-mono text-sm">x2</code>)</li>
|
||
<li>AES requires a replay-protected counter: SPI1 bits b5 b4 must be <code class="font-mono text-sm">10</code> (counter higher) or <code class="font-mono text-sm">11</code> (counter +1) per TS 102 225 §5.1.2/§5.1.3.1</li>
|
||
<li>Padding byte configurable (<code class="font-mono text-sm">00</code> default, or <code class="font-mono text-sm">FF</code>)</li>
|
||
</ul>
|
||
<p class="text-sm mb-3">A “Verify vs pySim” button cross-checks the assembled packet against pySim’s reference <code class="font-mono text-sm">OtaDialectSms.encode_cmd</code>. A “Send to Card” button delivers it via SMS-PP-DOWNLOAD ENVELOPE (when connected to the server). The returned Proof of Receipt is decoded and shown as a PoR status line (status, TAR, counter, raw PoR); the last command’s status word and response data are filled into the <strong>Response parser</strong> tab, and a successful PoR advances the replay counter and clears the packet.</p>
|
||
|
||
<h3 id="cards" class="text-lg font-medium mb-2">3.2 Cards</h3>
|
||
<p class="mb-2">Stores card presets locally in the browser (<code class="font-mono text-sm">localStorage</code>) so the Secured Packet view can auto-fill keys and parameters.</p>
|
||
<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">Field</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Name</td><td class="py-1 px-2">Human-readable label</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">ICCID</td><td class="py-1 px-2">Optional card identifier</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">KIc / KID</td><td class="py-1 px-2">Key and algorithm indicators (e.g. 15 = index 1, 3DES-CBC2; x2 = AES)</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">SPI1 / SPI2</td><td class="py-1 px-2">Security Parameter Indicators</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">TAR</td><td class="py-1 px-2">Toolkit Application Reference</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Counter</td><td class="py-1 px-2">Replay counter (5 bytes)</td></tr>
|
||
<tr><td class="py-1 px-2">KIc key / KID key</td><td class="py-1 px-2">16/24/32 hex chars (8/16/24-byte 3DES) or 32/48/64 hex chars (16/24/32-byte AES) keys</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="text-sm mb-3">Presets can be shared with <strong>Export as JSON</strong> and <strong>Export to file</strong>, and restored with <strong>Import from file</strong>, <strong>Paste & import</strong>, or <strong>Import JSON from clipboard</strong>. The selected card preset auto-fills the Secured Packet form.</p>
|
||
|
||
<h3 id="ram" class="text-lg font-medium mb-2">3.3 RAM</h3>
|
||
<p class="mb-2">Delivers Remote Application Management operations as SCP80 secured packets via SMS-PP-DOWNLOAD ENVELOPE. The card must support SCP03 (AES or 3DES). A saved card preset from the <strong>Cards</strong> sub-tab provides the SPI, keys, TAR, and counter.</p>
|
||
|
||
<h4 id="ram-operations" class="font-medium mb-1">Operations</h4>
|
||
<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">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||
<tbody>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item <strong>Delete</strong> buttons.</td></tr>
|
||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)].</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h4 id="ram-explorer" class="font-medium mb-1">Explorer View</h4>
|
||
<p class="text-sm mb-2">After “Explore Card” runs, the explorer displays:</p>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li><strong>ISD</strong> — AID, lifecycle, privileges (no delete; the ISD cannot be removed)</li>
|
||
<li><strong>Applications</strong> — AID, lifecycle, privileges, associated ELF/SD. Each has a <strong>Delete</strong> button (GP <code class="font-mono text-sm">DELETE</code> by AID).</li>
|
||
<li><strong>Executable Load Files</strong> — AID, lifecycle, version, module AIDs. Each has <strong>Delete</strong> (ELF only) and <strong>Delete All</strong> (cascade: ELF + modules + installed Applications, P2=<code class="font-mono text-sm">0x80</code>) buttons.</li>
|
||
</ul>
|
||
<p class="text-sm mb-3">Delete confirms via a browser prompt before sending the GP <code class="font-mono text-sm">DELETE</code> command via SCP80. The explorer auto-refreshes after a successful deletion.</p>
|
||
|
||
|
||
<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>
|
||
|
||
|
||
<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>
|
||
<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>
|
||
<li><strong>Edit</strong> — modify hex data, <strong>Save</strong> to write back (or <strong>Cancel</strong>)</li>
|
||
<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>
|
||
<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>
|
||
<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>
|
||
<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>
|
||
|
||
<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.</p>
|
||
|
||
<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>
|
||
<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>
|
||
|
||
<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) 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>
|
||
<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">
|
||
<li><strong>New profile</strong> — creates an empty ruleset after prompting for a name.</li>
|
||
<li><strong>Profile from card</strong> — scans the equipped card and generates one rule per existing file (see below), then opens the editor.</li>
|
||
<li><strong>Import profile</strong> — loads a ruleset from a JSON file (the name is stored inside the JSON).</li>
|
||
<li>Each profile row shows its name and creation time, with <strong>Check card ▶</strong>, <strong>Edit</strong>, <strong>Export</strong> (download JSON), and <strong>Delete</strong> actions.</li>
|
||
</ul>
|
||
<h4 class="font-medium mb-1">Filesystem rules</h4>
|
||
<p class="text-sm mb-2">Rules run sequentially. The editor shows the symbolic pySim name (when known) next to each rule’s path; use <strong>Add rule</strong> to append one and <strong>Save</strong> to keep the changes. A filesystem rule is defined by:</p>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li><strong>Path</strong> — starts with <code class="font-mono text-sm">MF</code> (e.g. <code class="font-mono text-sm">MF/7F10/6F3A</code>) or an ADF AID (e.g. <code class="font-mono text-sm">A0000000871002/6F07</code>).</li>
|
||
<li><strong>FCP/FCI check</strong> — how much of the file control information to verify: <strong>Filetype only (FCP)</strong> (existence + file type), <strong>Filetype + size (FCP)</strong> (adds file size, or record length/count for record files), or <strong>Exact FCI</strong> (adds a byte-for-byte comparison of the raw SELECT response — the FCP template <code class="font-mono text-sm">'62'</code> — catching FID/AID, life-cycle status, security-attribute and proprietary-parameter changes).</li>
|
||
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
||
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
||
</ul>
|
||
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match.</p>
|
||
<h4 class="font-medium mb-1">“Profile from card” scan options</h4>
|
||
<p class="text-sm mb-2">The scan dialog asks for a profile name and offers a <strong>“FCP/FCI check”</strong> selector (the same three modes above, default <strong>Filetype + size</strong>) applied to every generated rule, plus an <strong>“Ignore contents of files”</strong> checklist (all checked by default except <code class="font-mono text-sm">EF.ARR</code>; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Two further checked-by-default options <strong>“Match first 4 bytes for”</strong> <code class="font-mono text-sm">EF.IMSI</code> and <code class="font-mono text-sm">EF.ICCID</code> capture those files’ contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows <em>N / total files</em> with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the <strong>Custom files</strong> sub-tab are included under the same existence check.</p>
|
||
|
||
<h4 id="card-snapshots" class="font-medium mb-1">Card snapshots</h4>
|
||
<p class="text-sm mb-2">The list view shows two columns — <strong>Profiles</strong> and <strong>Card snapshots</strong>. A card snapshot is an immutable capture of the card filesystem: for every existing file it stores the path, symbolic name, file type, size (or record length/count), the raw FCI from the SELECT response, and the contents whenever the file is readable (no ignore list, no masking). The ICCID is decoded from EF.ICCID and shown next to the snapshot name.</p>
|
||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||
<li><strong>New snapshot</strong> — asks for a name and scans the card, then returns to the list.</li>
|
||
<li><strong>Import snapshot</strong> — loads a snapshot from a JSON file.</li>
|
||
<li>Each snapshot row has <strong>Open</strong>, <strong>Export</strong>, and <strong>Delete</strong>. <strong>Open</strong> shows all captured data read-only (raw FCI with the decoded FCI, contents); only the snapshot name is editable.</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 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>
|
||
|
||
<h3 id="prerequisites" class="text-lg font-medium mb-2">6.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>
|
||
<li><strong>Smart card reader</strong> (PC/SC or serial/FTDI). PC/SC is preferable; on Linux it requires <code class="font-mono text-sm">pcsc-lite</code> + <code class="font-mono text-sm">ccid</code></li>
|
||
<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>
|
||
<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>
|
||
<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>
|
||
<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>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">setup.sh / setup.bat</td><td class="py-1 px-2">Creates <code class="font-mono text-sm">.venv/</code>, installs pySim and the server. Run once after cloning.</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">start.sh / start.bat</td><td class="py-1 px-2">Starts the server from the venv (falls back to a global install).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="reader-autodetect" class="text-lg font-medium mb-2">6.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>
|
||
<li><strong>Server fallback</strong> — started without reader arguments, the server itself probes for a PC/SC reader at startup (3 attempts, 2 s apart)</li>
|
||
<li><strong>Serial readers</strong> — start the server manually with <code class="font-mono text-sm">-d /dev/ttyUSB0</code> (Linux)</li>
|
||
</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>
|
||
<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
|
||
# .venv\Scripts\activate # Windows
|
||
|
||
# Install pysim
|
||
pip install git+https://github.com/osmocom/pysim.git
|
||
|
||
# Install pysim-otaman-server (editable, so it serves the bundled PWA)
|
||
pip install -e .
|
||
|
||
# Start the server (serves PWA + API)
|
||
pysim-otaman-server --http-port 8080</pre>
|
||
<p class="text-sm mb-3">Connect a PC/SC reader with a SIM card, then open <code class="font-mono text-sm">http://127.0.0.1:8080</code> — the PWA and API share one origin, so no CORS is involved.</p>
|
||
<p class="text-sm mb-3">If the PWA is served from a public HTTPS host (e.g. <code class="font-mono text-sm">https://otaman.example.com</code>), two things are required to reach a local card server: (1) the server must answer the preflight with <code class="font-mono text-sm">Access-Control-Allow-Private-Network: true</code> (pysim-otaman-server ≥ 1.6.1 does this automatically), and (2) the browser must be allowed to access the local network — in Chrome/Edge/Vivaldi: Site settings → Local network access → allow the site (or accept the permission prompt). Without the browser permission, the request to <code class="font-mono text-sm">127.0.0.1</code> is blocked before any preflight is sent.</p>
|
||
|
||
|
||
<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>
|
||
<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>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2">✅ Compatible</td></tr>
|
||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">0.x.x</td><td class="py-1 px-2">❌ Outdated — update server</td></tr>
|
||
<tr><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">2.x.x+</td><td class="py-1 px-2">⚠️ Server newer — update PWA</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="text-sm">The PWA checks the server version on connect via <code class="font-mono text-sm">GET /api/version</code> and warns if versions are incompatible.</p>
|
||
|
||
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
if (localStorage.getItem('theme') === 'dark' ||
|
||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||
document.documentElement.classList.add('dark');
|
||
}
|
||
var toc = document.getElementById('toc');
|
||
var main = document.querySelector('main');
|
||
if (!toc || !main) return;
|
||
var headings = Array.prototype.filter.call(main.querySelectorAll('h2, h3, h4'), function (h) {
|
||
return /^\d/.test(h.textContent.trim());
|
||
});
|
||
var stack = [document.createElement('ol')];
|
||
stack[0].className = 'space-y-1 list-none';
|
||
var lastLevel = 2;
|
||
headings.forEach(function (h) {
|
||
var level = parseInt(h.tagName.charAt(1), 10);
|
||
var li = document.createElement('li');
|
||
var a = document.createElement('a');
|
||
a.href = '#' + h.id;
|
||
a.textContent = h.textContent;
|
||
li.appendChild(a);
|
||
if (level > lastLevel) {
|
||
var ul = document.createElement('ul');
|
||
ul.className = 'pl-4 mt-1 space-y-1 list-none';
|
||
var parent = stack[stack.length - 1].lastElementChild;
|
||
parent.appendChild(ul);
|
||
stack.push(ul);
|
||
} else if (level < lastLevel) {
|
||
while (stack.length > 1 && level < lastLevel) { stack.pop(); lastLevel--; }
|
||
}
|
||
stack[stack.length - 1].appendChild(li);
|
||
lastLevel = level;
|
||
});
|
||
toc.appendChild(stack[0]);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|