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