v3.14.0: event form object-set fixes + the slices single-envelope mandate
An audit of every event form against the pinned specs (31.111 7.5.x/9.3, 102 223 8.87, TS 101 220's master tag table) found three missing objects, three malformed/wrong ones, and settled the slice-data scope. - 0x00 MT call: the IMS URI object is cut at 100 bytes and now carries the URI truncated object (8.135, F3 00) when the cut happened. - 0x1F Slices: the form gains the Partial NSSAI object (8.154, F9, rel 18); a single envelope carrying it is marked as the Last Envelope (8.79, F0 00) automatically (7.5.27.2). - 0x06 Card reader status and 0x07 Language: the builders now emit their object tags (A0 01 ..., AD ...) - they emitted bare values before. - 0x18 Incoming IMS data: the IARI tag is F6 (76/F6), not F7 (which is the IMPU list tag - the two-column 9.3 table had been misread). - Single-envelope mandate: the event data is capped at 244 bytes (inner 252; the D6 long-form length and the APDU Lc stay in one byte). The server refuses more with a 400 and the PWA shows the size error before sending. Chained (multi-envelope) delivery, the More Data object and envelope sequences are not implemented - documented in the help and the repo AGENTS. - Tests: event form vectors (URI truncation, the restored tags, partial / Last Envelope, the size helper) and the server guard; the release reference and its PWA/code mirrors gain the three objects and the corrected IARI key. - sw cache simple-v375.
This commit is contained in:
@@ -507,11 +507,11 @@
|
|||||||
<li><strong>События звонка</strong> — MT call (transaction identifier, номер вызывающего + TON → Address, subaddress, IMS URI, media type; источник <em>network</em>), Call connected и Call disconnected (transaction identifier, источник near/far, media type и причина — отсутствует, нулевая форма radio link timeout или свои байты), а также необязательная причина IMS-разрыва (SIP / Q.850 + cause)</li>
|
<li><strong>События звонка</strong> — MT call (transaction identifier, номер вызывающего + TON → Address, subaddress, IMS URI, media type; источник <em>network</em>), Call connected и Call disconnected (transaction identifier, источник near/far, media type и причина — отсутствует, нулевая форма radio link timeout или свои байты), а также необязательная причина IMS-разрыва (SIP / Q.850 + cause)</li>
|
||||||
<li><strong>Browser termination</strong> (причина user / error), <strong>(I-)WLAN access status</strong> (нет покрытия / покрытие / соединение) и <strong>CSG cell selection</strong> (статус + механизм выбора, access technology, CSG ID, HNB name, PLMN ID; источник <em>network</em>)</li>
|
<li><strong>Browser termination</strong> (причина user / error), <strong>(I-)WLAN access status</strong> (нет покрытия / покрытие / соединение) и <strong>CSG cell selection</strong> (статус + механизм выбора, access technology, CSG ID, HNB name, PLMN ID; источник <em>network</em>)</li>
|
||||||
<li><strong>Channel status</strong> (канал + состояние звена + инфо, тег <code class="font-mono text-sm">B8</code>, с необязательными bearer description <code class="font-mono text-sm">B5</code> и local address <code class="font-mono text-sm">BE</code>) и <strong>Data available</strong> (тот же channel status + длина данных канала, тег <code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (строки/столбцы + флаги sizing/fonts/effects, тег <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, тег <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (код ошибки браузера, тег <code class="font-mono text-sm">E4</code>) и <strong>Frames information change</strong> (фрейм по умолчанию + инфо по фреймам, тег <code class="font-mono text-sm">E7</code>)</li>
|
<li><strong>Channel status</strong> (канал + состояние звена + инфо, тег <code class="font-mono text-sm">B8</code>, с необязательными bearer description <code class="font-mono text-sm">B5</code> и local address <code class="font-mono text-sm">BE</code>) и <strong>Data available</strong> (тот же channel status + длина данных канала, тег <code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (строки/столбцы + флаги sizing/fonts/effects, тег <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, тег <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (код ошибки браузера, тег <code class="font-mono text-sm">E4</code>) и <strong>Frames information change</strong> (фрейм по умолчанию + инфо по фреймам, тег <code class="font-mono text-sm">E7</code>)</li>
|
||||||
<li><strong>IMS registration</strong> (IMPU URI + IMS status code) и <strong>Incoming IMS data</strong> (IARI) — источник <em>network</em>; <strong>CAG cell selection</strong> (статус + механизм, access technology, CAG information list + HRNN list) и <strong>Slices status change</strong> (allowed / rejected / served / partially, access technology, списки allowed / served / rejected S-NSSAI, с маппингом и без) — источник <em>network</em></li>
|
<li><strong>IMS registration</strong> (IMPU URI + IMS status code) и <strong>Incoming IMS data</strong> (IARI) — источник <em>network</em>; <strong>CAG cell selection</strong> (статус + механизм, access technology, CAG information list + HRNN list) и <strong>Slices status change</strong> (allowed / rejected / served / partially, access technology, списки allowed / served / rejected S-NSSAI, с маппингом и без, partial NSSAI) — источник <em>network</em></li>
|
||||||
<li><strong>Data Connection Status Change</strong> (0x1D) — статус (successful / rejected / dropped-deactivated), тип соединения (PDP / PDN / PDU), необязательная причина (E/5G)SM, transaction identifier и location status, а также необязательные date-time, access technology, APN, тип PDP/PDN/PDU и location information (MCC/MNC + LAC + Cell ID) — TS 131 111 §7.5.25</li>
|
<li><strong>Data Connection Status Change</strong> (0x1D) — статус (successful / rejected / dropped-deactivated), тип соединения (PDP / PDN / PDU), необязательная причина (E/5G)SM, transaction identifier и location status, а также необязательные date-time, access technology, APN, тип PDP/PDN/PDU и location information (MCC/MNC + LAC + Cell ID) — TS 131 111 §7.5.25</li>
|
||||||
<li><strong>Poll Interval Negotiation</strong> (0x1C) — предлагает интервал опроса в виде Duration (единица + значение); после отправки показывается ответ UICC (принято / отклонено / изменено + длительность), а значение <em>изменено</em> становится интервалом фонового опроса (TS 102 223 §7.5.22). Форма появляется, когда карта подписана на это событие.</li>
|
<li><strong>Poll Interval Negotiation</strong> (0x1C) — предлагает интервал опроса в виде Duration (единица + значение); после отправки показывается ответ UICC (принято / отклонено / изменено + длительность), а значение <em>изменено</em> становится интервалом фонового опроса (TS 102 223 §7.5.22). Форма появляется, когда карта подписана на это событие.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-sm mb-3">Отправка события использует <code class="font-mono text-sm">ENVELOPE(Event Download)</code> по TS 102 223 / TS 131 111. Форма скрывает параметры, появившиеся после <strong>действующего релиза CAT</strong> — релиза пресета подключённой карты (совпадение по ICCID) или Rel-12, если он не указан, — а примечание под формой называет источник и то, что было скрыто (релиз задаётся на вкладке «Карты»).</p>
|
<p class="text-sm mb-3">Отправка события использует <code class="font-mono text-sm">ENVELOPE(Event Download)</code> по TS 102 223 / TS 131 111. Форма скрывает параметры, появившиеся после <strong>действующего релиза CAT</strong> — релиза пресета подключённой карты (совпадение по ICCID) или Rel-12, если он не указан, — а примечание под формой называет источник и то, что было скрыто (релиз задаётся на вкладке «Карты»). Информация о срезах отправляется в одном <code class="font-mono text-sm">ENVELOPE</code> (не более 252 байт внутренних данных) — доставка цепочкой из нескольких ENVELOPE не реализована, поэтому данные должны помещаться в один конверт; конверт с partial NSSAI автоматически помечается как Last Envelope.</p>
|
||||||
|
|
||||||
<h3 id="proactive-log" class="text-lg font-medium mb-2">8.3 Журнал проактивных команд</h3>
|
<h3 id="proactive-log" class="text-lg font-medium mb-2">8.3 Журнал проактивных команд</h3>
|
||||||
<p class="text-sm mb-2">Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Раскрытие строки показывает декодированную команду: текст и пункты DISPLAY TEXT / SELECT ITEM / SET UP MENU, параметры BIP-каналов, действия TIMER MANAGEMENT, названия квалификаторов PROVIDE LOCAL INFORMATION (все стандартные, включая ESN 07, MEID 0B и Supported RATs 1A) и SEND SHORT MESSAGE — разбирается SMS TPDU (тип, TP-MR, TP-DA, TP-PID, TP-DCS, срок действия, TP-UDL), а пользовательские данные показываются как текст для текстовых кодировок (GSM 7-бит, UCS2, 8-бит) или как secured-пакет (TS 31.115) при TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); заголовки конкатенации UDH декодируются. Для команд с данными ответа показывается строка <code class="font-mono text-sm">Ответ:</code> с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.</p>
|
<p class="text-sm mb-2">Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Раскрытие строки показывает декодированную команду: текст и пункты DISPLAY TEXT / SELECT ITEM / SET UP MENU, параметры BIP-каналов, действия TIMER MANAGEMENT, названия квалификаторов PROVIDE LOCAL INFORMATION (все стандартные, включая ESN 07, MEID 0B и Supported RATs 1A) и SEND SHORT MESSAGE — разбирается SMS TPDU (тип, TP-MR, TP-DA, TP-PID, TP-DCS, срок действия, TP-UDL), а пользовательские данные показываются как текст для текстовых кодировок (GSM 7-бит, UCS2, 8-бит) или как secured-пакет (TS 31.115) при TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); заголовки конкатенации UDH декодируются. Для команд с данными ответа показывается строка <code class="font-mono text-sm">Ответ:</code> с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.</p>
|
||||||
|
|||||||
+2
-2
@@ -506,11 +506,11 @@
|
|||||||
<li><strong>Call events</strong> — MT call (transaction identifier, calling party number + TON → Address, subaddress, IMS URI, media type; source <em>network</em>), Call connected and Call disconnected (transaction identifier, near/far-end source, media type, and a cause — absent, the zero-length radio-link-timeout form, or custom bytes), plus the optional IMS disconnection cause (SIP / Q.850 + cause)</li>
|
<li><strong>Call events</strong> — MT call (transaction identifier, calling party number + TON → Address, subaddress, IMS URI, media type; source <em>network</em>), Call connected and Call disconnected (transaction identifier, near/far-end source, media type, and a cause — absent, the zero-length radio-link-timeout form, or custom bytes), plus the optional IMS disconnection cause (SIP / Q.850 + cause)</li>
|
||||||
<li><strong>Browser termination</strong> (user / error cause), <strong>(I-)WLAN access status</strong> (no coverage / coverage / connection on-going) and <strong>CSG cell selection</strong> (status + selection mechanism, access technology, CSG ID, HNB name, PLMN ID; source <em>network</em>)</li>
|
<li><strong>Browser termination</strong> (user / error cause), <strong>(I-)WLAN access status</strong> (no coverage / coverage / connection on-going) and <strong>CSG cell selection</strong> (status + selection mechanism, access technology, CSG ID, HNB name, PLMN ID; source <em>network</em>)</li>
|
||||||
<li><strong>Channel status</strong> (channel + link state + channel info, tag <code class="font-mono text-sm">B8</code>, with an optional bearer description <code class="font-mono text-sm">B5</code> and local address <code class="font-mono text-sm">BE</code>) and <strong>Data available</strong> (the same channel status plus the channel data length, tag <code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (rows/columns + sizing/fonts/effects flags, tag <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, tag <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (browser error code, tag <code class="font-mono text-sm">E4</code>) and <strong>Frames information change</strong> (default frame + per-frame info, tag <code class="font-mono text-sm">E7</code>)</li>
|
<li><strong>Channel status</strong> (channel + link state + channel info, tag <code class="font-mono text-sm">B8</code>, with an optional bearer description <code class="font-mono text-sm">B5</code> and local address <code class="font-mono text-sm">BE</code>) and <strong>Data available</strong> (the same channel status plus the channel data length, tag <code class="font-mono text-sm">B7</code>), <strong>Display parameters changed</strong> (rows/columns + sizing/fonts/effects flags, tag <code class="font-mono text-sm">C0</code>), <strong>Network search mode change</strong> (manual/automatic, tag <code class="font-mono text-sm">E5</code>), <strong>Browsing status</strong> (browser error code, tag <code class="font-mono text-sm">E4</code>) and <strong>Frames information change</strong> (default frame + per-frame info, tag <code class="font-mono text-sm">E7</code>)</li>
|
||||||
<li><strong>IMS registration</strong> (IMPU URIs + IMS status code) and <strong>Incoming IMS data</strong> (IARI) — source <em>network</em>; <strong>CAG cell selection</strong> (status + mechanism, access technology, CAG information list + HRNN list) and <strong>Slices status change</strong> (allowed / rejected / served / partially status, access technology, allowed / served / rejected S-NSSAI lists, with and without mapping) — source <em>network</em></li>
|
<li><strong>IMS registration</strong> (IMPU URIs + IMS status code) and <strong>Incoming IMS data</strong> (IARI) — source <em>network</em>; <strong>CAG cell selection</strong> (status + mechanism, access technology, CAG information list + HRNN list) and <strong>Slices status change</strong> (allowed / rejected / served / partially status, access technology, allowed / served / rejected S-NSSAI lists, with and without mapping, partial NSSAI) — source <em>network</em></li>
|
||||||
<li><strong>Data Connection Status Change</strong> (0x1D) — status (successful / rejected / dropped-deactivated), connection type (PDP / PDN / PDU), optional (E/5G)SM cause, transaction identifier and location status, plus optional date-time, access technology, APN, PDP/PDN/PDU type and location information (MCC/MNC + LAC + Cell ID) — TS 131 111 §7.5.25</li>
|
<li><strong>Data Connection Status Change</strong> (0x1D) — status (successful / rejected / dropped-deactivated), connection type (PDP / PDN / PDU), optional (E/5G)SM cause, transaction identifier and location status, plus optional date-time, access technology, APN, PDP/PDN/PDU type and location information (MCC/MNC + LAC + Cell ID) — TS 131 111 §7.5.25</li>
|
||||||
<li><strong>Poll Interval Negotiation</strong> (0x1C) — proposes a poll interval as a Duration (unit + value); after sending, the UICC's answer is shown (accepted / rejected / modified + duration), and a <em>modified</em> duration becomes the background poll interval (TS 102 223 §7.5.22). The form appears when the card subscribed to this event.</li>
|
<li><strong>Poll Interval Negotiation</strong> (0x1C) — proposes a poll interval as a Duration (unit + value); after sending, the UICC's answer is shown (accepted / rejected / modified + duration), and a <em>modified</em> duration becomes the background poll interval (TS 102 223 §7.5.22). The form appears when the card subscribed to this event.</li>
|
||||||
</ul>
|
</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. The form hides the parameters introduced after the <strong>effective CAT release</strong> — the equipped card preset's <code class="font-mono text-sm">CAT release</code> (matched by ICCID), or Rel-12 when unspecified — and a note under the form names the source and what was hidden (the release is set in the Cards tab).</p>
|
<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. The form hides the parameters introduced after the <strong>effective CAT release</strong> — the equipped card preset's <code class="font-mono text-sm">CAT release</code> (matched by ICCID), or Rel-12 when unspecified — and a note under the form names the source and what was hidden (the release is set in the Cards tab). Slice information is sent in a single <code class="font-mono text-sm">ENVELOPE</code> (max 252 bytes of inner data) — chained (multi-envelope) delivery is not implemented, so the data must fit one envelope; an envelope carrying the partial NSSAI is marked as the Last Envelope automatically.</p>
|
||||||
|
|
||||||
<h3 id="proactive-log" class="text-lg font-medium mb-2">8.3 Proactive command log</h3>
|
<h3 id="proactive-log" class="text-lg font-medium mb-2">8.3 Proactive command log</h3>
|
||||||
<p class="text-sm mb-2">Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Expanding a row shows the decoded command: DISPLAY TEXT / SELECT ITEM / SET UP MENU text and items, BIP channel parameters, TIMER MANAGEMENT actions, PROVIDE LOCAL INFORMATION qualifier names (all standard qualifiers, including ESN 07, MEID 0B and Supported RATs 1A), and SEND SHORT MESSAGE — the SMS TPDU is parsed (type, TP-MR, TP-DA, TP-PID, TP-DCS, validity period, TP-UDL) with the user data shown as text for text codings (GSM 7-bit, UCS2, 8-bit) or as a secured packet (TS 31.115) when TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); UDH concatenation headers are decoded. 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>
|
<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). Expanding a row shows the decoded command: DISPLAY TEXT / SELECT ITEM / SET UP MENU text and items, BIP channel parameters, TIMER MANAGEMENT actions, PROVIDE LOCAL INFORMATION qualifier names (all standard qualifiers, including ESN 07, MEID 0B and Supported RATs 1A), and SEND SHORT MESSAGE — the SMS TPDU is parsed (type, TP-MR, TP-DA, TP-PID, TP-DCS, validity period, TP-UDL) with the user data shown as text for text codings (GSM 7-bit, UCS2, 8-bit) or as a secured packet (TS 31.115) when TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); UDH concatenation headers are decoded. 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>
|
||||||
|
|||||||
+46
-6
@@ -1877,7 +1877,7 @@
|
|||||||
// ===== Version =====
|
// ===== Version =====
|
||||||
// Single source of truth for the PWA version: shown in the header and used
|
// Single source of truth for the PWA version: shown in the header and used
|
||||||
// by the server version check in pysimConnect().
|
// by the server version check in pysimConnect().
|
||||||
const SIMPLE_VERSION = '3.13.4';
|
const SIMPLE_VERSION = '3.14.0';
|
||||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||||
|
|
||||||
// ===== Tab switching =====
|
// ===== Tab switching =====
|
||||||
@@ -9136,10 +9136,11 @@ const CAT_RELEASE = {
|
|||||||
'56/D6 CSG ID': 9,
|
'56/D6 CSG ID': 9,
|
||||||
'57/D7 HNB name': 9,
|
'57/D7 HNB name': 9,
|
||||||
'31/B1 IMS URI': 10,
|
'31/B1 IMS URI': 10,
|
||||||
'77/F7 IARI': 10,
|
'76/F6 IARI': 10,
|
||||||
'77/F7 IMPU list': 11,
|
'77/F7 IMPU list': 11,
|
||||||
'78/F8 IMS status code': 11,
|
'78/F8 IMS status code': 11,
|
||||||
'7E/FE Media Type': 12,
|
'7E/FE Media Type': 12,
|
||||||
|
'73/F3 URI truncated': 12,
|
||||||
'09/89 PLMN ID': 4,
|
'09/89 PLMN ID': 4,
|
||||||
'3F/BF Access technology': 4,
|
'3F/BF Access technology': 4,
|
||||||
'B4 Supported Radio Access Technologies': 13,
|
'B4 Supported Radio Access Technologies': 13,
|
||||||
@@ -9159,6 +9160,8 @@ const CAT_RELEASE = {
|
|||||||
'77/F7 Allowed slices with S-NSSAI mapping': 18,
|
'77/F7 Allowed slices with S-NSSAI mapping': 18,
|
||||||
'D7 Rejected slices with S-NSSAI mapping': 18,
|
'D7 Rejected slices with S-NSSAI mapping': 18,
|
||||||
'B1 Rejected slices information': 18,
|
'B1 Rejected slices information': 18,
|
||||||
|
'79/F9 Partial NSSAI': 18,
|
||||||
|
'70/F0 Last Envelope': 18,
|
||||||
},
|
},
|
||||||
pli: {
|
pli: {
|
||||||
'00': 4, '01': 4, '02': 4, '03': 4, '04': 4, '06': 4, '07': 4, '08': 6,
|
'00': 4, '01': 4, '02': 4, '03': 4, '04': 4, '06': 4, '07': 4, '08': 6,
|
||||||
@@ -13897,8 +13900,12 @@ const EVENT_FORMS = {
|
|||||||
d += '88' + (h.length / 2).toString(16).padStart(2, '0') + h;
|
d += '88' + (h.length / 2).toString(16).padStart(2, '0') + h;
|
||||||
}
|
}
|
||||||
if (v.ims_uri) {
|
if (v.ims_uri) {
|
||||||
const b = new TextEncoder().encode(v.ims_uri).slice(0, 100);
|
let b = new TextEncoder().encode(v.ims_uri);
|
||||||
|
const truncated = b.length > 100;
|
||||||
|
b = b.slice(0, 100);
|
||||||
d += 'B1' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b));
|
d += 'B1' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b));
|
||||||
|
// 8.135: the object is included when the URI was truncated
|
||||||
|
if (truncated) d += 'F300';
|
||||||
}
|
}
|
||||||
if (v.media !== '' && v.media !== undefined)
|
if (v.media !== '' && v.media !== undefined)
|
||||||
d += 'FE01' + ((parseInt(v.media) || 0) & 0xFF).toString(16).padStart(2, '0');
|
d += 'FE01' + ((parseInt(v.media) || 0) & 0xFF).toString(16).padStart(2, '0');
|
||||||
@@ -13973,13 +13980,13 @@ const EVENT_FORMS = {
|
|||||||
{ id: 'status', label: 'Status', type: 'select',
|
{ id: 'status', label: 'Status', type: 'select',
|
||||||
opts: [{v:0,l:'Card reader removed'},{v:1,l:'Card reader inserted'}] }
|
opts: [{v:0,l:'Card reader removed'},{v:1,l:'Card reader inserted'}] }
|
||||||
],
|
],
|
||||||
build(v) { return '' + (v.status|0).toString(16).padStart(2,'0'); }
|
build(v) { return 'A001' + ((parseInt(v.status) || 0) & 0xFF).toString(16).padStart(2,'0'); }
|
||||||
},
|
},
|
||||||
0x07: { title: 'Language selection',
|
0x07: { title: 'Language selection',
|
||||||
fields: [
|
fields: [
|
||||||
{ id: 'lang', label: 'Language code', type: 'text', placeholder: 'ISO 639, e.g. en, ru', pattern: '[a-zA-Z]{2,3}' }
|
{ id: 'lang', label: 'Language code', type: 'text', placeholder: 'ISO 639, e.g. en, ru', pattern: '[a-zA-Z]{2,3}' }
|
||||||
],
|
],
|
||||||
build(v) { const b = new TextEncoder().encode(v.lang||'en'); return b.length.toString(16).padStart(2,'0') + bytesToHex(Array.from(b)); }
|
build(v) { const b = new TextEncoder().encode(v.lang||'en'); return 'AD' + b.length.toString(16).padStart(2,'0') + bytesToHex(Array.from(b)); }
|
||||||
},
|
},
|
||||||
0x08: { title: 'Browser termination',
|
0x08: { title: 'Browser termination',
|
||||||
fields: [
|
fields: [
|
||||||
@@ -14240,7 +14247,7 @@ const EVENT_FORMS = {
|
|||||||
build(v) {
|
build(v) {
|
||||||
if (!v.iari) return '';
|
if (!v.iari) return '';
|
||||||
const b = new TextEncoder().encode(v.iari).slice(0, 100);
|
const b = new TextEncoder().encode(v.iari).slice(0, 100);
|
||||||
return ('F7' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b))).toUpperCase();
|
return ('F6' + b.length.toString(16).padStart(2, '0') + bytesToHex(Array.from(b))).toUpperCase();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x1C: { title: 'Poll interval negotiation',
|
0x1C: { title: 'Poll interval negotiation',
|
||||||
@@ -14357,6 +14364,7 @@ const EVENT_FORMS = {
|
|||||||
{ id: 'allowed', label: 'Allowed slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{8,80}', placeholder: '4 bytes per S-NSSAI (SST + SD)' },
|
{ id: 'allowed', label: 'Allowed slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{8,80}', placeholder: '4 bytes per S-NSSAI (SST + SD)' },
|
||||||
{ id: 'rejected_map', label: 'Rejected slices with S-NSSAI mapping (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 elements + rejection cause', rel: 18 },
|
{ id: 'rejected_map', label: 'Rejected slices with S-NSSAI mapping (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 elements + rejection cause', rel: 18 },
|
||||||
{ id: 'rejected', label: 'Rejected slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 rejected S-NSSAI elements', rel: 18 },
|
{ id: 'rejected', label: 'Rejected slices (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 rejected S-NSSAI elements', rel: 18 },
|
||||||
|
{ id: 'partial', label: 'Partial NSSAI (optional, hex)', type: 'text', pattern: '[0-9A-Fa-f]{2,80}', placeholder: 'TS 24.501 partial NSSAI elements', rel: 18 },
|
||||||
],
|
],
|
||||||
build(v) {
|
build(v) {
|
||||||
let d = '';
|
let d = '';
|
||||||
@@ -14377,6 +14385,11 @@ const EVENT_FORMS = {
|
|||||||
list(v.allowed, 'F8');
|
list(v.allowed, 'F8');
|
||||||
list(v.rejected_map, 'D7');
|
list(v.rejected_map, 'D7');
|
||||||
list(v.rejected, 'B1');
|
list(v.rejected, 'B1');
|
||||||
|
list(v.partial, 'F9');
|
||||||
|
// 7.5.27.2: a single envelope carrying the partial NSSAI is the
|
||||||
|
// last one - the chaining marker goes with it (multi-envelope
|
||||||
|
// delivery is not implemented; the data must fit one envelope)
|
||||||
|
if ((v.partial || '').replace(/[^0-9a-fA-F]/g, '')) d += 'F000';
|
||||||
return d.toUpperCase();
|
return d.toUpperCase();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -14799,6 +14812,24 @@ function eventFieldsForRelease(cfg, rel) {
|
|||||||
return { fields: fields, hidden: hidden };
|
return { fields: fields, hidden: hidden };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The single-ENVELOPE budget: the inner data (event list + device identities +
|
||||||
|
// the event objects) is wrapped as D6 81 <len> + inner, and the APDU Lc must
|
||||||
|
// stay within one byte (255) - so the inner data is capped at 252 bytes
|
||||||
|
// (8 header bytes + at most 244 bytes of event data). Chained
|
||||||
|
// (multi-envelope) delivery is not implemented: the slice/NSSAI data must fit
|
||||||
|
// one envelope (see the help).
|
||||||
|
const EVENT_INNER_MAX = 252;
|
||||||
|
|
||||||
|
// The ENVELOPE inner size of an event-data hex string (pure).
|
||||||
|
function eventEnvelopeSize(eventDataHex) {
|
||||||
|
return 8 + Math.floor((eventDataHex || '').length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the event data fits one ENVELOPE (pure).
|
||||||
|
function eventFitsOneEnvelope(eventDataHex) {
|
||||||
|
return eventEnvelopeSize(eventDataHex) <= EVENT_INNER_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
function pysimEventSendForm(eventType) {
|
function pysimEventSendForm(eventType) {
|
||||||
const modal = document.getElementById('event-send-modal');
|
const modal = document.getElementById('event-send-modal');
|
||||||
const title = document.getElementById('event-send-title');
|
const title = document.getElementById('event-send-title');
|
||||||
@@ -14925,6 +14956,13 @@ async function pysimEventSend() {
|
|||||||
err.classList.remove('hidden');
|
err.classList.remove('hidden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (eventData && !eventFitsOneEnvelope(eventData)) {
|
||||||
|
err.textContent = t('The event data does not fit one ENVELOPE') + ' (' +
|
||||||
|
eventEnvelopeSize(eventData) + ' > ' + EVENT_INNER_MAX + ' B). ' +
|
||||||
|
t('Multi-envelope (chained) delivery is not implemented - reduce the slice lists.');
|
||||||
|
err.classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
@@ -20738,6 +20776,8 @@ const LANG_RU = {
|
|||||||
'Event sent.': 'Событие отправлено.',
|
'Event sent.': 'Событие отправлено.',
|
||||||
'Event data sent.': 'Данные события отправлены.',
|
'Event data sent.': 'Данные события отправлены.',
|
||||||
'Sending...': 'Отправка...',
|
'Sending...': 'Отправка...',
|
||||||
|
'The event data does not fit one ENVELOPE': 'Данные события не помещаются в один ENVELOPE',
|
||||||
|
'Multi-envelope (chained) delivery is not implemented - reduce the slice lists.': 'Доставка в несколько ENVELOPE (цепочкой) не реализована — сократите списки срезов.',
|
||||||
'Yes': 'Да',
|
'Yes': 'Да',
|
||||||
'No': 'Нет',
|
'No': 'Нет',
|
||||||
'Help': 'Справка',
|
'Help': 'Справка',
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simple-v374';
|
const CACHE = 'simple-v375';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ eval(extractFunc(html, 'cmdQualifierShort'));
|
|||||||
eval(extractBlock('const EVENT_NAMES = {', 'const REJECTION_CAUSES = [').replace(/^const /gm, 'var '));
|
eval(extractBlock('const EVENT_NAMES = {', 'const REJECTION_CAUSES = [').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const REJECTION_CAUSES = [', 'const EVENT_FORMS = {').replace(/^const /gm, 'var '));
|
eval(extractBlock('const REJECTION_CAUSES = [', 'const EVENT_FORMS = {').replace(/^const /gm, 'var '));
|
||||||
eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var '));
|
eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var '));
|
||||||
|
eval(extractBlock('const EVENT_INNER_MAX = ', 'function pysimEventSendForm').replace(/^const /gm, 'var '));
|
||||||
eval(extractFunc(html, 'eventFieldsForRelease'));
|
eval(extractFunc(html, 'eventFieldsForRelease'));
|
||||||
eval(extractFunc(html, 'encPlmn'));
|
eval(extractFunc(html, 'encPlmn'));
|
||||||
eval(extractFunc(html, 'bytesToHex'));
|
eval(extractFunc(html, 'bytesToHex'));
|
||||||
@@ -172,6 +173,11 @@ test('the call events build their 31.111 object sets', () => {
|
|||||||
// call disconnected: radio link timeout = zero-length cause, custom = bytes
|
// call disconnected: radio link timeout = zero-length cause, custom = bytes
|
||||||
assert.strictEqual(EVENT_FORMS[0x02].build({ ti: '00', cause_mode: 'rlt', media: '' }), '1C01009A00');
|
assert.strictEqual(EVENT_FORMS[0x02].build({ ti: '00', cause_mode: 'rlt', media: '' }), '1C01009A00');
|
||||||
assert.strictEqual(EVENT_FORMS[0x02].build({ ti: '00', cause_mode: 'custom', cause: '10', media: '' }), '1C01009A0110');
|
assert.strictEqual(EVENT_FORMS[0x02].build({ ti: '00', cause_mode: 'custom', cause: '10', media: '' }), '1C01009A0110');
|
||||||
|
// a URI longer than the 100-byte limit is cut and tagged (8.135)
|
||||||
|
const long = EVENT_FORMS[0x00].build({ ti: '00', ims_uri: 'x'.repeat(120) });
|
||||||
|
assert.ok(long.includes('B164' + '78'.repeat(100)), 'the URI is cut to 100 bytes');
|
||||||
|
assert.ok(long.endsWith('F300'), long.slice(-8));
|
||||||
|
assert.ok(!EVENT_FORMS[0x00].build({ ti: '00', ims_uri: 'sip:a@b' }).includes('F300'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the browser, WLAN and CSG events build their object sets', () => {
|
test('the browser, WLAN and CSG events build their object sets', () => {
|
||||||
@@ -199,6 +205,9 @@ test('the data/display/search/frames events build their object sets', () => {
|
|||||||
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '12' }), 'E40112');
|
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '12' }), 'E40112');
|
||||||
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '' }), 'E40100');
|
assert.strictEqual(EVENT_FORMS[0x0F].build({ status: '' }), 'E40100');
|
||||||
assert.strictEqual(EVENT_FORMS[0x10].build({ frame: '01', list: '0410' }), 'E703010410');
|
assert.strictEqual(EVENT_FORMS[0x10].build({ frame: '01', list: '0410' }), 'E703010410');
|
||||||
|
// Card reader status (8.33) and Language (8.45) carry their object tags
|
||||||
|
assert.strictEqual(EVENT_FORMS[0x06].build({ status: '1' }), 'A00101');
|
||||||
|
assert.strictEqual(EVENT_FORMS[0x07].build({ lang: 'en' }), 'AD02656E');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the IMS, CAG and slices events build their object sets', () => {
|
test('the IMS, CAG and slices events build their object sets', () => {
|
||||||
@@ -207,7 +216,7 @@ test('the IMS, CAG and slices events build their object sets', () => {
|
|||||||
assert.strictEqual(EVENT_FORMS[0x17].build({ impu: 'sip:a@b', code: '200' }),
|
assert.strictEqual(EVENT_FORMS[0x17].build({ impu: 'sip:a@b', code: '200' }),
|
||||||
'F70980077369703A614062F803323030');
|
'F70980077369703A614062F803323030');
|
||||||
assert.strictEqual(EVENT_FORMS[0x18].src, '83');
|
assert.strictEqual(EVENT_FORMS[0x18].src, '83');
|
||||||
assert.strictEqual(EVENT_FORMS[0x18].build({ iari: 'a' }), 'F70161');
|
assert.strictEqual(EVENT_FORMS[0x18].build({ iari: 'a' }), 'F60161');
|
||||||
// CAG cell selection: tech + status/mechanism + information list
|
// CAG cell selection: tech + status/mechanism + information list
|
||||||
assert.strictEqual(EVENT_FORMS[0x1E].src, '83');
|
assert.strictEqual(EVENT_FORMS[0x1E].src, '83');
|
||||||
assert.strictEqual(EVENT_FORMS[0x1E].build({ status: '1', mech: 'manual', tech: '8', list: '00000001' }),
|
assert.strictEqual(EVENT_FORMS[0x1E].build({ status: '1', mech: 'manual', tech: '8', list: '00000001' }),
|
||||||
@@ -216,6 +225,9 @@ test('the IMS, CAG and slices events build their object sets', () => {
|
|||||||
assert.strictEqual(EVENT_FORMS[0x1F].src, '83');
|
assert.strictEqual(EVENT_FORMS[0x1F].src, '83');
|
||||||
assert.strictEqual(EVENT_FORMS[0x1F].build({ status: '1', tech: '8', served: '01020304' }),
|
assert.strictEqual(EVENT_FORMS[0x1F].build({ status: '1', tech: '8', served: '01020304' }),
|
||||||
'BF0108D50101D6050101020304');
|
'BF0108D50101D6050101020304');
|
||||||
|
// partial NSSAI: the F9 object + the automatic Last Envelope marker
|
||||||
|
assert.strictEqual(EVENT_FORMS[0x1F].build({ status: '1', partial: '0102' }), 'D50101F9020102F000');
|
||||||
|
assert.ok(!EVENT_FORMS[0x1F].build({ status: '1', served: '01020304' }).includes('F000'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the location-status event can carry the location information', () => {
|
test('the location-status event can carry the location information', () => {
|
||||||
@@ -265,10 +277,22 @@ test('event fields introduced after the effective release are hidden', () => {
|
|||||||
assert.deepStrictEqual(r.hidden.map(f => f.id).sort(), ['code', 'impu']);
|
assert.deepStrictEqual(r.hidden.map(f => f.id).sort(), ['code', 'impu']);
|
||||||
// slices: the mapping/rejected objects came in Rel-18
|
// slices: the mapping/rejected objects came in Rel-18
|
||||||
r = eventFieldsForRelease(EVENT_FORMS[0x1F], 16);
|
r = eventFieldsForRelease(EVENT_FORMS[0x1F], 16);
|
||||||
assert.deepStrictEqual(r.hidden.map(f => f.id).sort(), ['allowed_map', 'rejected', 'rejected_map']);
|
assert.deepStrictEqual(r.hidden.map(f => f.id).sort(), ['allowed_map', 'partial', 'rejected', 'rejected_map']);
|
||||||
assert.strictEqual(eventFieldsForRelease(EVENT_FORMS[0x1F], 18).hidden.length, 0);
|
assert.strictEqual(eventFieldsForRelease(EVENT_FORMS[0x1F], 18).hidden.length, 0);
|
||||||
// a field without a known release is never hidden
|
// a field without a known release is never hidden
|
||||||
r = eventFieldsForRelease(EVENT_FORMS[0x12], 4);
|
r = eventFieldsForRelease(EVENT_FORMS[0x12], 4);
|
||||||
assert.strictEqual(r.hidden.length, 0);
|
assert.strictEqual(r.hidden.length, 0);
|
||||||
assert.strictEqual(r.fields.length, EVENT_FORMS[0x12].fields.length);
|
assert.strictEqual(r.fields.length, EVENT_FORMS[0x12].fields.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the single-envelope budget is enforced by the size helper', () => {
|
||||||
|
// inner = 8 header bytes + the event data; D6 81 <len> + inner must stay
|
||||||
|
// within the 1-byte APDU Lc, so the inner data is capped at 252 bytes
|
||||||
|
assert.strictEqual(EVENT_INNER_MAX, 252);
|
||||||
|
assert.strictEqual(eventEnvelopeSize(''), 8);
|
||||||
|
assert.strictEqual(eventEnvelopeSize('AABB'), 10);
|
||||||
|
assert.strictEqual(eventEnvelopeSize('AB'.repeat(244)), 252);
|
||||||
|
assert.ok(eventFitsOneEnvelope('AB'.repeat(244)));
|
||||||
|
assert.ok(!eventFitsOneEnvelope('AB'.repeat(245)));
|
||||||
|
assert.ok(eventFitsOneEnvelope(null));
|
||||||
|
});
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pysim-simple-server"
|
name = "pysim-simple-server"
|
||||||
version = "3.13.4"
|
version = "3.14.0"
|
||||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||||
|
|||||||
@@ -73,10 +73,11 @@ CAT_RELEASE = {
|
|||||||
'56/D6 CSG ID': 9,
|
'56/D6 CSG ID': 9,
|
||||||
'57/D7 HNB name': 9,
|
'57/D7 HNB name': 9,
|
||||||
'31/B1 IMS URI': 10,
|
'31/B1 IMS URI': 10,
|
||||||
'77/F7 IARI': 10,
|
'76/F6 IARI': 10,
|
||||||
'77/F7 IMPU list': 11,
|
'77/F7 IMPU list': 11,
|
||||||
'78/F8 IMS status code': 11,
|
'78/F8 IMS status code': 11,
|
||||||
'7E/FE Media Type': 12,
|
'7E/FE Media Type': 12,
|
||||||
|
'73/F3 URI truncated': 12,
|
||||||
'09/89 PLMN ID': 4,
|
'09/89 PLMN ID': 4,
|
||||||
'3F/BF Access technology': 4,
|
'3F/BF Access technology': 4,
|
||||||
'B4 Supported Radio Access Technologies': 13,
|
'B4 Supported Radio Access Technologies': 13,
|
||||||
@@ -96,6 +97,8 @@ CAT_RELEASE = {
|
|||||||
'77/F7 Allowed slices with S-NSSAI mapping': 18,
|
'77/F7 Allowed slices with S-NSSAI mapping': 18,
|
||||||
'D7 Rejected slices with S-NSSAI mapping': 18,
|
'D7 Rejected slices with S-NSSAI mapping': 18,
|
||||||
'B1 Rejected slices information': 18,
|
'B1 Rejected slices information': 18,
|
||||||
|
'79/F9 Partial NSSAI': 18,
|
||||||
|
'70/F0 Last Envelope': 18,
|
||||||
},
|
},
|
||||||
'pli': {
|
'pli': {
|
||||||
'00': 4, '01': 4, '02': 4, '03': 4, '04': 4, '06': 4, '07': 4, '08': 6,
|
'00': 4, '01': 4, '02': 4, '03': 4, '04': 4, '06': 4, '07': 4, '08': 6,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from osmocom.tlv import BER_TLV_IE
|
|||||||
from osmocom.utils import rpad
|
from osmocom.utils import rpad
|
||||||
|
|
||||||
|
|
||||||
VERSION = '3.13.4'
|
VERSION = '3.14.0'
|
||||||
|
|
||||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||||
|
|
||||||
@@ -4595,11 +4595,24 @@ def _verify_adm(scc, app, adm_hex):
|
|||||||
'error': 'Security status not satisfied' if sw == '6982' else 'Error'}
|
'error': 'Security status not satisfied' if sw == '6982' else 'Error'}
|
||||||
|
|
||||||
|
|
||||||
|
# The single-ENVELOPE budget for event data: the inner data (event list +
|
||||||
|
# device identities + the event objects) is wrapped as D6 81 <len> + inner,
|
||||||
|
# and the APDU Lc must stay within one byte (255), so the inner data is
|
||||||
|
# capped at 252 bytes (244 bytes of event data + the 8-byte header).
|
||||||
|
# Chained (multi-envelope) delivery is not implemented - the slice/S-NSSAI
|
||||||
|
# data must fit one envelope (see the help).
|
||||||
|
_EVENT_INNER_MAX = 252
|
||||||
|
_EVENT_DATA_MAX = _EVENT_INNER_MAX - 8
|
||||||
|
|
||||||
|
|
||||||
def _send_event_download(scc, event_type, event_data=None, drain=True, src=None):
|
def _send_event_download(scc, event_type, event_data=None, drain=True, src=None):
|
||||||
"""Send ENVELOPE(Event Download) for the given event type.
|
"""Send ENVELOPE(Event Download) for the given event type.
|
||||||
Builds: CLA C2 0000 Lc D6 [len] (99 01 [type] 82 02 <src> 81 [extra]).
|
Builds: CLA C2 0000 Lc D6 [len] (99 01 [type] 82 02 <src> 81 [extra]).
|
||||||
`src` is the source device identity (TS 102 223 8.7: '82' terminal,
|
`src` is the source device identity (TS 102 223 8.7: '82' terminal,
|
||||||
'83' network); the default is the terminal."""
|
'83' network); the default is the terminal."""
|
||||||
|
if event_data and len(event_data) > _EVENT_DATA_MAX:
|
||||||
|
raise ValueError('event data does not fit one ENVELOPE (max %d bytes; '
|
||||||
|
'chained delivery is not implemented)' % _EVENT_DATA_MAX)
|
||||||
src_byte = 0x82
|
src_byte = 0x82
|
||||||
if src:
|
if src:
|
||||||
try:
|
try:
|
||||||
@@ -7205,6 +7218,10 @@ class PysimHandler(BaseHTTPRequestHandler):
|
|||||||
resp['net_state'] = self.server.net_state
|
resp['net_state'] = self.server.net_state
|
||||||
self._send_json(resp)
|
self._send_json(resp)
|
||||||
self._log_resp(resp)
|
self._log_resp(resp)
|
||||||
|
except ValueError as e:
|
||||||
|
# the single-envelope budget (no chained delivery)
|
||||||
|
self._send_json({'error': str(e)}, 400)
|
||||||
|
self._log_resp({'error': str(e)})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write('Event send error: %s\n' % e)
|
sys.stderr.write('Event send error: %s\n' % e)
|
||||||
_handle_card_disconnect(stale=_is_transport_fatal(e))
|
_handle_card_disconnect(stale=_is_transport_fatal(e))
|
||||||
|
|||||||
@@ -934,6 +934,20 @@ class TestEventDownload(unittest.TestCase):
|
|||||||
d6 = bytes([0xD6, 0x81, len(inner)]) + inner
|
d6 = bytes([0xD6, 0x81, len(inner)]) + inner
|
||||||
self.assertEqual(apdu, '80c20000%02x%s' % (len(d6), d6.hex()))
|
self.assertEqual(apdu, '80c20000%02x%s' % (len(d6), d6.hex()))
|
||||||
|
|
||||||
|
def test_event_data_beyond_one_envelope_is_refused(self):
|
||||||
|
# the tool sends one envelope per event: the inner data is capped at
|
||||||
|
# 252 bytes (the D6 long-form length and the APDU Lc stay in one byte);
|
||||||
|
# chained (multi-envelope) delivery is not implemented
|
||||||
|
with self.assertRaises(ValueError) as ctx:
|
||||||
|
self._send(0x1F, bytes(245))
|
||||||
|
self.assertIn('one ENVELOPE', str(ctx.exception))
|
||||||
|
# 244 bytes of event data still fit (inner = 252, the maximum)
|
||||||
|
apdu, sw = self._send(0x1F, bytes(244))
|
||||||
|
self.assertEqual(sw, '9000')
|
||||||
|
inner = bytes([0x99, 0x01, 0x1F, 0x82, 0x02, 0x82, 0x81]) + bytes(244)
|
||||||
|
d6 = bytes([0xD6, 0x81, len(inner)]) + inner
|
||||||
|
self.assertEqual(apdu, '80c20000%02x%s' % (len(d6), d6.hex()))
|
||||||
|
|
||||||
|
|
||||||
class TestTimerManagement(unittest.TestCase):
|
class TestTimerManagement(unittest.TestCase):
|
||||||
"""Terminal side of TIMER MANAGEMENT (TS 102 223 6.6.21, 6.8.13/14, 7.4).
|
"""Terminal side of TIMER MANAGEMENT (TS 102 223 6.6.21, 6.8.13/14, 7.4).
|
||||||
|
|||||||
Reference in New Issue
Block a user