Show T=15 as PPS capability, not a transport protocol
T=15 in the ATR is a Protocol-and-Parameter-Selection capability, not a data protocol like T=0/T=1. Previously 'Protocols: T=0, T=15' was misleading. - _decode_atr: exclude T=15 from protocols, set pps=True instead - TCK presence: only-t0 rule now also requires no PPS (T=15 implies TCK) - PWA ATR detail: 'Protocol: T=0' + 'PPS: supported' rows - +1 test with the real UICC ATR (3B 9F 95 80 1F C7 ...) - Bump 1.22.2, SW cache v33->v34
This commit is contained in:
1 parent
6376fb1591
commit
6bdbbea209
7 files changed
+29
-7
No files matched your search
+5
-2
@@ -1450,7 +1450,10 @@ function atrPpsRows(decoded) {
|
|||||||
rows.push(['Convention', decoded.convention || 'unknown']);
|
rows.push(['Convention', decoded.convention || 'unknown']);
|
||||||
if (decoded.ts) rows.push(['TS', '0x' + decoded.ts]);
|
if (decoded.ts) rows.push(['TS', '0x' + decoded.ts]);
|
||||||
if (decoded.t0) rows.push(['T0', '0x' + decoded.t0 + ' — ' + decoded.historical_len + ' historical bytes']);
|
if (decoded.t0) rows.push(['T0', '0x' + decoded.t0 + ' — ' + decoded.historical_len + ' historical bytes']);
|
||||||
if (decoded.protocols) rows.push(['Protocols', decoded.protocols.join(', ')]);
|
if (decoded.protocols && decoded.protocols.length) {
|
||||||
|
rows.push([decoded.protocols.length > 1 ? 'Protocols' : 'Protocol', decoded.protocols.join(', ')]);
|
||||||
|
}
|
||||||
|
if (decoded.pps) rows.push(['PPS', 'supported']);
|
||||||
if (decoded.interface) {
|
if (decoded.interface) {
|
||||||
for (const ifc of decoded.interface) {
|
for (const ifc of decoded.interface) {
|
||||||
rows.push([ifc.name, ifaceValue(ifc)]);
|
rows.push([ifc.name, ifaceValue(ifc)]);
|
||||||
@@ -2511,7 +2514,7 @@ function onFilterChange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────── Versioning ────────────────────
|
// ──────────────────── Versioning ────────────────────
|
||||||
const PWA_VERSION = '1.22.1';
|
const PWA_VERSION = '1.22.2';
|
||||||
const MIN_SERVER_VERSION = '1.0.0';
|
const MIN_SERVER_VERSION = '1.0.0';
|
||||||
|
|
||||||
function parseVersion(v) {
|
function parseVersion(v) {
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simtrace-analyser-v33';
|
const CACHE = 'simtrace-analyser-v34';
|
||||||
|
|
||||||
const PRECACHE = [
|
const PRECACHE = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "simtrace2-pysniff"
|
name = "simtrace2-pysniff"
|
||||||
version = "1.22.1"
|
version = "1.22.2"
|
||||||
description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)"
|
description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)"
|
||||||
readme = {file = "README.md", content-type = "text/markdown"}
|
readme = {file = "README.md", content-type = "text/markdown"}
|
||||||
license = {text = "GPL-2.0-or-later"}
|
license = {text = "GPL-2.0-or-later"}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from .gsmtap import GsmtapSender, GsmtapReceiver
|
|||||||
from .pcap import PcapWriter
|
from .pcap import PcapWriter
|
||||||
from .dump import FileDumper, format_message
|
from .dump import FileDumper, format_message
|
||||||
|
|
||||||
__version__ = '1.22.1'
|
__version__ = '1.22.2'
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SniffSession',
|
'SniffSession',
|
||||||
'DeviceDisconnected',
|
'DeviceDisconnected',
|
||||||
|
|||||||
@@ -4177,6 +4177,7 @@ def _decode_atr(data):
|
|||||||
|
|
||||||
interface = []
|
interface = []
|
||||||
protocols = []
|
protocols = []
|
||||||
|
pps = False
|
||||||
flags = t0 & 0xF0
|
flags = t0 & 0xF0
|
||||||
i = 1
|
i = 1
|
||||||
level = 1
|
level = 1
|
||||||
@@ -4229,6 +4230,9 @@ def _decode_atr(data):
|
|||||||
b = body[i]
|
b = body[i]
|
||||||
i += 1
|
i += 1
|
||||||
cur_t = b & 0x0F
|
cur_t = b & 0x0F
|
||||||
|
if cur_t == 15:
|
||||||
|
pps = True
|
||||||
|
else:
|
||||||
protocols.append(_T_PROTOCOLS.get(cur_t, f'T={cur_t}'))
|
protocols.append(_T_PROTOCOLS.get(cur_t, f'T={cur_t}'))
|
||||||
flags = b & 0xF0
|
flags = b & 0xF0
|
||||||
level += 1
|
level += 1
|
||||||
@@ -4239,6 +4243,8 @@ def _decode_atr(data):
|
|||||||
result['interface'] = interface
|
result['interface'] = interface
|
||||||
if protocols:
|
if protocols:
|
||||||
result['protocols'] = protocols
|
result['protocols'] = protocols
|
||||||
|
if pps:
|
||||||
|
result['pps'] = True
|
||||||
|
|
||||||
k = result['historical_len']
|
k = result['historical_len']
|
||||||
if k and i + k <= len(body):
|
if k and i + k <= len(body):
|
||||||
@@ -4246,7 +4252,7 @@ def _decode_atr(data):
|
|||||||
i += k
|
i += k
|
||||||
|
|
||||||
# TCK present unless only T=0 is proposed (ISO 7816-3 §8.2.5).
|
# TCK present unless only T=0 is proposed (ISO 7816-3 §8.2.5).
|
||||||
only_t0 = not protocols or protocols == ['T=0']
|
only_t0 = (not protocols or protocols == ['T=0']) and not pps
|
||||||
if not only_t0 and i < len(body):
|
if not only_t0 and i < len(body):
|
||||||
tck = body[i]
|
tck = body[i]
|
||||||
check = 0
|
check = 0
|
||||||
|
|||||||
@@ -898,6 +898,19 @@ class TestAtrPps(unittest.TestCase):
|
|||||||
self.assertEqual(r['historical']['category'], 'status information (life cycle)')
|
self.assertEqual(r['historical']['category'], 'status information (life cycle)')
|
||||||
self.assertEqual(r['historical']['life_cycle'], 'operational state (activated)')
|
self.assertEqual(r['historical']['life_cycle'], 'operational state (activated)')
|
||||||
|
|
||||||
|
def test_atr_t15_is_pps_capability(self):
|
||||||
|
from simtrace2_pysniff.server.decode import _decode_atr
|
||||||
|
# Real UICC ATR: T0=9F (TA1+TD1, 15 hist bytes), TA1=95, TD1=80 (T=0),
|
||||||
|
# TD2=1F (T=15 → PPS capability), TA3=C7 (clock stop + classes A/B/C).
|
||||||
|
r = _decode_atr(bytes.fromhex('3B9F95801FC78031E073FE21136364035B83079000F7'))
|
||||||
|
self.assertEqual(r['protocols'], ['T=0'])
|
||||||
|
self.assertTrue(r['pps'])
|
||||||
|
ta3 = r['interface'][1]
|
||||||
|
self.assertEqual(ta3['name'], 'TA3')
|
||||||
|
self.assertEqual(ta3['clock_stop'], 'no preference')
|
||||||
|
self.assertEqual(ta3['classes'], ['A', 'B', 'C'])
|
||||||
|
self.assertTrue(r['tck_valid'])
|
||||||
|
|
||||||
def test_pps(self):
|
def test_pps(self):
|
||||||
from simtrace2_pysniff.server.decode import _decode_pps
|
from simtrace2_pysniff.server.decode import _decode_pps
|
||||||
# FF 10 11 xx: PPS0=10 (T=0, PPS1 present), PPS1=11 (Fi=1,Di=1), PCK=XOR(FF,10,11)=FE
|
# FF 10 11 xx: PPS0=10 (T=0, PPS1 present), PPS1=11 (Fi=1,Di=1), PCK=XOR(FF,10,11)=FE
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
|
|||||||
class TestVersion(unittest.TestCase):
|
class TestVersion(unittest.TestCase):
|
||||||
def test_package_version(self):
|
def test_package_version(self):
|
||||||
from simtrace2_pysniff import __version__
|
from simtrace2_pysniff import __version__
|
||||||
self.assertEqual(__version__, '1.22.1')
|
self.assertEqual(__version__, '1.22.2')
|
||||||
|
|
||||||
def test_status_includes_version(self):
|
def test_status_includes_version(self):
|
||||||
import os
|
import os
|
||||||
|
|||||||
Reference in new issue
Block a user