feat: Explore exposes the rest of the registry data + install NV footprint (v3.6.51)

The compact (legacy) GET STATUS entry is <aid_len><AID><life cycle>
<privileges> + (P1=10) <module count> (<len><module AID>)*, cross-checked
against GlobalPlatformPro's populate_legacy (the 4th byte is privileges;
0x00 is required for load files).  The parser read it as a 'version' one
byte late, so ELF rows showed the module count / the next entry's length as
Version: 00/10 - the compact listing has no version at all; it now comes
from the expanded CE tag (ramFmtVersion: 2 bytes -> major.minor, GP Table
11-37 Note 1) and merges (the bogus compact value no longer blocked it).

New data exposed:
- EA SCP Registry Data (TS 102 226 8.2.1.6) -> 80 Menu Parameters -> the
  applet's STK menu entries: STK menu entry: 0x80 (position 1, Enabled)
- unknown registry objects are preserved (rawTlvs) and shown as
  'Other registry data: 8F=752B . 90=012D ...' instead of being dropped
- 84 parses the GP coding (one Executable Module AID per TLV, repeated; the
  concatenated 4F<len><aid> form stays as fallback) and the ELF expanded
  queries add 84 to the tag list (with a tag list the card returns only the
  listed tags - the fallback path would otherwise lose the module lists and
  their Install buttons)
- life cycle names decode per object kind (GP Tables 11-3..11-6: ISD/card,
  app, SD - detected via the Security Domain privilege - and ELF), with
  multi-byte 9F70 (02 0700 on live cards) decoding the first byte and
  surfacing non-zero extras; the R-APDU parser's LIFECYCLE_MAP labels were
  aligned with the same tables

/api/ram-install measures the NV footprint: a silent GET DATA FF21 read
before and after the chain (also on failure) returns nv_before/nv_after/
nv_delta; the reads never fail the operation (no FF21 -> no fields) and the
PWA shows 'NV used: ~N B (free A -> B)' in the install result.

sw cache simple-v324; tests: 548 Python / 698 Node.
This commit is contained in:
2026-09-30 09:00:33 +03:00
parent be411a7b4b
commit 599d84fd66
11 changed files with 637 additions and 76 deletions
+11 -6
View File
@@ -38,14 +38,19 @@ test('gp 6310 label does not reference removed P2=42 option', () => {
});
test('LIFECYCLE_MAP per GPC v2.3 life cycles', () => {
assert.strictEqual(LIFECYCLE_MAP['01'], 'OP_READY (card) / LOADED (ELF)');
assert.strictEqual(LIFECYCLE_MAP['03'], 'INSTALLED');
assert.strictEqual(LIFECYCLE_MAP['07'], 'SELECTABLE');
assert.strictEqual(LIFECYCLE_MAP['0F'], 'SECURED (card)');
assert.strictEqual(LIFECYCLE_MAP['1F'], 'PERSONALIZED (SD)');
// GP Card Spec v2.3.1 11.1.1 Tables 11-3..11-6: the coding depends on the
// object kind (the ISD inherits the card life cycle), so the shared map
// names every interpretation. The Explore decodes per kind instead
// (ramFmtLifecycle).
assert.strictEqual(LIFECYCLE_MAP['01'], 'OP_READY (card/ISD) / LOADED (ELF)');
assert.strictEqual(LIFECYCLE_MAP['03'], 'INSTALLED (app/SD)');
assert.strictEqual(LIFECYCLE_MAP['07'], 'SELECTABLE (app/SD) / INITIALIZED (card/ISD)');
assert.strictEqual(LIFECYCLE_MAP['0F'], 'SECURED (card/ISD) / PERSONALIZED (SD)');
assert.strictEqual(LIFECYCLE_MAP['1F'], 'App-specific (app)');
assert.strictEqual(LIFECYCLE_MAP['7F'], 'CARD_LOCKED');
assert.strictEqual(LIFECYCLE_MAP['FF'], 'TERMINATED');
assert.strictEqual(LIFECYCLE_MAP['83'], 'LOCKED (SD)');
assert.strictEqual(LIFECYCLE_MAP['83'], 'LOCKED');
assert.strictEqual(LIFECYCLE_MAP['8F'], 'LOCKED');
});
test('TS 51.011 SIM families resolve (94xx/98xx/92xx/9Exx/9Fxx)', () => {