diff --git a/frontend/index.html b/frontend/index.html index 760204a..e82f91f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -809,6 +809,18 @@ +
+ +
+
+ +
@@ -1665,7 +1677,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.10'; +const SIMPLE_VERSION = '3.6.11'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -2502,6 +2514,8 @@ function updateRcTkMode() { const isSim = document.getElementById('rc-tk-mode').value === 'ca'; document.getElementById('rc-tk-ad-row').style.display = isSim ? '' : 'none'; document.getElementById('rc-tk-services-row').style.display = isSim ? 'none' : ''; + document.getElementById('rc-tk-fsaccess-row').style.display = isSim ? 'none' : ''; + document.getElementById('rc-tk-adfaccess-row').style.display = isSim ? 'none' : ''; } // Pure SIM/UICC toolkit install-parameter builder shared by the RAM install @@ -2509,7 +2523,10 @@ function updateRcTkMode() { // `v` carries the field values; returns the CA/EA TLV hex, or null when a // 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). +// 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 +// them with an ADF.USIM entry; the SIM path grants access via the CA Access +// Domain field instead. function stkParamsBuild(v) { const priority = parseInt(v.priority, 10) || 0; const timers = parseInt(v.timers, 10) || 0; @@ -2557,8 +2574,17 @@ function stkParamsBuild(v) { mslField + (tar ? (tar.length / 2).toString(16).padStart(2, '0') + tar : '00') + services.toString(16).padStart(2, '0'); - const innerTlv = '80' + berLenStr(tkPayload.length / 2) + tkPayload; - return 'EA' + berLenStr(innerTlv.length / 2) + innerTlv; + 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): [file system AID length 00 = shared file system] + // [Access Domain length 01][ADP 00 = full access], optionally with an + // ADF entry [AID length 07][ADF.USIM][AD length 01][ADP 00]. + let acc = '000100'; + if (v.adfAccess) acc += '07A00000008710020100'; + eaValue += '82' + berLenStr(acc.length / 2) + acc; + } + return 'EA' + berLenStr(eaValue.length / 2) + eaValue; } // The RAM install form's toolkit fields -> the install parameters hex. @@ -2573,6 +2599,8 @@ function buildRcToolkitParams() { lastPos: g('rc-tk-lastpos'), lastId: g('rc-tk-lastid'), channels: g('rc-tk-channels'), msl: g('rc-tk-msl'), tar: g('rc-tk-tar'), ad: g('rc-tk-ad'), services: g('rc-tk-services'), + fsAccess: document.getElementById('rc-tk-fsaccess').checked, + adfAccess: document.getElementById('rc-tk-adfaccess').checked, }); } @@ -3437,6 +3465,12 @@ function chainRamToolkitHtml(chainId, idx, f, uf) { } else { html += '
' + '
'; + html += '
'; + html += '
'; } html += ''; return html; @@ -4055,6 +4089,7 @@ function chainRamBuildRowHex(idx, row) { lastPos: f.tkLastpos, lastId: f.tkLastid, channels: f.tkChannels, msl: f.tkMsl || '16', tar: f.tkTar, ad: f.tkAd, services: f.tkServices, + fsAccess: f.tkFsAccess, adfAccess: f.tkAdfAccess, }); } // INSTALL/LOAD are case-3 commands (no trailing Le), matching the @@ -16391,6 +16426,8 @@ const LANG_RU = { 'Last menu ID (hex)': 'ID последнего пункта меню (hex)', 'Channels (max)': 'Каналы (макс)', 'Access domain (hex)': 'Домен доступа (hex)', + 'File system access (full)': 'Доступ к файловой системе (полный)', + 'ADF.USIM access (full)': 'Доступ к ADF.USIM (полный)', 'Load data (hex)': 'Данные загрузки (hex)', 'Block number': 'Номер блока', 'Encryption': 'Шифрование', diff --git a/frontend/sw.js b/frontend/sw.js index 89819fa..bd5f77b 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v283'; +const CACHE = 'simple-v284'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/stk_params.test.js b/frontend/tests/stk_params.test.js index 03d334c..f9ac60d 100644 --- a/frontend/tests/stk_params.test.js +++ b/frontend/tests/stk_params.test.js @@ -102,7 +102,7 @@ test('every toolkit field regenerates the STK parameters hex on edit', () => { assert.ok(/on(?:input|change)="[^"]*updateStkParamsHex/.test(m[0]), m[1] + ' does not refresh the hex: ' + m[0]); } - assert.strictEqual(seen.length, 14, 'expected 14 toolkit fields, got ' + seen.join(', ')); + assert.strictEqual(seen.length, 16, 'expected 16 toolkit fields, got ' + seen.join(', ')); }); test('the applet TAR field has no B00001 default or placeholder', () => { @@ -127,11 +127,13 @@ function fakeForm(values) { const ids = ['rc-toolkit-enable', 'rc-tk-mode', 'rc-tk-priority', 'rc-tk-timers', 'rc-tk-textlen', 'rc-tk-menus', 'rc-tk-firstpos', 'rc-tk-firstid', 'rc-tk-lastpos', 'rc-tk-lastid', 'rc-tk-channels', 'rc-tk-msl', - 'rc-tk-tar', 'rc-tk-ad', 'rc-tk-services', 'ram-stk-params']; + 'rc-tk-tar', 'rc-tk-ad', 'rc-tk-services', 'rc-tk-fsaccess', + 'rc-tk-adfaccess', 'ram-stk-params']; + const checks = ['rc-toolkit-enable', 'rc-tk-fsaccess', 'rc-tk-adfaccess']; const els = {}; for (const id of ids) els[id] = { value: '', checked: false, dataset: {} }; for (const [id, v] of Object.entries(values || {})) { - if (id === 'rc-toolkit-enable') els[id].checked = v; + if (checks.indexOf(id) >= 0) els[id].checked = !!v; else els[id].value = v; } globalThis.document = { getElementById: id => els[id] || null }; @@ -147,6 +149,34 @@ test('the RAM form fields build the live install parameters end to end', () => { assert.strictEqual(buildRcToolkitParams(), 'EA0F800D000000000102011203AF4D0100'); }); +test('UICC file-access parameters (82) are appended in EA mode', () => { + // TS 102 226 8.2.1.3.2.2.2: [file system AID len 00 = shared FS] + // [Access Domain len 01][ADP 00 = full access]; the SIM path grants the + // same rights via the CA Access Domain field. The ADF entry is an + // extension of the file-system entry. + const base = vals({ channels: '1', msl: '12', tar: 'AF4D01' }); + assert.strictEqual(stkParamsBuild(Object.assign({}, base, { fsAccess: true })), + 'EA14800D000000000102011203AF4D01008203000100'); + assert.strictEqual( + stkParamsBuild(Object.assign({}, base, { fsAccess: true, adfAccess: true })), + 'EA1E800D000000000102011203AF4D0100820D00010007A00000008710020100'); + assert.strictEqual(stkParamsBuild(base), 'EA0F800D000000000102011203AF4D0100'); + assert.strictEqual(stkParamsBuild(Object.assign({}, base, { adfAccess: true })), + 'EA0F800D000000000102011203AF4D0100'); +}); + +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(), + 'EA14800D000000000102011203AF4D01008203000100'); + 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(), + 'EA1E800D000000000102011203AF4D0100820D00010007A00000008710020100'); +}); + test('updateStkParamsHex refreshes the field and clears the manual flag', () => { const els = fakeForm({ 'rc-toolkit-enable': true, 'rc-tk-mode': 'ea', 'rc-tk-tar': 'AF4D01' }); diff --git a/pyproject.toml b/pyproject.toml index ed757bd..1f9d0a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.6.10" +version = "3.6.11" 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 3729fd2..ff26824 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.10' +VERSION = '3.6.11' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE