fix: SCP80 RC/CPL parity, file-life-cycle labels, EF.ARR and PS template decoders (v3.5.1)
Three fixes found while filling the ETSI/3GPP registry gaps against the SIMalliance Stepping Stones R7: - SCP80 builder/verification parity (TS 31.115 Table 1 NOTE / 4.2 / 4.3): the CPL is now transmitted whenever the packet is ciphered or carries RC/CC/DS - it is part of their input - and whenever the packet needs SMS concatenation; a single unprotected SM keeps pySim's CHL-first form. Before, the JS dropped the CPL for every unciphered packet while the server reference re-added it, so "Verify vs pySim" reported a false MISMATCH for every unciphered RC/CC packet (SPI1 01/02/0A/12/1A...). All ten offered SPI1 values now match the server reference byte-for-byte. - RC (SPI1 b2b1 = 01) was offered but not built: the JS now computes CRC-32 (TS 102 225 5.1.3.2, pySim zlib.crc32 parity) over the same CPL frame as the CC; the packet no longer silently omits the 4-byte RC field. - Server: _build_secured_packet/_ota_reference add the CPL to a concatenated unprotected packet too (Table 1 NOTE / 4.3). - fcpLifeCycle: unlisted values with b8 clear are RFU, b8 set is proprietary (Table 11.7b); previously all unmatched values were labelled proprietary. - EF.ARR decoder now decodes the expanded format (AM_DO/SC_DO per ISO 7816-4 5.4.3.2 + TS 102 221 9.2.7): operation bit masks, INCREASE/RESIZE AM_DO 0x84, OR/AND/NOT templates, PIN key references with usage qualifiers. - FCP 'C6' PS template DO decoded (PS_DO bitmap + key references + usage qualifiers, TS 102 221 11.1.1.4.10/9.5.2) with a shared key-reference map. Tests: sp.test.js (CPL/RC vectors + crc32 known answer), ef_decode.test.js (expanded-format ARR vectors), profiler.test.js (LCSI RFU/proprietary, C6), test_ota_helpers.py (RC reference, unprotected single-SM vs concatenated). Help EN/RU and READMEs: CPL size 2 (SMS), RC/CC/DS 4-8, RC bullet, CPL rule. 547 frontend / 397 Python green; version 3.5.1; sw cache simple-v253.
This commit is contained in:
+29
-10
@@ -27,7 +27,7 @@ function extractFunc(src, name) {
|
||||
}
|
||||
|
||||
const FNS = ['hexToBytes', 'bytesToHex', 'des3Keys', 'des3EncryptBlock', 'des3CbcEncrypt',
|
||||
'xorBytes', 'zeroPad', 'cbcMac', 'aesCbcEncrypt', 'aesShiftLeft1', 'aesCmacSubkeys',
|
||||
'xorBytes', 'zeroPad', 'crc32Bytes', 'cbcMac', 'aesCbcEncrypt', 'aesShiftLeft1', 'aesCmacSubkeys',
|
||||
'aesCmac', '_genSpBuild', 'genSp', 'spNextCntr', 'scp80SegmentInfo', 'spSizeInfoText',
|
||||
'spShowSizeInfo'];
|
||||
let code = '';
|
||||
@@ -95,16 +95,35 @@ test('ciphered + CC SPI 16/01 (counter_must_be_higher, plaintext PoR)', () => {
|
||||
'00201516011515B00000E42573469E68A8462A57A505B0E2B1C09C1928C7A182311F');
|
||||
});
|
||||
|
||||
test('unciphered + CC SPI 02/09', () => {
|
||||
assert.strictEqual(
|
||||
makeRun({ 'sp-spi1': '02', 'sp-spi2-hex': '09' }),
|
||||
'1502091515B0000000000000010085A8CA1A9828B0BB00A40000023F00');
|
||||
test('unciphered + CC SPI 02/09 carries the CPL', () => {
|
||||
const out = makeRun({ 'sp-spi1': '02', 'sp-spi2-hex': '09' });
|
||||
assert.strictEqual(out,
|
||||
'001D1502091515B0000000000000010085A8CA1A9828B0BB00A40000023F00');
|
||||
assert.strictEqual(parseInt(out.slice(0, 4), 16), out.length / 2 - 2);
|
||||
});
|
||||
|
||||
test('unciphered packet starts at CHL, no CPL prefix (pySim parity)', () => {
|
||||
const out = makeRun({ 'sp-spi1': '02', 'sp-spi2-hex': '09' });
|
||||
assert.strictEqual(out.slice(0, 2), '15');
|
||||
assert.strictEqual(out.length, 58);
|
||||
test('unprotected packet (SPI 00) keeps the pySim CHL-first form', () => {
|
||||
assert.strictEqual(
|
||||
makeRun({ 'sp-spi1': '00', 'sp-spi2-hex': '09' }),
|
||||
'0D00091515B0000000000000010000A40000023F00');
|
||||
});
|
||||
|
||||
test('unprotected concatenated packet gains the CPL (TS 31.115 4.3)', () => {
|
||||
const out = makeRun({ 'sp-spi1': '00', 'sp-spi2-hex': '09', 'sp-apdu': 'A0'.repeat(200) });
|
||||
assert.strictEqual(out.length / 2, 216);
|
||||
assert.strictEqual(parseInt(out.slice(0, 4), 16), 214);
|
||||
assert.strictEqual(out.slice(4, 6), '0D');
|
||||
assert.strictEqual(scp80SegmentInfo(out).segments, 2);
|
||||
});
|
||||
|
||||
test('RC (SPI 01) computes CRC-32 over the CPL frame', () => {
|
||||
assert.strictEqual(
|
||||
makeRun({ 'sp-spi1': '01', 'sp-spi2-hex': '09' }),
|
||||
'00191101091515B0000000000000010050C942DC00A40000023F00');
|
||||
});
|
||||
|
||||
test('crc32Bytes known answer (TS 102 225 Annex B)', () => {
|
||||
assert.strictEqual(bytesToHex(crc32Bytes(hexToBytes('0102030405'))), '470B99F4');
|
||||
});
|
||||
|
||||
test('sysmocom public reference vector (spi1 04 / spi2 19, cntr=0)', () => {
|
||||
@@ -174,7 +193,7 @@ test('AES unciphered + CC SPI 12/09 (counter higher)', () => {
|
||||
'sp-kic-key': KIC_AES,
|
||||
'sp-kid-key': KID_AES,
|
||||
}),
|
||||
'1512092222B0001100000000110029826122C7A0B79500A40004023F00');
|
||||
'001D1512092222B0001100000000110029826122C7A0B79500A40004023F00');
|
||||
});
|
||||
|
||||
test('AES ciphered + CC SPI 1E/19 (counter +1)', () => {
|
||||
|
||||
Reference in New Issue
Block a user