v1.9.2: PPS CLK upshift + exact CLK-edge projection (new reader fixture)

- handle_pps(): flag the first post-PPS character; _read_byte_clk() re-measures
  the CLK period there and accounts the measurement's 16 edges.  The Alcor
  reader upclocks 4.0 -> 4.8 MHz at PPS (FI/DI still 512/32), so the ATR
  measurement was stale: 64 vs 53.33 samples/ETU, no post-PPS APDU framed.
- _wait_clk_rising(): project to the n-th edge (skip to target - half a
  period, take one edge) instead of "skip n-2 cycles + 2 edges", which lost
  ~1 edge per call on fractional periods and slid the samples ~0.5 ETU early
  by the parity bit.  xiaomi 7698 -> 2, 4gmodem 7218 -> 0, S21p_sim2 394 -> 0
  CHKSUM lines (previously misread as dirty-wire noise).
- CONCAT gate: EMBEDDED_CLA excludes the structural 0x4x/0x5x classes (payload
  false hit on the Alcor secured packet); mirrored in tools/vs_reader.py and
  covered by a new vector in tests/test_gsmtap.py.
- tools/capture_baseline.sh: add the alcor_reader_sample fixture; baseline
  regenerated (APDU counts and RESULT: OK unchanged everywhere, 0 CONCAT).
- 75/75 unit tests.  README/AGENTS updated.
This commit is contained in:
catarrh committed 2026-09-27 23:52:40 +03:00
1 parent 18ee720fda
commit 3bf5cee222
7 files changed
+184 -104

No files matched your search

+49 -59
View File
@@ -1,7 +1,7 @@
=== Baseline captured 2026-09-19T00:47:38+03:00 ===
=== Baseline captured 2026-09-27T23:45:58+03:00 ===
=== test_8 ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('etu recovered (samples):', 1237)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -36,7 +36,7 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== test_7 ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('etu recovered (samples):', 1236)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -77,27 +77,11 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== xiaomi_phone_sample ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('etu recovered (samples):', 53)
(11673977, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 0, 0, 1, 1, 1, 1])
(11674007, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 0, 0, 1, 1, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 0, 0, 1, 1, 1, 1]
(11695589, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
(11697509, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
(11698149, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
(11698789, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 0, 0, 0, 0, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 0, 0, 0, 0, 1, 1]
(11699429, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
(11701349, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
(11701989, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 1, 0, 0, 1, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
(11709030, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
CHKSUM ERROR count: 7698
CHKSUM ERROR count: 2
CONCAT APDU count: 0
reader exchanges : 0
capture ATRs : 0
@@ -118,7 +102,7 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== xiaomi_mi_a1_coldboot ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -145,27 +129,13 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== 4gmodem_coldboot ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
('RST deasserted (hunt/ATR already in progress)',)
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
(10083201, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
(10084089, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
(32207790, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 0, 0, 1, 1, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 0, 0, 1, 1, 0, 0]
(32214835, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 1, 0, 1, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
(32216580, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 0, 0, 0, 0, 1, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
(32219038, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
(32219857, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
CHKSUM ERROR count: 7218
CHKSUM ERROR count: 0
CONCAT APDU count: 0
reader exchanges : 0
capture ATRs : 1
@@ -185,8 +155,41 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== alcor_reader_sample ===
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
('RST deasserted (hunt/ATR already in progress)',)
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
(26904874, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 1, 1, 1, 1, 1])
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
iso7816-1: RST deasserted
('RST deasserted',)
('RST deasserted (hunt/ATR already in progress)',)
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
CHKSUM ERROR count: 2
CONCAT APDU count: 0
reader exchanges : 0
capture ATRs : 2
capture APDUs : 48
byte-exact matches (LCS) : 0
reader exchanges not in capture (pre-capture / dropped): 0
capture APDUs not in reader log:
- proactive/CAT (unlogged by reader SDK): 36
- valid exchanges (reader retries/dupes): 12
- GARBAGE (mis-framed/desync): 0
Degradation metrics:
- BAD_FCS (desynced by decoder): 0
- CONCAT (multi-exchange swallowed in one packet): 0
- payload mismatches (LCS-matched, bytes differ): 0
- suspicious CLA: 0
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== samsung_A55_coldboot_new_cable ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -213,7 +216,7 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== samsung_A55_coldboot_new_cable_sim2 ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -240,7 +243,7 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== samsung_S21p_coldboot_new_cable ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
@@ -267,27 +270,13 @@ Degradation metrics:
RESULT: OK - capture fully explained (reader + CAT + valid retries)
=== samsung_S21p_coldboot_new_cable_sim2 ===
('=== iso7816 decoder v1.9.1 ===',)
('=== iso7816 decoder v1.9.2 ===',)
('RST low at start: expecting cold-boot ATR',)
iso7816-1: RST deasserted
('RST deasserted',)
('RST deasserted (hunt/ATR already in progress)',)
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
(72333388, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 1, 0, 1, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
(72394723, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
(72424969, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
(72429512, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 0, 1, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 0, 1, 0, 0, 0]
(72487445, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
(72631364, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 0, 1, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
(98955516, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
CHKSUM ERROR count: 394
CHKSUM ERROR count: 0
CONCAT APDU count: 0
reader exchanges : 0
capture ATRs : 1
@@ -381,6 +370,7 @@ test_clean_atr_fragment_select (__main__.TestEmbeddedExchanges.test_clean_atr_fr
test_clean_channel1_select_after_manage_channel (__main__.TestEmbeddedExchanges.test_clean_channel1_select_after_manage_channel) ... ok
test_clean_get_response_fcp (__main__.TestEmbeddedExchanges.test_clean_get_response_fcp) ... ok
test_clean_read_record_ff_run (__main__.TestEmbeddedExchanges.test_clean_read_record_ff_run) ... ok
test_clean_secured_packet_payload_alcor (__main__.TestEmbeddedExchanges.test_clean_secured_packet_payload_alcor) ... ok
test_clean_select_611e (__main__.TestEmbeddedExchanges.test_clean_select_611e) ... ok
test_clean_select_6a82 (__main__.TestEmbeddedExchanges.test_clean_select_6a82) ... ok
test_concat_packets_are_not_is_desynced (__main__.TestEmbeddedExchanges.test_concat_packets_are_not_is_desynced) ... ok
@@ -425,6 +415,6 @@ test_valid_cla_interindustry_classes (__main__.TestValidateT0Apdu.test_valid_cla
test_valid_direct_sw_with_p3_nonzero (__main__.TestValidateT0Apdu.test_valid_direct_sw_with_p3_nonzero) ... ok
----------------------------------------------------------------------
Ran 74 tests in 0.027s
Ran 75 tests in 0.024s
OK