181544b360
New 'HTTP OTA' sub-tab in the C-APDU constructor builds Remote Application
Management over HTTP payloads (GPC v2.2 Amendment B v1.1 §4.7):
- Trigger (Push SMS): 81 > 83 > 84/[85]/[86]/89, with optional Command
Scripting template ('AA') wrap per TS 102 226 §5.2.1
- Store (SD admin params): STORE DATA TLV mode (80 E2 90 00), tag 85/A5
- Connection parameters: OPEN CHANNEL comprehension-TLV row editor + presets
(Device Identities 02, Alpha 80, Bearer 01)
- Security parameters (Table 4-6): LV PSK Identity + LV KVN/KID
- Retry policy (Table 4-7): retry counter + TS 102 223 timer TLV (25 03)
- HTTP POST (Tables 4-8/9/10): 8A host / 8B agent / 8C URI, text→octets
- 'Pack into Secured packet' reuses packToSp for SPI/counter filling
Pure builder functions covered by 12 new tests reproducing the byte layout
from the spec tables. i18n entries, help.html/help-ru.html §2.7 added.
SW cache bumped otaman-v49 → v50.
142 lines
5.1 KiB
JavaScript
142 lines
5.1 KiB
JavaScript
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
|
|
|
function extractFunc(src, name) {
|
|
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
|
const m = re.exec(src);
|
|
if (!m) throw new Error('function ' + name + ' not found');
|
|
let i = m.index + m[0].length - 1;
|
|
let depth = 0;
|
|
for (; i < src.length; i++) {
|
|
if (src[i] === '{') depth++;
|
|
else if (src[i] === '}') {
|
|
depth--;
|
|
if (depth === 0) break;
|
|
}
|
|
}
|
|
return src.slice(m.index, i + 1);
|
|
}
|
|
|
|
const FNS = ['_hotaHex', '_hotaAsciiHex', '_hotaBerLen', '_hotaTlv',
|
|
'hotaTlv', 'hotaBuildConn', 'hotaBuildSec', 'hotaBuildRetry',
|
|
'hotaBuildHttpPost', 'hotaBuildTrigger', 'hotaBuildStore', 'hotaBuild'];
|
|
let code = '';
|
|
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
|
|
|
eval(code);
|
|
|
|
test('_hotaHex strips non-hex and uppercases', () => {
|
|
assert.strictEqual(_hotaHex(' 81 82 '), '8182');
|
|
assert.strictEqual(_hotaHex('b0-00'), 'B000');
|
|
assert.strictEqual(_hotaHex(''), '');
|
|
assert.strictEqual(_hotaHex(undefined), '');
|
|
});
|
|
|
|
test('_hotaAsciiHex encodes text as ASCII bytes', () => {
|
|
assert.strictEqual(_hotaAsciiHex('megafon.ru'), '6D656761666F6E2E7275');
|
|
assert.strictEqual(_hotaAsciiHex('v1.0'), '76312E30');
|
|
assert.strictEqual(_hotaAsciiHex('/sd'), '2F7364');
|
|
assert.strictEqual(_hotaAsciiHex(''), '');
|
|
});
|
|
|
|
test('_hotaBerLen encodes definite lengths', () => {
|
|
assert.strictEqual(_hotaBerLen(0), '00');
|
|
assert.strictEqual(_hotaBerLen(11), '0B');
|
|
assert.strictEqual(_hotaBerLen(127), '7F');
|
|
assert.strictEqual(_hotaBerLen(128), '8180');
|
|
assert.strictEqual(_hotaBerLen(255), '81FF');
|
|
assert.strictEqual(_hotaBerLen(256), '820100');
|
|
});
|
|
|
|
test('hotaTlv wraps tag + definite length + value', () => {
|
|
assert.strictEqual(hotaTlv('02', '8182'), '02028182');
|
|
assert.strictEqual(hotaTlv('05', ''), '0500');
|
|
assert.strictEqual(hotaTlv('8A', '6D656761666F6E2E7275'), '8A0A6D656761666F6E2E7275');
|
|
});
|
|
|
|
test('hotaBuildConn produces a full 84 TLV', () => {
|
|
assert.strictEqual(hotaBuildConn([{ tag: '02', value: '8182' }]), '840402028182');
|
|
assert.strictEqual(
|
|
hotaBuildConn([
|
|
{ tag: '01', value: '014001' },
|
|
{ tag: '02', value: '81 82' },
|
|
{ tag: '05', value: '' },
|
|
]),
|
|
'840B0103014001020281820500');
|
|
assert.strictEqual(hotaBuildConn([{ tag: '02', value: '8182' }, { tag: '', value: 'FF' }]), '840402028182');
|
|
assert.strictEqual(hotaBuildConn([]), '8400');
|
|
});
|
|
|
|
test('hotaBuildSec produces a full 85 TLV per Table 4-6', () => {
|
|
assert.strictEqual(
|
|
hotaBuildSec({ pskIdentity: 'Test123', kvn: '01', kid: '01' }),
|
|
'850B0754657374313233020101');
|
|
assert.strictEqual(
|
|
hotaBuildSec({ pskIdentity: '', kvn: '01', kid: '01' }),
|
|
'850400020101');
|
|
});
|
|
|
|
test('hotaBuildRetry uses TS 102 223 timer TLV (25 03) and wraps in 86', () => {
|
|
assert.strictEqual(
|
|
hotaBuildRetry({ counter: 'B000', delayH: 1, delayM: 2, delayS: 3, reportFailure: '' }),
|
|
'8607B0002503010203');
|
|
assert.strictEqual(
|
|
hotaBuildRetry({ counter: 'b0 00', delayH: '1', delayM: '2', delayS: '3', reportFailure: '0A080102030405060708' }),
|
|
'8611B00025030102030A080102030405060708');
|
|
assert.strictEqual(
|
|
hotaBuildRetry({ counter: '0000', delayH: 0, delayM: 0, delayS: 0, reportFailure: '0A0' }),
|
|
'860700002503000000');
|
|
});
|
|
|
|
test('hotaBuildHttpPost wraps 8A/8B/8C in a full 89 TLV', () => {
|
|
assert.strictEqual(
|
|
hotaBuildHttpPost({ host: '', agent: '', uri: '' }),
|
|
'89068A008B008C00');
|
|
assert.strictEqual(
|
|
hotaBuildHttpPost({ host: 'megafon.ru', agent: 'v1.0', uri: '/sd' }),
|
|
'89178A0A6D656761666F6E2E72758B0476312E308C032F7364');
|
|
});
|
|
|
|
test('hotaBuildTrigger wraps 81 > 83 > (84/85/86/89)', () => {
|
|
const conn = '840402028182';
|
|
const sec = '850400020101';
|
|
const retry = '8607B0002503010203';
|
|
const httpPost = '89068A008B008C00';
|
|
assert.strictEqual(
|
|
hotaBuildTrigger(conn, sec, retry, httpPost, false),
|
|
'811F831D8404020281828504000201018607B000250301020389068A008B008C00');
|
|
});
|
|
|
|
test('hotaBuildTrigger expanded wraps the 81 command in Command Scripting template AA', () => {
|
|
const conn = '840402028182';
|
|
const plain = hotaBuildTrigger(conn, '', '', '', false);
|
|
const expanded = hotaBuildTrigger(conn, '', '', '', true);
|
|
const byteLen = plain.length / 2;
|
|
assert.strictEqual(expanded, 'AA' + _hotaBerLen(byteLen) + plain);
|
|
assert.ok(expanded.startsWith('AA'));
|
|
});
|
|
|
|
test('hotaBuildStore emits STORE DATA TLV-mode APDU (80 E2 90 00)', () => {
|
|
assert.strictEqual(
|
|
hotaBuildStore('840402028182', '', '', '', '85'),
|
|
'80E29000088506840402028182');
|
|
assert.strictEqual(
|
|
hotaBuildStore('840402028182', '', '', '', 'A5'),
|
|
'80E2900008A506840402028182');
|
|
});
|
|
|
|
test('hotaBuild dispatches on mode', () => {
|
|
assert.strictEqual(
|
|
hotaBuild('trigger', '840402028182', '', '', '', '85', false),
|
|
hotaBuildTrigger('840402028182', '', '', '', false));
|
|
assert.strictEqual(
|
|
hotaBuild('store', '840402028182', '', '', '', '85', false),
|
|
hotaBuildStore('840402028182', '', '', '', '85'));
|
|
assert.strictEqual(
|
|
hotaBuild('store', '840402028182', '', '8607B0002503010203', '', '85', false),
|
|
hotaBuildStore('840402028182', '', '8607B0002503010203', '', '85'));
|
|
}); |