fix BER long-form lengths: parseBerLen read b length bytes instead of b & 0x7F
parseBerLen treated the first long-form length byte (0x81/0x82/...) as the count of length bytes, so any TLV with a long-form length parsed as garbage: the profiler FCI decoder returned ok:false (decoded preview disappeared and never came back after editing an FCP with a '62 81 xx' outer length or a long-form inner TLV), and the same bug hit parseTlvList (C-APDU parser, INSTALL param walker), parseBerScript (expanded script rows >= 128 bytes) and readLvField. Per ISO 7816-4 5.2 / UICC_SPECS.md 1.7 the long form is 81-84 followed by (b & 0x7F) length bytes. Fixed; short-form behavior unchanged. New tests: parseBerLen short/81/82 forms, parseTlvList long-form outer and inner TLVs, fcpDecode long-form regression (81/82, nested A5, >=128-byte FCP) incl. the editor preview path. SW cache v73 -> v74.
This commit is contained in:
+2
-2
@@ -3127,8 +3127,8 @@ const PARSE_INS = {
|
|||||||
function parseBerLen(hex, i) {
|
function parseBerLen(hex, i) {
|
||||||
const b = parseInt(hex.substr(i, 2), 16);
|
const b = parseInt(hex.substr(i, 2), 16);
|
||||||
if (b < 128) return {len: b, consumed: 2};
|
if (b < 128) return {len: b, consumed: 2};
|
||||||
const n = parseInt(hex.substr(i + 2, b * 2), 16);
|
const n = b & 0x7F;
|
||||||
return {len: n, consumed: 2 + b * 2};
|
return {len: parseInt(hex.substr(i + 2, n * 2), 16), consumed: 2 + n * 2};
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTlvList(hex) {
|
function parseTlvList(hex) {
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'otaman-v73';
|
const CACHE = 'otaman-v74';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -165,6 +165,39 @@ test('parseTlvList handles BER-TLVs', () => {
|
|||||||
assert.strictEqual(tlvs[1].tag, '82');
|
assert.strictEqual(tlvs[1].tag, '82');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseBerLen handles short and long form lengths (ISO 7816-4 5.2)', () => {
|
||||||
|
assert.deepStrictEqual(parseBerLen('1200', 0), { len: 18, consumed: 2 });
|
||||||
|
assert.deepStrictEqual(parseBerLen('8112', 0), { len: 18, consumed: 4 });
|
||||||
|
assert.deepStrictEqual(parseBerLen('820100', 0), { len: 256, consumed: 6 });
|
||||||
|
assert.deepStrictEqual(parseBerLen('820182' + '0102030405060708', 0), { len: 386, consumed: 6 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parseTlvList parses long-form lengths (81/82)', () => {
|
||||||
|
const short = parseTlvList('6212' + '8202412183026F078A010580020009880110');
|
||||||
|
assert.strictEqual(short.length, 1);
|
||||||
|
assert.strictEqual(short[0].tag, '62');
|
||||||
|
assert.strictEqual(short[0].length, 18);
|
||||||
|
|
||||||
|
// same content with a long-form outer length
|
||||||
|
const long81 = parseTlvList('628112' + '8202412183026F078A010580020009880110');
|
||||||
|
assert.strictEqual(long81.length, 1);
|
||||||
|
assert.strictEqual(long81[0].length, 18);
|
||||||
|
assert.strictEqual(long81[0].value, short[0].value);
|
||||||
|
|
||||||
|
// 2-byte length form
|
||||||
|
const long82 = parseTlvList('62820004' + '80020009');
|
||||||
|
assert.strictEqual(long82.length, 1);
|
||||||
|
assert.strictEqual(long82[0].length, 4);
|
||||||
|
assert.strictEqual(long82[0].value, '80020009');
|
||||||
|
|
||||||
|
// inner TLV with a long-form length (A5 81 05 85 03 00 00 00)
|
||||||
|
const inner = parseTlvList('A581058503000000');
|
||||||
|
assert.strictEqual(inner.length, 1);
|
||||||
|
assert.strictEqual(inner[0].tag, 'A5');
|
||||||
|
assert.strictEqual(inner[0].length, 5);
|
||||||
|
assert.strictEqual(inner[0].value, '8503000000');
|
||||||
|
});
|
||||||
|
|
||||||
test('gsm7Decode unpacks "HI" from C824', () => {
|
test('gsm7Decode unpacks "HI" from C824', () => {
|
||||||
const bytes = new Uint8Array([0xC8, 0x24]);
|
const bytes = new Uint8Array([0xC8, 0x24]);
|
||||||
assert.strictEqual(gsm7Decode(bytes), 'HI');
|
assert.strictEqual(gsm7Decode(bytes), 'HI');
|
||||||
|
|||||||
@@ -641,6 +641,35 @@ test('fcpDecode unwraps an FCI 6F template and rejects malformed input', () => {
|
|||||||
assert.strictEqual(fcpDecode('6213' + FCP_TRANSPARENT.slice(4)).ok, false); // wrong outer length
|
assert.strictEqual(fcpDecode('6213' + FCP_TRANSPARENT.slice(4)).ok, false); // wrong outer length
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('fcpDecode accepts long-form BER lengths (81/82) — editor preview regression', () => {
|
||||||
|
// 62 12 <18B> vs 62 81 12 <same content>
|
||||||
|
const long81 = '628112' + FCP_TRANSPARENT.slice(4);
|
||||||
|
const d1 = fcpDecode(long81);
|
||||||
|
assert.strictEqual(d1.ok, true);
|
||||||
|
assert.strictEqual(d1.items.find(it => it.key === '80').decoded, '9 bytes');
|
||||||
|
|
||||||
|
// 2-byte length form
|
||||||
|
const d2 = fcpDecode('62820004' + '80020009');
|
||||||
|
assert.strictEqual(d2.ok, true);
|
||||||
|
assert.strictEqual(d2.items.find(it => it.key === '80').decoded, '9 bytes');
|
||||||
|
|
||||||
|
// nested A5 with a long-form length (A5 81 05 …)
|
||||||
|
const d3 = fcpDecode('62138202412183026F078A0105A581058503000000');
|
||||||
|
assert.strictEqual(d3.ok, true);
|
||||||
|
assert.strictEqual(d3.items.find(it => it.key === 'A5/85').decoded, '0 bytes');
|
||||||
|
|
||||||
|
// FCP content >= 128 bytes uses a long-form outer length
|
||||||
|
let big = '';
|
||||||
|
for (let i = 0; i < 13; i++) big += '8808' + '0102030405060708'; // 13 x 10B = 130B
|
||||||
|
const d4 = fcpDecode('628182' + big);
|
||||||
|
assert.strictEqual(d4.ok, true);
|
||||||
|
assert.strictEqual(d4.items.filter(it => it.key === '88').length, 13);
|
||||||
|
|
||||||
|
global.t = s => s;
|
||||||
|
assert.ok(profilerFciPreviewItems(long81).includes('File size: '));
|
||||||
|
delete global.t;
|
||||||
|
});
|
||||||
|
|
||||||
test('fcpDiffHtml highlights differing FCP parameters', () => {
|
test('fcpDiffHtml highlights differing FCP parameters', () => {
|
||||||
global.t = s => s;
|
global.t = s => s;
|
||||||
const same = fcpDiffHtml(FCP_TRANSPARENT, FCP_TRANSPARENT);
|
const same = fcpDiffHtml(FCP_TRANSPARENT, FCP_TRANSPARENT);
|
||||||
|
|||||||
Reference in New Issue
Block a user