diff --git a/frontend/index.html b/frontend/index.html index 56a57cb..569a8ac 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1682,7 +1682,7 @@ // ===== Version ===== // Single source of truth for the PWA version: shown in the header and used // by the server version check in pysimConnect(). -const SIMPLE_VERSION = '3.6.13'; +const SIMPLE_VERSION = '3.6.14'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -2548,7 +2548,7 @@ function ramResetGrants() { // value cannot be coded: each TAR is 3 bytes (6 hex digits), and menu item // identifiers 128..255 are reserved for the toolkit framework, so only // 01..7F may be requested (TS 102 226 8.2.1.3.2.3). In EA mode `v.fsAccess` -// adds the UICC file-access parameters (tag '82') and `v.adfAccess` extends +// adds the UICC file-access parameters (tag '81') and `v.adfAccess` extends // them with an ADF entry (AID from `v.adfAid`, default ADF.USIM); the SIM path // grants access via the CA Access Domain field instead. function stkParamsBuild(v) { @@ -2600,21 +2600,22 @@ function stkParamsBuild(v) { services.toString(16).padStart(2, '0'); let eaValue = '80' + berLenStr(tkPayload.length / 2) + tkPayload; if (v.fsAccess) { - // UICC Access Application specific parameters (TS 102 226 - // 8.2.1.3.2.2.2): every entry ends with the "Length of Access Domain - // DAP" byte (00 = no DAP): + // UICC Access Application specific parameters (tag '81', TS 102 226 + // 8.2.1.3.2.2/8.2.1.3.2.2.2; '82' is the *Administrative* Access + // field): every entry ends with the "Length of Access Domain DAP" + // byte (00 = no DAP): // [file system AID length 00 = shared FS][AD length 01] // [ADP 00 = full access][DAP length 00] // [ADF AID length][ADF AID][AD length 01][ADP 00][DAP length 00] - // (without the DAP length byte the card rejected the ADF entry with - // 6A80; the ADF AID must be 5..16 bytes.) + // (the ADF AID must be 5..16 bytes; under '82' the card rejected the + // ADF entry with 6A80 - the applet never got its FileView rights.) let acc = '000100' + '00'; if (v.adfAccess) { const adf = (v.adfAid || 'A0000000871002').replace(/[^0-9a-fA-F]/g, '').toUpperCase(); if (adf.length < 10 || adf.length > 32) return null; acc += (adf.length / 2).toString(16).padStart(2, '0').toUpperCase() + adf + '0100' + '00'; } - eaValue += '82' + berLenStr(acc.length / 2) + acc; + eaValue += '81' + berLenStr(acc.length / 2) + acc; } return 'EA' + berLenStr(eaValue.length / 2) + eaValue; } diff --git a/frontend/sw.js b/frontend/sw.js index d5395b8..fb27eff 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v286'; +const CACHE = 'simple-v287'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/stk_params.test.js b/frontend/tests/stk_params.test.js index 61ef794..b67034c 100644 --- a/frontend/tests/stk_params.test.js +++ b/frontend/tests/stk_params.test.js @@ -150,24 +150,24 @@ test('the RAM form fields build the live install parameters end to end', () => { }); test('UICC file-access parameters (82) are appended in EA mode', () => { - // TS 102 226 8.2.1.3.2.2.2: every entry ends with the "Length of Access - // Domain DAP" byte (00 = no DAP): + // TS 102 226 8.2.1.3.2.2/8.2.1.3.2.2.2: the file-access list is the + // tag '81' field ('82' is the *Administrative* Access field); every entry + // ends with the "Length of Access Domain DAP" byte (00 = no DAP): // [FS AID len 00 = shared FS][AD len 01][ADP 00 = full][DAP len 00] // [ADF AID len][ADF AID][AD len 01][ADP 00][DAP len 00] // The SIM path grants the same rights via the CA Access Domain field; the - // ADF entry is an extension of the file-system entry. A missing DAP - // length byte made the card reject the ADF entry with 6A80 (live - // 2026-09-28). + // ADF entry is an extension of the file-system entry. Under '82' the card + // rejected the ADF entry with 6A80 (live 2026-09-28). const base = vals({ channels: '1', msl: '12', tar: 'AF4D01' }); assert.strictEqual(stkParamsBuild(Object.assign({}, base, { fsAccess: true })), - 'EA15800D000000000102011203AF4D0100820400010000'); + 'EA15800D000000000102011203AF4D0100810400010000'); assert.strictEqual( stkParamsBuild(Object.assign({}, base, { fsAccess: true, adfAccess: true })), - 'EA20800D000000000102011203AF4D0100820F0001000007A0000000871002010000'); + 'EA20800D000000000102011203AF4D0100810F0001000007A0000000871002010000'); assert.strictEqual( stkParamsBuild(Object.assign({}, base, { fsAccess: true, adfAccess: true, adfAid: 'A0000000871002FF33FFFF89010101' })), - 'EA28800D000000000102011203AF4D01008217000100000FA0000000871002FF33FFFF89010101010000'); + 'EA28800D000000000102011203AF4D01008117000100000FA0000000871002FF33FFFF89010101010000'); assert.strictEqual(stkParamsBuild(base), 'EA0F800D000000000102011203AF4D0100'); assert.strictEqual(stkParamsBuild(Object.assign({}, base, { adfAccess: true })), 'EA0F800D000000000102011203AF4D0100'); @@ -182,12 +182,12 @@ test('the RAM form emits full file access when the checkbox is ticked', () => { fakeForm({ 'rc-toolkit-enable': true, 'rc-tk-mode': 'ea', 'rc-tk-msl': '12', 'rc-tk-tar': 'AF4D01', 'rc-tk-channels': '1', 'rc-tk-fsaccess': true }); assert.strictEqual(buildRcToolkitParams(), - 'EA15800D000000000102011203AF4D0100820400010000'); + 'EA15800D000000000102011203AF4D0100810400010000'); fakeForm({ 'rc-toolkit-enable': true, 'rc-tk-mode': 'ea', 'rc-tk-msl': '12', 'rc-tk-tar': 'AF4D01', 'rc-tk-channels': '1', 'rc-tk-fsaccess': true, 'rc-tk-adfaccess': true }); assert.strictEqual(buildRcToolkitParams(), - 'EA20800D000000000102011203AF4D0100820F0001000007A0000000871002010000'); + 'EA20800D000000000102011203AF4D0100810F0001000007A0000000871002010000'); }); test('updateStkParamsHex refreshes the field and clears the manual flag', () => { diff --git a/pyproject.toml b/pyproject.toml index 8de9e97..47d1f7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.6.13" +version = "3.6.14" description = "HTTP REST server wrapping pysim for the SIMple PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index f3baa22..98447c7 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '3.6.13' +VERSION = '3.6.14' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE