v3.13.2: ELF versions derived through the package's module AIDs

ramDeriveElfVersions matches an applet instance to its package through a
shared ramElfAppMatch: the app's C4 (Load File AID) when the card reports
the real one, then the package's module AIDs (the applet instance AIDs),
then the shared-prefix fallback (the same tiers ramElfAppletCandidate uses
for the per-AID queries).  Live 2026-10-02 (test-eSIM): the card returns
C4 = the applet's own AID (c4 10 ...3908 for package ...3900), so the old
C4-only derivation filled nothing; the module lists map ...3900 -> ...3908
(9.3), ...0200 -> ...0208/...494D4508 (1.1) and F0414C46416001 ->
F0414C4641600101 (0.1).

The ELF version hint names the remaining case: a package without an
applet instance has no version to derive either (the Oracle
A00000006203010101 stays bare - the card refuses every ELF tag-list form,
so its CE is unreachable).

Docs: help EN/RU, repo AGENTS (v3.13.2 note).  sw cache simple-v372.
Tests: 667 Python + 796 frontend green.
This commit is contained in:
2026-10-02 13:16:08 +03:00
parent 5b8324ed02
commit 2c49c54d2d
7 changed files with 95 additions and 31 deletions
+47 -1
View File
@@ -26,7 +26,7 @@ const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamB
'ramFormatIsExpanded', 'ramFmtLadder',
'stkParamsBuild', 'ramRemoteSwOk', 'spPorAccepted', 'ramIncrementCntr', 'ramDeleteFromExplorer', 'ramListingSpi2', 'ramRemoveFromExplorer', 'ramHasInstance', 'ramExpandedQueryApdu',
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'ramParseModuleAids', 'parseTLV', '_parseE3Entry', '_parseMenuEntries',
'ramExpandedReport', 'ramExpandedMore', 'ramPageFormats', 'ramAppsMissingDetails', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate',
'ramExpandedReport', 'ramExpandedMore', 'ramPageFormats', 'ramAppsMissingDetails', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate', 'ramElfAppMatch',
'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow',
'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'spPresetIdx', 'tarPresetIdx',
@@ -636,6 +636,47 @@ test('ramDeriveElfVersions reads a package version from its applet instance', ()
assert.strictEqual(elfs[2].version, undefined);
});
test('ramDeriveElfVersions falls back to the package module AIDs', () => {
// live 2026-10-02 (test-eSIM): the card returns C4 = the applet's own AID,
// so the C4 rule matches nothing - the package's module AIDs (the applet
// instance AIDs) are the mapping that works there
const elfs = [
{ aid: 'A1130001180001FFFFFFFF89A1003900', moduleAids: ['A1130001180001FFFFFFFF89A1003908'] },
{ aid: 'A1130001180002FFF7100E8904000200', moduleAids: ['A1130001180002FFF7100E8904000208', 'A1130001180002FFF7100E89494D4508'] },
{ aid: 'F0414C46416001', moduleAids: ['F0414C4641600101'] },
{ aid: 'A00000006203010101', moduleAids: [] },
];
const apps = [
{ aid: 'A1130001180001FFFFFFFF89A1003908', version: '0903', elfAid: 'A1130001180001FFFFFFFF89A1003908' },
{ aid: 'A1130001180002FFF7100E8904000208', version: '0101', elfAid: 'A1130001180002FFF7100E8904000208' },
{ aid: 'A1130001180002FFF7100E89494D4508', version: '0101', elfAid: 'A1130001180002FFF7100E89494D4508' },
{ aid: 'F0414C4641600101', version: '0001', elfAid: 'F0414C4641600101' },
];
ramDeriveElfVersions(elfs, apps);
assert.strictEqual(elfs[0].version, '0903');
assert.strictEqual(elfs[0].versionFrom, 'app');
assert.strictEqual(elfs[1].version, '0101');
assert.strictEqual(elfs[2].version, '0001');
// no applet instance: nothing to derive from
assert.strictEqual(elfs[3].version, undefined);
});
test('ramElfVersionHint names a package with no applet instance', () => {
globalThis.t = s => s;
const elfs = [{ aid: 'A1130001180001FFFFFFFF89A1003900', moduleAids: ['A1130001180001FFFFFFFF89A1003908'] }];
const apps = [{ aid: 'A1130001180001FFFFFFFF89A1003908', version: '0903' }];
// a versionless package with a matching applet instance needs no clause
const ok = ramElfVersionHint('notags', elfs, apps);
// ...but a package with no applet instance is called out
const orphan = ramElfVersionHint('notags', [{ aid: 'A00000006203010101', moduleAids: [] }], apps);
// the base hints stay form-aware
const base = ramElfVersionHint('tags', [], []);
delete globalThis.t;
assert.match(ok, /no-tag-list form\.$/);
assert.match(orphan, /no version to derive either\.$/);
assert.match(base, /did not return them\.$/);
});
test('ramElfAppletCandidate maps a package to its applet instance', () => {
// live 2026-09-30: the applet instance AIDs are the package's module AIDs
// (they differ from the package AID in the last byte)
@@ -659,6 +700,11 @@ test('ramElfAppletCandidate maps a package to its applet instance', () => {
assert.strictEqual(ramElfAppletCandidate(null, apps), null);
// the shared-prefix rule is the fallback
assert.strictEqual(ramElfAppletCandidate({ aid: 'ABCD' }, [{ aid: 'ABCDEF' }]).aid, 'ABCDEF');
// the app's C4 (Load File AID) is the authoritative mapping when the card
// reports the real package AID
assert.strictEqual(ramElfAppletCandidate({ aid: 'A0000005591010FFFFFFFF8900000E00' },
[{ aid: 'A0000005591010FFFFFFFF8900001200', elfAid: 'A0000005591010FFFFFFFF8900000E00' }]).aid,
'A0000005591010FFFFFFFF8900001200');
});
test('the explorer annotates a derived package version', () => {