diff --git a/frontend/help-ru.html b/frontend/help-ru.html index 98c8a26..1899dd9 100644 --- a/frontend/help-ru.html +++ b/frontend/help-ru.html @@ -321,7 +321,7 @@ ОперацияОписание Обзор карты (все данные GP)Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 (расширенные ресурсы карты: число установленных приложений и свободная энергонезависимая/энергозависимая память). Если карта отвергает компактный формат листинга (P2.b2=0, устаревший по GP), запросы повторяются в расширенном формате E3; флажок Запрашивать расширенные данные реестра (E3) дополнительно запрашивает их и объединяет расширенные поля (привилегии, связи ELF/SD, списки модулей). Результаты отображаются в обзоре с кнопками Удалить для каждого элемента и определённым форматом команд RAM. - Установка пакета (.cap файл)Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера размер блока LOAD (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой .cap просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается оценка требований к памяти: требование NVRAM (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны библиотеки, с которыми слинкован CAP (его компонент Import: имя библиотеки, AID и минимальная версия ≥ x.y — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам Package, Requires, Memory и Components. Строка Platform показывает минимальный уровень Java Card, который должна предоставить карта (выводится из импорта javacard.framework; флаг int в заголовке дополнительно требует поддержки int — Java Card 2.2+): JCRE проверяет каждый импорт по своим пакетам при верификации load-файла (JC VM spec 4.5.2), поэтому карта со старой платформой отклоняет LOAD сразу после завершения компонента Import (в строке шага виден remote SW карты и компонент, который завершил блок, например завершает Import) — меньший размер блока LOAD помогает только если блок превышает буфер карты. Набор «Проба импортов» (диагностика) переписывает запрошенные версии импортов CAP перед загрузкой — все записи в 0.0 или по одной: карта проверяет этот список при верификации load-файла, поэтому это показывает, является ли он причиной отказа, а перебором — какой пакет и какую версию карта не поддерживает. Меняются только два байта версии на запись (длина load-файла, размеры компонентов и все смещения остаются прежними), проба никогда не используется для настоящей установки, а применённые изменения видны в строке результата и в журнале сервера. Использование в два шага: нажмите «Все 0.0» и запустите установку — если LOAD теперь проходит, карта отклоняла список импортов; затем возвращайте по одной строке значение из CAP (например, A0000000620101=1.3) и повторяйте: строка, после которой снова происходит отказ, указывает пакет, который карта не может предоставить в этой версии. Повреждённый файл не проходит анализ, и Execute остаётся недоступной до успешного анализа. Оценка носит справочный характер и никогда не используется при установке: NVRAM — это образ кода плюс постоянные данные (сумма в стиле C6+C8 по GP Table 11-48; управление памятью карты, округление и запись в реестре не учитываются, а образ статических полей входит в обе части); рекомендуемый C6 следует за load-файлом, а рекомендуемые квоты — это десятичные значения в байтах (шестнадцатеричное значение в скобках — это кодирование параметров установки C7/C8: C6 — энергонезависимый код, C7 — энергозависимая RAM, C8 — энергонезависимые данные). + Установка пакета (.cap файл)Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера размер блока LOAD (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой .cap просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается оценка требований к памяти: требование NVRAM (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны библиотеки, с которыми слинкован CAP (его компонент Import: имя библиотеки, AID и минимальная версия ≥ x.y — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам Package, Requires, Memory и Components. Строка Platform показывает минимальный уровень Java Card, который должна предоставить карта (выводится из импорта javacard.framework; флаг int в заголовке дополнительно требует поддержки int — Java Card 2.2+), и релиз ETSI, из которого пришли импорты UICC API (версия uicc.toolkit → REL-N по корпусу sim-tools в рабочем каталоге; основание показано в скобках): JCRE проверяет каждый импорт по своим пакетам при верификации load-файла (JC VM spec 4.5.2), поэтому карта со старой платформой отклоняет LOAD сразу после завершения компонента Import (в строке шага виден remote SW карты и компонент, который завершил блок, например завершает Import) — меньший размер блока LOAD помогает только если блок превышает буфер карты. Набор «Проба импортов» (диагностика) переписывает запрошенные версии импортов CAP перед загрузкой — все записи в 0.0 или по одной: карта проверяет этот список при верификации load-файла, поэтому это показывает, является ли он причиной отказа, а перебором — какой пакет и какую версию карта не поддерживает. Меняются только два байта версии на запись (длина load-файла, размеры компонентов и все смещения остаются прежними), проба никогда не используется для настоящей установки, а применённые изменения видны в строке результата и в журнале сервера. Использование в два шага: нажмите «Все 0.0» и запустите установку — если LOAD теперь проходит, карта отклоняла список импортов; затем возвращайте по одной строке значение из CAP (например, A0000000620101=1.3) и повторяйте: строка, после которой снова происходит отказ, указывает пакет, который карта не может предоставить в этой версии. Повреждённый файл не проходит анализ, и Execute остаётся недоступной до успешного анализа. Оценка носит справочный характер и никогда не используется при установке: NVRAM — это образ кода плюс постоянные данные (сумма в стиле C6+C8 по GP Table 11-48; управление памятью карты, округление и запись в реестре не учитываются, а образ статических полей входит в обе части); рекомендуемый C6 следует за load-файлом, а рекомендуемые квоты — это десятичные значения в байтах (шестнадцатеричное значение в скобках — это кодирование параметров установки C7/C8: C6 — энергонезависимый код, C7 — энергозависимая RAM, C8 — энергонезависимые данные). diff --git a/frontend/help.html b/frontend/help.html index 4c70df1..b7478de 100644 --- a/frontend/help.html +++ b/frontend/help.html @@ -320,7 +320,7 @@ OperationDescription Explore Card (all GP data)Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 (Extended Card Resources: the number of installed applications and the free non-volatile/volatile memory). If a card rejects the compact listing (P2.b2=0, deprecated by GP) the queries are retried in the expanded E3 form; the Query expanded registry details (E3) checkbox also requests it as an extra pass and merges the expanded-only fields (privileges, ELF/SD associations, module lists). Results appear in an explorer view with per-item Delete buttons and the detected RAM command format. - Install Package (.cap file)Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the LOAD block size (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large .cap simply takes several SMS. As soon as the file is selected it is validated and its memory requirements are estimated in the form: the NVRAM requirement (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the libraries the CAP is linked against (its Import component: library name, AID and the minimum version ≥ x.y, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the Package, Requires, Memory and Components headings. The Platform line names the minimum Java Card level the card must provide (derived from the javacard.framework import; the header int flag additionally requires int support, Java Card 2.2+): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the Import component is complete (the step line carries the card's remote SW and names the component the block completed, e.g. completes Import) — a smaller LOAD block only helps when the block exceeds the card's buffer. The Import probe fieldset (diagnostic) rewrites the CAP's requested import versions before the load — all entries to 0.0, or one at a time: since the card checks that list while it verifies the load file, this shows whether it is the rejection cause and, by bisection, which package and version the card lacks. Only the two version bytes per entry change (the load file length, the component sizes and every offset stay untouched), the probe is never used for a real install, and the applied changes are reported in the result line and the server log. Two-step use: press Set all 0.0 and run the install — if the LOAD now passes, the card was refusing the import list; then put one line back to its CAP value at a time (e.g. A0000000620101=1.3) and retry: the line that makes it fail again names the package the card cannot provide at that version. A corrupt or wrong-format file fails the analysis, and Execute stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 — card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested C6 follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data). + Install Package (.cap file)Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the LOAD block size (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large .cap simply takes several SMS. As soon as the file is selected it is validated and its memory requirements are estimated in the form: the NVRAM requirement (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the libraries the CAP is linked against (its Import component: library name, AID and the minimum version ≥ x.y, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the Package, Requires, Memory and Components headings. The Platform line names the minimum Java Card level the card must provide (derived from the javacard.framework import; the header int flag additionally requires int support, Java Card 2.2+) and the ETSI release the UICC API imports come from (uicc.toolkit version → REL-N, per the workspace sim-tools corpus; shown with its basis in brackets): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the Import component is complete (the step line carries the card's remote SW and names the component the block completed, e.g. completes Import) — a smaller LOAD block only helps when the block exceeds the card's buffer. The Import probe fieldset (diagnostic) rewrites the CAP's requested import versions before the load — all entries to 0.0, or one at a time: since the card checks that list while it verifies the load file, this shows whether it is the rejection cause and, by bisection, which package and version the card lacks. Only the two version bytes per entry change (the load file length, the component sizes and every offset stay untouched), the probe is never used for a real install, and the applied changes are reported in the result line and the server log. Two-step use: press Set all 0.0 and run the install — if the LOAD now passes, the card was refusing the import list; then put one line back to its CAP value at a time (e.g. A0000000620101=1.3) and retry: the line that makes it fail again names the package the card cannot provide at that version. A corrupt or wrong-format file fails the analysis, and Execute stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 — card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested C6 follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data). diff --git a/frontend/index.html b/frontend/index.html index b293e63..efc9d20 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1747,7 +1747,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.43'; +const SIMPLE_VERSION = '3.6.44'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -8499,6 +8499,12 @@ function capMemHtml(mem) { (fw ? JC_FRAMEWORK_SDK[(Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0)] : ''); let against = sdk ? 'Java Card \u2265 ' + sdk : (fw ? 'javacard.framework \u2265 ' + (Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0) : ''); + // the ETSI release the UICC API imports come from (capmem: uicc.toolkit + // version -> REL-N per the workspace sim-tools corpus), basis in brackets + if (mem.requires_etsi_release) { + against += (against ? ' \u00b7 ' : '') + 'ETSI ' + mem.requires_etsi_release + + (mem.requires_etsi_basis ? ' (' + mem.requires_etsi_basis + ')' : ''); + } if (mem.cap_version) against += (against ? ' \u00b7 ' : '') + t('CAP format') + ' ' + mem.cap_version; if (mem.needs_int) against += (against ? ' \u00b7 ' : '') + t('int support'); if (against) pkgRows += row(label(esc(t('Platform'))) + cell(esc(against), 'pl-4')); diff --git a/frontend/sw.js b/frontend/sw.js index 3523c8a..62b1ceb 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v316'; +const CACHE = 'simple-v317'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/capmem.test.js b/frontend/tests/capmem.test.js index 9885fc0..6bb5fac 100644 --- a/frontend/tests/capmem.test.js +++ b/frontend/tests/capmem.test.js @@ -148,6 +148,18 @@ test('capMemHtml shows the server platform requirement and int support', () => { assert.ok(!out.includes('2.1.1/2.1.2'), out); }); +test('capMemHtml names the ETSI release the UICC API imports come from', () => { + const mem = memFixture(); + mem.requires_java_card = '2.2.2'; + mem.requires_etsi_release = 'REL-7'; + mem.requires_etsi_basis = 'uicc.toolkit 1.4'; + const out = capMemHtml(mem); + assert.ok(out.includes('Java Card \u2265 2.2.2 \u00b7 ETSI REL-7 (uicc.toolkit 1.4) \u00b7 CAP format 2.1'), out); + // a CAP without uicc.toolkit (or an unmapped version) shows no ETSI part + const noEtsi = capMemHtml(memFixture()); + assert.ok(!noEtsi.includes('ETSI'), noEtsi); +}); + test('capMemHtml falls back to the raw framework version outside the corpus', () => { const mem = memFixture(); mem.imports = [{ aid: 'A0000000620101', minor: 0, major: 7, refs: 6 }]; diff --git a/pyproject.toml b/pyproject.toml index 1534e32..d082065 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.6.43" +version = "3.6.44" 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/capmem.py b/pysim_simple_server/capmem.py index 84d2538..2b8c4d7 100755 --- a/pysim_simple_server/capmem.py +++ b/pysim_simple_server/capmem.py @@ -1458,6 +1458,22 @@ JC_FRAMEWORK_JDK = { '1.4': '3.0.3', '1.5': '3.0.4', '1.6': '3.0.5', } +# uicc.toolkit (ETSI TS 102 241, UICC API for Java Card) export version -> the +# ETSI/3GPP release the library comes from. Sources: the workspace corpus - +# sim-tools/ETSI-REL.md (the prepared trees: api21_rel7_export_files carries +# uicc.toolkit 1.4 = the REL-7 jar, api21_export_files 1.11 = the REL-12 jar) +# and sim-tools/etsi/102241/README.md's package-version table (spec version -> +# toolkit version, read from the Annex B exports: 1.0 spans 06.00.01-07.02.00, +# 1.11 spans 13.01.00-14.02.00, ...). Only uicc.toolkit has a documented +# mapping - the SIM API (sim.toolkit, TS 102 230) corpus is not mirrored, so a +# SIM-only CAP gets no release label. +UICC_TOOLKIT_AID = 'A0000000090005FFFFFFFF8912000000' +ETSI_TOOLKIT_RELEASE = { + '1.0': 'REL-6/7', '1.1': 'REL-7', '1.2': 'REL-7', '1.3': 'REL-8', + '1.4': 'REL-7', '1.5': 'REL-9', '1.11': 'REL-12', + '1.12': 'REL-15/16/17', '1.13': 'REL-17', '1.14': 'REL-17/18', +} + def analyze_bytes(cap_bytes, verbose=False): """Analyze a CAP archive (bytes) and return (report, memory).""" @@ -1502,6 +1518,13 @@ def analyze_bytes(cap_bytes, verbose=False): if fw: report['requires_framework'] = f'{fw.major}.{fw.minor}' report['requires_java_card'] = JC_FRAMEWORK_JDK.get(report['requires_framework']) + # ETSI release the UICC API comes from (uicc.toolkit import version) + tk = next((p for p in cap.components['import'].packages + if p.aid_hex == UICC_TOOLKIT_AID), None) + if tk: + tk_v = f'{tk.major}.{tk.minor}' + report['requires_etsi_basis'] = 'uicc.toolkit %s' % tk_v + report['requires_etsi_release'] = ETSI_TOOLKIT_RELEASE.get(tk_v) # Component sizes (the load file order) for the breakdown display report['components'] = [{'name': name, 'size': size} for name, size in cap.files] @@ -1745,6 +1768,8 @@ def memory_json(report, memory, load_file_bytes=None): # the framework import, and the header int flag's loader requirement 'requires_framework': report.get('requires_framework'), 'requires_java_card': report.get('requires_java_card'), + 'requires_etsi_release': report.get('requires_etsi_release'), + 'requires_etsi_basis': report.get('requires_etsi_basis'), 'needs_int': bool(report.get('needs_int')), 'components': report.get('components', []), 'class_count': report.get('class_count', 0), @@ -1800,6 +1825,9 @@ def format_report(report, memory): if report.get('needs_int'): req += ', int support' lines.append(req) + if report.get('requires_etsi_release'): + lines.append('Requires ETSI %s (%s)' % (report['requires_etsi_release'], + report.get('requires_etsi_basis') or 'uicc.toolkit')) if 'package_version' in report: lines.append(f'Package version: {report["package_version"]}') if 'package_aid' in report: diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index 1359329..bf60d2d 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.43' +VERSION = '3.6.44' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE diff --git a/tests/test_cap_memory.py b/tests/test_cap_memory.py index 44059da..fbd05e5 100644 --- a/tests/test_cap_memory.py +++ b/tests/test_cap_memory.py @@ -332,6 +332,28 @@ class TestCapAnalyzer(unittest.TestCase): self.assertEqual(info['requires_framework'], '2.0') self.assertIsNone(info['requires_java_card']) + def test_etsi_release_from_the_uicc_toolkit_import(self): + uicc = bytes.fromhex('A0000000090005FFFFFFFF8912000000') + # the REL-7 prepared tree carries uicc.toolkit 1.4 + report, _ = capmem.analyze_bytes(build_cap(imports=[(4, 1, uicc)])) + info = capmem.memory_json(report, capmem.compute_memory(report)) + self.assertEqual(info['requires_etsi_release'], 'REL-7') + self.assertEqual(info['requires_etsi_basis'], 'uicc.toolkit 1.4') + # the default (REL-12) tree carries 1.11 + report, _ = capmem.analyze_bytes(build_cap(imports=[(11, 1, uicc)])) + info = capmem.memory_json(report, capmem.compute_memory(report)) + self.assertEqual(info['requires_etsi_release'], 'REL-12') + # an unmapped version keeps the basis, without a release + report, _ = capmem.analyze_bytes(build_cap(imports=[(7, 1, uicc)])) + info = capmem.memory_json(report, capmem.compute_memory(report)) + self.assertIsNone(info['requires_etsi_release']) + self.assertEqual(info['requires_etsi_basis'], 'uicc.toolkit 1.7') + # no uicc.toolkit import -> no ETSI line (SIM-only CAPs have no mapping) + report, _ = capmem.analyze_bytes(build_cap(imports=[(3, 1, JAVACARD_FRAMEWORK)])) + info = capmem.memory_json(report, capmem.compute_memory(report)) + self.assertIsNone(info['requires_etsi_release']) + self.assertIsNone(info['requires_etsi_basis']) + def test_memory_json_nvram_requirement_uses_the_load_file(self): report, memory = capmem.analyze_bytes(rich_cap()) info = capmem.memory_json(report, memory, load_file_bytes=1000)