Decode cross-spec "reserved" values via the sibling specs

- REFRESH '07'/'08': use the TS 31.111 §8.6 names ("Steering of Roaming" /
  "... for I-WLAN", per TS 23.122 / TS 24.234) instead of "Reserved (...)"
- envelope tags 'E1'-'E3': TS 31.111 §9.1 leaves them reserved for future
  usage — drop the placeholder label and decode them as unknown
- TR result '28' = Bearer Independent Protocol temporary error (TS 102 223
  §8.12); '29' stays reserved
- PLI qualifier '0B' = MEID of the terminal added; '05' documented as
  TS 31.111's Timing Advance (ETSI text says "Reserved for GSM")
- ISO 7816-4:2005 Table 6: add '62 86' (sensor) and '63 81' (file filled)
- drop the two fabricated '66xx' names (no such rows in the pinned specs)
- tests: add the new value tests plus a guard that no decoder table emits
  a "Reserved ..." label
- v2.1.1; SW cache simtrace-analyser-v56
This commit is contained in:
catarrh committed 2026-09-20 14:06:40 +03:00
1 parent 561801a983
commit d4a378695c
7 files changed
+78 -13

No files matched your search

+1 -1
View File
@@ -2706,7 +2706,7 @@ function onFilterChange() {
}
// ──────────────────── Versioning ────────────────────
const PWA_VERSION = '2.1.0';
const PWA_VERSION = '2.1.1';
const MIN_SERVER_VERSION = '1.0.0';
function parseVersion(v) {
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v55';
const CACHE = 'simtrace-analyser-v56';
const PRECACHE = [
'index.html',
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "simtrace-analyser"
version = "2.1.0"
version = "2.1.1"
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"}
+10 -8
View File
@@ -17,18 +17,18 @@ SW_NAMES = {
(0x62, 0x83): 'Warning — selected file deactivated',
(0x62, 0x84): 'Warning — FCI not formatted per ISO',
(0x62, 0x85): 'Warning — selected file in termination state',
(0x62, 0x86): 'No input data available from a sensor on the card',
(0x62, 0xf1): 'More data available',
(0x62, 0xf2): 'More data available and proactive command pending',
(0x62, 0xf3): 'Response data available',
(0x63, 0x00): 'Warning — no information',
(0x63, 0x81): 'File filled up by the last write',
(0x63, 0xf1): 'More data expected',
(0x63, 0xf2): 'More data expected and proactive command pending',
(0x64, 0x00): 'Execution error — no information',
(0x64, 0x01): 'Execution error — immediate response required',
(0x65, 0x00): 'Execution error — no information, NV memory changed',
(0x65, 0x81): 'Execution error — memory failure',
(0x66, 0x00): 'Security error — no information',
(0x66, 0x81): 'Reserved for security-related issues',
(0x67, 0x00): 'Checking error — wrong length',
(0x68, 0x00): 'Checking error — no information',
(0x68, 0x81): 'Logical channel not supported',
@@ -1030,8 +1030,8 @@ REFRESH_MODES = {
0x04: 'UICC Reset',
0x05: 'NAA Application Reset',
0x06: 'NAA Session Reset',
0x07: 'Reserved (Steering of Roaming)',
0x08: 'Reserved (Steering of Roaming for I-WLAN)',
0x07: 'Steering of Roaming',
0x08: 'Steering of Roaming for I-WLAN',
0x09: 'eUICC Profile State Change',
0x0A: 'Application Update',
}
@@ -1053,9 +1053,8 @@ ENVELOPE_TYPES = {
0xDE: 'ENVELOPE CONTAINER',
0xDF: 'PROSE REPORT',
0xE0: '5G PROSE REPORT',
0xE1: 'Reserved for 3GPP',
0xE2: 'Reserved for 3GPP',
0xE3: 'Reserved for 3GPP',
# 'E1'-'E3': reserved for 3GPP (for future usage) per TS 31.111 §9.1 —
# no name assigned yet, so they decode as unknown.
0xE4: 'GSMA',
}
@@ -1100,12 +1099,14 @@ PLI_QUALIFIERS = {
0x02: 'Network Measurement results',
0x03: 'Date, time and time zone',
0x04: 'Language setting',
# ETSI TS 102 223: "Reserved for GSM" — defined by TS 31.111 §8.6.
0x05: 'Timing Advance',
0x06: 'Access Technology (single)',
0x07: 'ESN of the terminal',
0x08: 'IMEISV',
0x09: 'Search Mode',
0x0A: 'Battery charge state',
0x0B: 'MEID of the terminal',
0x0C: 'Current WSID',
0x0D: 'Broadcast Network info',
0x0E: 'Multiple Access Technologies',
@@ -1410,7 +1411,8 @@ TR_RESULTS = {
0x25: 'Interaction with call control by NAA, temporary problem',
0x26: 'Launch browser generic error',
0x27: 'MMS temporary problem',
# '28'-'29' reserved for 3GPP
0x28: 'Bearer Independent Protocol temporary error',
# '29' reserved for 3GPP (for future usage)
0x30: 'Command beyond ME capabilities',
0x31: 'Command type not understood by ME',
0x32: 'Command data not understood by ME',
+1 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version."""
__version__ = '2.1.0'
__version__ = '2.1.1'
+63
View File
@@ -136,6 +136,12 @@ class TestCommandTypeTables(unittest.TestCase):
self.assertEqual(ENVELOPE_TYPES[0xD3], 'MENU SELECTION')
self.assertEqual(ENVELOPE_TYPES[0xD6], 'EVENT DOWNLOAD')
def test_unassigned_envelope_tags_unnamed(self):
# TS 31.111 §9.1: 'E1'-'E3' reserved for 3GPP (for future usage);
# no name exists, so no placeholder label may be shown.
for tag in (0xE1, 0xE2, 0xE3):
self.assertNotIn(tag, ENVELOPE_TYPES)
class TestChangeAndFidiDecoding(unittest.TestCase):
def test_change_reset_assert(self):
@@ -215,6 +221,13 @@ class TestPliQualifier(unittest.TestCase):
self.assertEqual(r['cat_command'], 'PROVIDE LOCAL INFORMATION')
self.assertEqual(r['cmd']['qualifier'], '0x03 Date, time and time zone')
def test_fetch_pli_meid(self):
# '0B' = MEID of the terminal (TS 102 223 §8.6).
r = decode_message(bytes.fromhex(
'801200000BD009810301260B820281829000'))
self.assertEqual(r['cat_command'], 'PROVIDE LOCAL INFORMATION')
self.assertEqual(r['cmd']['qualifier'], '0x0B MEID of the terminal')
def test_non_pli_no_qualifier(self):
r = decode_message(bytes.fromhex(
'801400000C810301250002028281830100910F'))
@@ -1757,6 +1770,23 @@ class TestSwWrongLength(unittest.TestCase):
from simtrace_analyser.decode import decode_sw
self.assertIsNone(decode_sw(bytes.fromhex('6205'))['name'])
def test_62_86_sensor_warning(self):
from simtrace_analyser.decode import decode_sw
self.assertEqual(decode_sw(bytes.fromhex('6286'))['name'],
'No input data available from a sensor on the card')
def test_63_81_file_filled_warning(self):
from simtrace_analyser.decode import decode_sw
self.assertEqual(decode_sw(bytes.fromhex('6381'))['name'],
'File filled up by the last write')
def test_66_class_not_fabricated(self):
# No '66xx' row exists in ISO 7816-4:2005 Table 6 or TS 102 221
# tables 10.9/10.10 — the decoder must not invent a name.
from simtrace_analyser.decode import decode_sw
self.assertIsNone(decode_sw(bytes.fromhex('6600'))['name'])
self.assertIsNone(decode_sw(bytes.fromhex('6681'))['name'])
class TestStatusFcp(unittest.TestCase):
def test_status_body_is_fcp(self):
@@ -1849,6 +1879,32 @@ class TestRefresh(unittest.TestCase):
self.assertEqual(r['cmd']['qualifier'], '0x01 File Change Notification')
self.assertEqual(r['cmd']['file_list'], ['6F07', '6F20'])
def test_steering_of_roaming(self):
# ETSI "Reserved for GSM/3G" — TS 31.111 §8.6 defines the mode.
r = decode_message(bytes.fromhex('801200000bd0098103010107820281829000'))
self.assertEqual(r['cmd']['qualifier'], '0x07 Steering of Roaming')
def test_steering_of_roaming_iwlan(self):
r = decode_message(bytes.fromhex('801200000bd0098103010108820281829000'))
self.assertEqual(r['cmd']['qualifier'],
'0x08 Steering of Roaming for I-WLAN')
class TestNoReservedLabels(unittest.TestCase):
# ETSI 'Reserved for GSM/3GPP' values must be resolved via the sibling
# spec or left unnamed — never surfaced as a 'Reserved ...' label.
def test_no_reserved_labels(self):
from simtrace_analyser import decode as d
for name in ('SW_NAMES', 'REFRESH_MODES', 'ENVELOPE_TYPES',
'TR_RESULTS', 'PLI_QUALIFIERS', 'CAT_COMMAND_TYPES',
'EVENT_TYPES'):
table = getattr(d, name)
for value in table.values():
if isinstance(value, str):
self.assertFalse(value.startswith('Reserved'),
f'{name}: {value}')
class TestIdleModeText(unittest.TestCase):
"""SET UP IDLE MODE TEXT (TS 102 223 §6.6.13) proactive command."""
@@ -2005,6 +2061,13 @@ class TestSpecRegistry(unittest.TestCase):
self.assertEqual(TR_RESULTS[0x3C], 'Frames error')
self.assertEqual(TR_RESULTS[0x3D], 'MMS error')
def test_tr_result_bip_temporary(self):
# '28' per TS 102 223 §8.12 (timer in Additional information).
from simtrace_analyser.decode import TR_RESULTS
self.assertEqual(TR_RESULTS[0x28],
'Bearer Independent Protocol temporary error')
self.assertNotIn(0x29, TR_RESULTS)
def test_envelope_types_extended(self):
from simtrace_analyser.decode import ENVELOPE_TYPES
self.assertEqual(ENVELOPE_TYPES[0xD9], 'USSD DOWNLOAD')
+1 -1
View File
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase):
def test_package_version(self):
from simtrace2_pysniff import __version__
self.assertEqual(__version__, '2.1.0')
self.assertEqual(__version__, '2.1.1')
def test_status_includes_version(self):
import os