diff --git a/docs/scp81-findings.md b/docs/scp81-findings.md index 0b8abb8..03336ff 100644 --- a/docs/scp81-findings.md +++ b/docs/scp81-findings.md @@ -245,6 +245,20 @@ was SW 6A80 (incorrect parameters in data field). Working INSTALL [for install] example (compact): `80E60C002E07AA1902BC22580108AA1902BC2258010108AA1902BC22580101010010C900EA0C800A00000F010000000000000000` +## RESOLVED 2026-09-16f: SW CAFE pagination used the wrong P2 (02 instead of 03) + +**Root cause:** the continuation GET STATUS used `P2=02`, which Table 11-34 +(GP Card Spec 2.3.1) defines as "**Get first or all occurrence(s)**" - the +card returned the first listing again (with the search criterion's single +match), so every listing appeared to end after one extra page and newly +installed/registered entries were invisible (the installed package +`AA1902BC225801` was missing from the ELF registry). The correct value is +`P2=03` = "**Get next occurrence(s)**". + +**Fix:** `_scp81_continuation` emits `80F2 03 4F 00` +and the earlier duplicate entry per page is gone (the criterion entry is no +longer re-returned). + ## Next tests / work 1. **UI:** group the per-page R-APDUs under their logical command in the diff --git a/frontend/index.html b/frontend/index.html index f92c75d..00f10c0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,7 +18,7 @@
-

OTAMan SIM OTA with a Human Face v2.1.14

+

OTAMan SIM OTA with a Human Face v2.1.15

diff --git a/frontend/sw.js b/frontend/sw.js index f3daf9e..3d4cea2 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v151'; +const CACHE = 'otaman-v152'; const URLS = [ 'index.html', 'help.html', diff --git a/pyproject.toml b/pyproject.toml index 35d4a50..c5daf5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-otaman-server" -version = "2.1.14" +version = "2.1.15" description = "HTTP REST server wrapping pysim for the OTAMan PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_otaman_server/server.py b/pysim_otaman_server/server.py index 0b3493d..19963f4 100644 --- a/pysim_otaman_server/server.py +++ b/pysim_otaman_server/server.py @@ -21,7 +21,7 @@ from osmocom.construct import GsmOrUcs2Adapter from osmocom.tlv import BER_TLV_IE -VERSION = '2.1.14' +VERSION = '2.1.15' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE @@ -1548,7 +1548,11 @@ def _scp81_continuation(apdu, rapdu): if not aid: return None lc = 2 + len(aid) - return '80F2%s02%02X4F%02X%s00' % (u[4:6], lc, len(aid), aid.hex().upper()) + # P2=03 = "get next occurrence(s)" (Table 11-34); P2=02 ("first or all") + # made the card return the first listing again, so every continuation + # page repeated its search criterion and the scan stopped early - the + # newly installed package never appeared in the registry. + return '80F2%s03%02X4F%02X%s00' % (u[4:6], lc, len(aid), aid.hex().upper()) def _scp81_script_responder(method, target, headers, body): diff --git a/tests/test_scp81.py b/tests/test_scp81.py index eac9dbf..ee3fa90 100644 --- a/tests/test_scp81.py +++ b/tests/test_scp81.py @@ -679,7 +679,7 @@ class TargetedAppTest(unittest.TestCase): def test_continuation_builds_next_occurrence_apdu(self): page = bytes.fromhex('E3114F08A0000000030000009F70010FC50100') self.assertEqual(server._scp81_continuation('80F24002024F0000', page), - '80F240020A4F08A00000000300000000') + '80F240030A4F08A00000000300000000') self.assertIsNone(server._scp81_continuation('80CAFF2100', page)) def test_cafe_page_auto_continuation(self): @@ -698,9 +698,9 @@ class TargetedAppTest(unittest.TestCase): 'POST', '/api/scp81?req=1', {'x-admin-script-status': 'ok'}, body) # The continuation was appended and sent as the next command. self.assertEqual(server._SCP81_SCRIPT[1], - '80F24002094F07A000000151535000') + '80F24003094F07A000000151535000') self.assertEqual(status, 200) - self.assertIn(bytes.fromhex('80F24002094F07A000000151535000'), out) + self.assertIn(bytes.fromhex('80F24003094F07A000000151535000'), out) finally: server._SCP81_SCRIPT = list(server._SCP81_SCRIPTS['explore']) server._SCP81_SCRIPT_SENT = 0 @@ -712,7 +712,7 @@ class TargetedAppTest(unittest.TestCase): server._SCP81_SCRIPT = ['80F24002024F0000'] server._SCP81_SCRIPT_SENT = 1 server._SCP81_SCRIPT_RESULTS = [] - server._SCP81_SCRIPT_INSERTED = ['80F240020A4F08A00000000300000000'] + server._SCP81_SCRIPT_INSERTED = ['80F240030A4F08A00000000300000000'] server._SCP81_PAGES = 1 try: page = bytes.fromhex('E3114F08A0000000030000009F70010FC50100')