Files
otaman/frontend/sw.js
T
catarrh 7ece8a531c Response parser: add TS 51.011 SIM status word families; v1.9.6
OTA inner-APDU SWs from SIM-domain applications surfaced as 'Unknown
status word' (e.g. 9404 on a failed SELECT inside an authenticated
B00000 packet). SW_MAP.generic gains the complete TS 51.011 §9.4
families, so they decode in every resp-cmd context:

- 9200/9240 memory management (retry / memory problem)
- 9400/9402/9404/9408 referencing management (no EF selected,
  out of range, file ID/pattern not found, file inconsistent)
- 9802/9804/9808/9810/9840/9850 security management (CHV/access-
  condition/invalidation contradictions and blocked states; 9850
  also added to generic alongside its gp/uicc copy)
- 9EXX/9FXX data-download error length / response length wildcards

Exact keys safely coexist with the 92XX proactive wildcard (exact
match wins). TS 102 221 sweep found no further stragglers: 63C0-C9
PIN retry counters are covered by the existing 63CX wildcard.

Secured packet page: enforce fresh packet per send
- any change to APDU/TAR/CNTR/keys/padding/SPI now clears the secured
  packet textarea via spInvalidate(), so Send cannot reuse a stale
  packet ('No secured packet to send' guards the path)
- on por_ok the CNTR field auto-increments (10-digit hex normalized,
  wraps modulo 2^40) and the textarea clears; security-error verdicts
  leave both untouched for retry after fixing the cause

SIM/USIM SELECT: P1/P2 per spec + live RFM idioms
- USIM FID selects requested no response data (P2=0C) - that coding is
  reserved for the select-MF-by-empty-data special case; ordinary FID
  selects now request the FCP template (P2=04) with Le='00' per
  TS 102 221 Table 11.2 / pySim sel_ctrl convention; preset tables
  fixed in both genSimUsim and updateP1P2Display
- path method gains base selector: from MF (P1=08) / from current DF
  (P1=09) - reproduces the dominant live RFM idiom 09/0C
- 'silent' checkbox on fid/path/chain emits P2=0C without Le for hops
  where the FCI is not needed
- chain syntax gains GET RESPONSE hops: 'C0' emits CLA C0 00 00 00 and
  'C0:NN' sets explicit Le, enabling classic SELECT -> 9FXX ->
  GET RESPONSE pairs in a single secured packet so the PoR carries the
  actual FCI/response bytes instead of a bare length SW
- SW dictionary: 9FXX reworded to point at GET RESPONSE
- placeholders show the new chain syntax
- findings & backport decisions written to ~/WSL/RFM_notes.md

response_map.test.js: +8 assertions across the new families.
sim.test.js: USIM FID expectation updated; silent/base/chain cases.
apdu_parse.test.js: live capture lines 1-3 as regression fixtures.
Version 1.9.5 -> 1.9.6 everywhere; SW cache otaman-v16 -> otaman-v17
2026-08-24 23:04:54 +03:00

56 lines
1.4 KiB
JavaScript

const CACHE = 'otaman-v17';
const URLS = [
'index.html',
'help.html',
'help-ru.html',
'style.css',
'sim.png',
'favicon.svg',
'icon-192.png',
'icon-512.png',
'manifest.json',
'des-bundle.js',
'aes-bundle.js',
'sim.svg',
'sim_anim.svg',
'nosim.svg',
];
self.addEventListener('install', e => {
e.waitUntil(
caches.open(CACHE).then(c => c.addAll(URLS))
);
self.skipWaiting();
});
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
).then(() => clients.claim())
);
});
self.addEventListener('fetch', e => {
if (!e.request.url.startsWith('http')) return;
if (new URL(e.request.url).pathname.startsWith('/api/')) return; // live data, never cache
if (e.request.method !== 'GET') return;
const isNavigate = e.request.mode === 'navigate' || e.request.url.endsWith('sw.js');
if (isNavigate) {
e.respondWith(
fetch(e.request).then(res => {
const clone = res.clone();
caches.open(CACHE).then(c => c.put(e.request, clone));
return res;
}).catch(() => caches.match(e.request))
);
} else {
e.respondWith(
caches.match(e.request).then(r => r || fetch(e.request).then(res => {
const clone = res.clone();
caches.open(CACHE).then(c => c.put(e.request, clone));
return res;
}))
);
}
});