diff --git a/frontend/index.html b/frontend/index.html index 00f787c..daf1eef 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -723,7 +723,14 @@ function fileRows(f) { for (const s of f.services) rows.push(['Service ' + s.n, s.name || '']); } if (f.applications) { - for (const a of f.applications) rows.push(['App', (a.label ? a.label + ' · ' : '') + (a.aid || '')]); + for (const a of f.applications) { + rows.push(['App', (a.label ? a.label + ' · ' : '') + (a.aid || '')]); + if (a.eap) { + const types = (a.eap.eap_types || []).join(', '); + const dfs = (a.eap.dfs || []).map(d => d.toUpperCase()).join(', '); + rows.push(['EAP', `${a.eap.label || 'EAP'} (${types}) in ${dfs}`]); + } + } } if (f.direction) rows.push(['SMS', f.direction + ' — ' + f.status]); if (f.message_ids) rows.push(['Message IDs', f.message_ids.join(', ')]); diff --git a/simtrace2_pysniff/server/decode.py b/simtrace2_pysniff/server/decode.py index 7bbb134..b0d2123 100644 --- a/simtrace2_pysniff/server/decode.py +++ b/simtrace2_pysniff/server/decode.py @@ -2505,7 +2505,8 @@ def _decode_plmn_act(u16): def _decode_dir(raw, p1=None): - """EF_DIR record: application template(s) — AID (4F) + label (50).""" + """EF_DIR record: application template(s) — AID (4F) + label (50) + + optional EAP discretionary template (73, TS 102 310).""" apps = [] for tag, _length, value in parse_tlv(raw): if tag != 0x61: @@ -2518,11 +2519,40 @@ def _decode_dir(raw, p1=None): label = _decode_annex_a(v2) if label: app['label'] = label + elif t2 == 0x73: + eap = _decode_eap_template(v2) + if eap: + app['eap'] = eap if app: apps.append(app) return {'applications': apps} if apps else {'raw': raw.hex().upper()} +# IANA EAP method types seen in TS 102 310 EF_DIR entries. +_EAP_TYPES = { + 18: 'EAP-SIM', + 23: 'EAP-AKA', + 50: 'EAP-AKA\'', +} + + +def _decode_eap_template(value): + """TS 102 310 §5.2: 73 → A0 { 80 EAP types, 81 DF FIDs, 82 label }.""" + for t2, _l2, v2 in parse_tlv(value): + if t2 != 0xA0: + continue + eap = {} + for t3, _l3, v3 in parse_tlv(v2): + if t3 == 0x80: + eap['eap_types'] = [_EAP_TYPES.get(b, b) for b in v3] + elif t3 == 0x81: + eap['dfs'] = [v3[i:i + 2].hex() for i in range(0, len(v3), 2)] + elif t3 == 0x82: + eap['label'] = v3.decode('ascii', 'replace') + return eap + return {} + + def _decode_arr(raw, p1=None): entries = [] for tag, _length, value in parse_tlv(raw): @@ -2829,7 +2859,16 @@ def _file_summary(f): if f.get('services'): return f"{len(f['services'])} services allocated" if f.get('applications'): - return '; '.join((a.get('label') or a.get('aid') or '') for a in f['applications']) + parts = [] + for a in f['applications']: + s = a.get('label') or a.get('aid') or '' + eap = a.get('eap') + if eap: + types = ', '.join(str(t) for t in eap.get('eap_types', [])) + dfs = ', '.join(KNOWN_FIDS.get(d, d) for d in eap.get('dfs', [])) + s += f" [{eap.get('label', 'EAP')}: {types} in {dfs}]" + parts.append(s) + return '; '.join(parts) if f.get('tpdu'): t = f['tpdu'] s = t.get('mti', '') diff --git a/tests/test_decode.py b/tests/test_decode.py index 23a17cb..7c630f1 100644 --- a/tests/test_decode.py +++ b/tests/test_decode.py @@ -1019,6 +1019,10 @@ class TestFileDecoders(unittest.TestCase): '730ea00c80011781025f408203454150ffffffffffffffffffffffffffffffffffffff')) self.assertEqual(f['applications'][0]['label'], 'MegaFon') self.assertEqual(f['applications'][0]['aid'], 'A0000000871002FFFFF00189000001FF') + eap = f['applications'][0]['eap'] + self.assertEqual(eap['eap_types'], ['EAP-AKA']) + self.assertEqual(eap['dfs'], ['5f40']) + self.assertEqual(eap['label'], 'EAP') def test_nai_empty_record(self): from simtrace2_pysniff.server.decode import _decode_file_data