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