v3.35.0: EF.SMSP decoded editor (the full SMS-parameter form)

TS 31.102 4.2.27 scope: alpha; TP-DA and TS-SCA with their own length
conventions (DA = digits, SCA = octets, 0 = default SMSC) and a TON
select; TP-PID; TP-DCS with TS 23.038 hints; the relative TP-VP with the
TS 23.040 ladder; a per-parameter "absent (the ME default applies)"
state.  Unchanged slots are copied verbatim (the live fixture's
DCS-absent 00 and present VP FF survive), alphanumeric TON-5 addresses
stay read-only, and every checked record is validated before any write.

Decoder fixes: the parameter-indicator bits were shifted (SCA on 5,
PID on 2, DCS on 3, VP on 4; the spec is DA/SCA/PID/DCS/VP on bits 1-5
with 6-8 reserved, 1 = absent), and the TP-DA length was read as octets
instead of digits (TS 23.040 9.1.2.5).

Live fixture: ffffffffffffffffffffffffffe9ffffffffffffffffffffffff07919733824009f0ffffffff0000ff
(SCA +79332804900, PID 00, DCS absent/00, VP FF) round-trips
byte-identically.

1022 Python + 1058 frontend green; simple-v468.
This commit is contained in:
2026-10-23 16:54:12 +03:00
parent 180dd50c06
commit 87bf4acbbc
13 changed files with 944 additions and 41 deletions
+29 -1
View File
@@ -27,7 +27,7 @@ const FNS = [
'efDecIccid', 'efDecImsi', 'efDecLi', 'efDecServiceTable', 'efDecSst', 'efDecPlmnList', 'efDecFplmn', 'efDecPlmnWact',
'efDecSpn', 'efDecLoci', 'efDecPsLoci', 'efDecEpsLoci', 'efDecEpsNsc', 'efDecKc', 'efDecAcc',
'efDecPhase', 'efDecCbmi', 'efDecCbmir', 'efDecHpplmn', 'efDecEcc', 'efDecOpl', 'efDecAd', 'efDecAcl',
'efDecSmsp', 'efDecSpdi', 'efDecNai', 'efDecDir', 'efDecArr', 'efDecPnn', 'efDecAdn',
'efSmspDaNumber', 'efDecSmsp', 'efDecSpdi', 'efDecNai', 'efDecDir', 'efDecArr', 'efDecPnn', 'efDecAdn',
'pinKeyRefName', 'efArrAmLabel', 'efArrSc', 'efArrAmDo',
'efDecExt1', 'efDecSms', 'efDecSume', 'efPbrHex', 'efDecPbr', 'efFindDecoder', 'efFidFromPath', 'efDecodeBytes',
'efDecodeFile', 'efDecodeHex', 'efIsRawOnly', 'efFieldLabel', 'efPrimitive', 'efFlatten',
@@ -278,6 +278,9 @@ test('EF record decoders: SMS, OPL, DIR, ARR, IMPI/SMSP', () => {
assert.strictEqual(smsp.sca, '+4915790109999');
assert.strictEqual(smsp.tp_pid, '0x00');
assert.strictEqual(smsp.tp_dcs, '0x00');
assert.strictEqual(smsp.tp_vp, '0xA9');
assert.strictEqual(smsp.indicators, '0xE1');
assert.strictEqual(smsp.tp_da, undefined);
// EF.SMS status byte + skip SMSC + TPDU
const sms = efDecSms(efBytes('030891945197109099f90000'));
assert.strictEqual(sms.direction, 'MT');
@@ -285,6 +288,31 @@ test('EF record decoders: SMS, OPL, DIR, ARR, IMPI/SMSP', () => {
assert.strictEqual(sms.tpdu, '0000');
});
test('EF.SMSP decodes the indicator bits (TS 31.102 4.2.27)', () => {
// live card record: DA absent (0x01), SCA present, PID present, DCS absent
// (0x08), VP present; bits 6..8 reserved = 1 -> 0xE9
const live = efDecSmsp(efBytes('ffffffffffffffffffffffffffe9ffffffffffffffffffffffff07919733824009f0ffffffff0000ff'));
assert.deepStrictEqual(live, {
indicators: '0xE9',
sca: '+79332804900',
tp_pid: '0x00',
tp_vp: '0xFF',
});
assert.strictEqual(live.tp_da, undefined);
assert.strictEqual(live.tp_dcs, undefined); // DCS absent, its 00 slot kept raw for the editor
});
test('EF.SMSP reads the TP-DA length as a digit count (00 padding is not ABS)', () => {
// alpha FF x13 | indicator FE (DA present, rest absent) |
// DA 04 81 21 43 00 00 FF... ("1234" padded with 00, not FF)
const hex = 'FF'.repeat(13) + 'FE' + '048121430000FFFFFFFFFFFF'
+ 'FF'.repeat(12) + 'FF' + 'FF' + 'FF';
assert.deepStrictEqual(efDecSmsp(efBytes(hex)), {
indicators: '0xFE',
tp_da: '1234',
});
});
test('service tables: UST service bits and IST fallback for 6F07', () => {
const ust = efDecServiceTable(efBytes('00000010'), EF_UST_SERVICES);
assert.deepStrictEqual(ust.services, [{ n: 29, name: 'Data download via SMS-CB' }]);