v1.5.0: invalid-INS one-byte re-frame + explicit discard logging
Residual CONCAT mis-frames (4gmodem/A55_sim2/S21p_sim2) traced to a silent 5-byte drop at an invalid-INS T=0 header (0x0f 00 a4 08 04). Drop only the stray leading byte and re-frame the remaining four via _replay so the real command (00 a4 08 04 02 2f 00 -> 61 1c) is recovered. - _note_discard()/_consume_first(): every consumed-but-unemitted byte is now logged; idle 0x00/0xFF discards are rate-limited, real data always gets an ANN_WARN annotation. - All read-until-SW loops log skipped non-SW bytes. - All traces: CONCAT 0; BAD_FCS/GARBAGE 0; test_8/test_7 RESULT OK; 47/47 unit tests; baseline byte-identical. History rewritten to a single root commit so no example trace is stored in the repo (fixtures are local-only, gitignored). README keeps only generic <trace>.sr placeholders; AGENTS.md retains the fixture names.
This commit is contained in:
commit
d3715c9385
34 files changed
+7855
No files matched your search
@@ -0,0 +1,403 @@
|
||||
=== Baseline captured 2026-09-09T14:15:00+03:00 ===
|
||||
|
||||
=== test_8 ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('etu recovered (samples):', 1237)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 37
|
||||
capture ATRs : 3
|
||||
capture APDUs : 56
|
||||
byte-exact matches (LCS) : 28
|
||||
reader exchanges not in capture (pre-capture / dropped): 9
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 17
|
||||
- valid exchanges (reader retries/dupes): 11
|
||||
- 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)
|
||||
|
||||
=== test_7 ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('etu recovered (samples):', 1236)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 37
|
||||
capture ATRs : 8
|
||||
capture APDUs : 65
|
||||
byte-exact matches (LCS) : 37
|
||||
reader exchanges not in capture (pre-capture / dropped): 0
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 17
|
||||
- valid exchanges (reader retries/dupes): 11
|
||||
- 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)
|
||||
|
||||
=== xiaomi_phone_sample ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('etu recovered (samples):', 53)
|
||||
(11673977, '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
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 640
|
||||
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): 20
|
||||
- valid exchanges (reader retries/dupes): 620
|
||||
- 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)
|
||||
|
||||
=== xiaomi_mi_a1_coldboot ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('RST low at start: expecting cold-boot ATR',)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 473
|
||||
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): 10
|
||||
- valid exchanges (reader retries/dupes): 463
|
||||
- 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)
|
||||
|
||||
=== 4gmodem_coldboot ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('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
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 327
|
||||
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): 9
|
||||
- valid exchanges (reader retries/dupes): 318
|
||||
- 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.5.0 ===',)
|
||||
('RST low at start: expecting cold-boot ATR',)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 407
|
||||
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): 24
|
||||
- valid exchanges (reader retries/dupes): 383
|
||||
- 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_sim2 ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('RST low at start: expecting cold-boot ATR',)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f96801fc78031e073fe2117574a3382033339001e')
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 395
|
||||
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): 24
|
||||
- valid exchanges (reader retries/dupes): 371
|
||||
- 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_S21p_coldboot_new_cable ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('RST low at start: expecting cold-boot ATR',)
|
||||
iso7816-1: RST deasserted
|
||||
('RST deasserted',)
|
||||
('RST deasserted (hunt/ATR already in progress)',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
CHKSUM ERROR count: 0
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 399
|
||||
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): 24
|
||||
- valid exchanges (reader retries/dupes): 375
|
||||
- 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_S21p_coldboot_new_cable_sim2 ===
|
||||
('=== iso7816 decoder v1.5.0 ===',)
|
||||
('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
|
||||
CONCAT APDU count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 375
|
||||
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): 21
|
||||
- valid exchanges (reader retries/dupes): 354
|
||||
- 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)
|
||||
|
||||
=== signal diagnostics ===
|
||||
--- test_8 ---
|
||||
First activity: sample 1
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 71858170
|
||||
Transitions: 6
|
||||
Transitions: 168
|
||||
Transitions: 7423
|
||||
--- test_7 ---
|
||||
First activity: sample 2
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 71428947
|
||||
Transitions: 16
|
||||
Transitions: 444
|
||||
Transitions: 8998
|
||||
--- xiaomi_phone_sample ---
|
||||
First activity: sample 11497325
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 12833950
|
||||
Transitions: 0
|
||||
Transitions: 78488
|
||||
--- samsung_A55_coldboot_new_cable ---
|
||||
First activity: sample 46045778
|
||||
Frequency: 4.00 MHz
|
||||
Period: 4.0 samples
|
||||
Rising edges: 29683152
|
||||
Transitions: 1
|
||||
Transitions: 1
|
||||
Transitions: 46003
|
||||
--- samsung_S21p_coldboot_new_cable ---
|
||||
First activity: sample 44024869
|
||||
Frequency: 4.00 MHz
|
||||
Period: 4.0 samples
|
||||
Rising edges: 30172749
|
||||
Transitions: 1
|
||||
Transitions: 3
|
||||
Transitions: 47161
|
||||
=== unit tests ===
|
||||
test_glitch_low_pulse_removed (__main__.TestDeglitchEdges.test_glitch_low_pulse_removed) ... ok
|
||||
test_no_edges (__main__.TestDeglitchEdges.test_no_edges) ... ok
|
||||
test_real_pulses_preserved (__main__.TestDeglitchEdges.test_real_pulses_preserved) ... ok
|
||||
test_short_pulse_start_edge_preserved (__main__.TestDeglitchEdges.test_short_pulse_start_edge_preserved) ... ok
|
||||
test_is_desynced_all_zero_is_not_desync (__main__.TestDesyncHelpers.test_is_desynced_all_zero_is_not_desync) ... ok
|
||||
test_is_desynced_bad_sw (__main__.TestDesyncHelpers.test_is_desynced_bad_sw) ... ok
|
||||
test_is_desynced_clean_packets (__main__.TestDesyncHelpers.test_is_desynced_clean_packets) ... ok
|
||||
test_is_desynced_oversized (__main__.TestDesyncHelpers.test_is_desynced_oversized) ... ok
|
||||
test_is_desynced_short_fragment (__main__.TestDesyncHelpers.test_is_desynced_short_fragment) ... ok
|
||||
test_max_tpdu_len (__main__.TestDesyncHelpers.test_max_tpdu_len) ... ok
|
||||
test_plausible_sw_accepts_valid_classes (__main__.TestDesyncHelpers.test_plausible_sw_accepts_valid_classes) ... ok
|
||||
test_plausible_sw_rejects_garbage (__main__.TestDesyncHelpers.test_plausible_sw_rejects_garbage) ... ok
|
||||
test_a55_u1_concat (__main__.TestEmbeddedExchanges.test_a55_u1_concat) ... ok
|
||||
test_a55_u4_mega_concat (__main__.TestEmbeddedExchanges.test_a55_u4_mega_concat) ... ok
|
||||
test_channel1_select_after_manage_channel_swallow (__main__.TestEmbeddedExchanges.test_channel1_select_after_manage_channel_swallow) ... ok
|
||||
test_clean_atr_fragment_select (__main__.TestEmbeddedExchanges.test_clean_atr_fragment_select) ... ok
|
||||
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_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
|
||||
test_samsung_record_burst_concat (__main__.TestEmbeddedExchanges.test_samsung_record_burst_concat) ... ok
|
||||
test_xiaomi_select_getresp_swallow (__main__.TestEmbeddedExchanges.test_xiaomi_select_getresp_swallow) ... ok
|
||||
test_build_packet_bad_fcs_flag (__main__.TestGsmtapFlags.test_build_packet_bad_fcs_flag) ... ok
|
||||
test_build_packet_payload_passthrough (__main__.TestGsmtapFlags.test_build_packet_payload_passthrough) ... ok
|
||||
test_build_packet_plain_res_zero (__main__.TestGsmtapFlags.test_build_packet_plain_res_zero) ... ok
|
||||
test_flag_constant_value (__main__.TestGsmtapFlags.test_flag_constant_value) ... ok
|
||||
test_accepts_full_hold_fall (__main__.TestNextStartFall.test_accepts_full_hold_fall) ... ok
|
||||
test_finds_fall_after_threshold (__main__.TestNextStartFall.test_finds_fall_after_threshold) ... ok
|
||||
test_finds_fall_exactly_at_threshold (__main__.TestNextStartFall.test_finds_fall_exactly_at_threshold)
|
||||
Regression test for Samsung 16 CLK/ETU concatenation. ... ok
|
||||
test_rejects_phantom_sub_etu_fall (__main__.TestNextStartFall.test_rejects_phantom_sub_etu_fall) ... ok
|
||||
test_returns_none_when_no_fall (__main__.TestNextStartFall.test_returns_none_when_no_fall) ... ok
|
||||
test_invalid_cla (__main__.TestValidateT0Apdu.test_invalid_cla) ... ok
|
||||
test_invalid_cla_proprietary_unseen (__main__.TestValidateT0Apdu.test_invalid_cla_proprietary_unseen) ... ok
|
||||
test_invalid_ins_lsb_odd (__main__.TestValidateT0Apdu.test_invalid_ins_lsb_odd) ... ok
|
||||
test_invalid_ins_sw_collision (__main__.TestValidateT0Apdu.test_invalid_ins_sw_collision) ... ok
|
||||
test_invalid_ins_zero (__main__.TestValidateT0Apdu.test_invalid_ins_zero) ... ok
|
||||
test_invalid_oversized (__main__.TestValidateT0Apdu.test_invalid_oversized) ... ok
|
||||
test_invalid_sw1 (__main__.TestValidateT0Apdu.test_invalid_sw1) ... ok
|
||||
test_invalid_too_short (__main__.TestValidateT0Apdu.test_invalid_too_short) ... ok
|
||||
test_valid_case1_direct_sw (__main__.TestValidateT0Apdu.test_valid_case1_direct_sw) ... ok
|
||||
test_valid_case2_with_response (__main__.TestValidateT0Apdu.test_valid_case2_with_response) ... ok
|
||||
test_valid_case3_with_command_data (__main__.TestValidateT0Apdu.test_valid_case3_with_command_data) ... ok
|
||||
test_valid_cla_channel1_select_after_manage_channel (__main__.TestValidateT0Apdu.test_valid_cla_channel1_select_after_manage_channel) ... ok
|
||||
test_valid_cla_interindustry_classes (__main__.TestValidateT0Apdu.test_valid_cla_interindustry_classes) ... ok
|
||||
test_valid_direct_sw_with_p3_nonzero (__main__.TestValidateT0Apdu.test_valid_direct_sw_with_p3_nonzero) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 47 tests in 0.017s
|
||||
|
||||
OK
|
||||
@@ -0,0 +1,476 @@
|
||||
=== Baseline captured 2026-09-04T22:55:20+03:00 ===
|
||||
|
||||
=== test_8 ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('Invalid TS byte 0x90 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x90 - skipped
|
||||
('Invalid TS byte 0x00 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x00 - skipped
|
||||
('Invalid TS byte 0xf7 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xf7 - skipped
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
('Invalid TS byte 0x10 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x10 - skipped
|
||||
('Invalid TS byte 0x95 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x95 - skipped
|
||||
('Invalid TS byte 0x7a (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x7a - skipped
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
('Invalid TS byte 0x10 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x10 - skipped
|
||||
('warm reset: re-arming ATR hunt',)
|
||||
CHKSUM ERROR count: 28
|
||||
reader exchanges : 37
|
||||
capture ATRs : 2
|
||||
capture APDUs : 54
|
||||
byte-exact matches (LCS) : 28
|
||||
reader exchanges not in capture (pre-capture / dropped): 9
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 17
|
||||
- valid exchanges (reader retries/dupes): 9
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== test_8 mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('etu recovered (samples):', 1237)
|
||||
('warm reset: re-arming ATR hunt',)
|
||||
('Invalid TS byte 0x95 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x95 - skipped
|
||||
('Invalid TS byte 0x7a (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x7a - skipped
|
||||
(382251, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
|
||||
('Invalid TS byte 0xfc (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xfc - skipped
|
||||
(7362370, '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]
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
(14335815, '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]
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
('Invalid TS byte 0xfb (pre-activation?), resyncing',)
|
||||
CHKSUM ERROR count: 38
|
||||
reader exchanges : 37
|
||||
capture ATRs : 2
|
||||
capture APDUs : 54
|
||||
byte-exact matches (LCS) : 28
|
||||
reader exchanges not in capture (pre-capture / dropped): 9
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 17
|
||||
- valid exchanges (reader retries/dupes): 9
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== test_7 ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('warm reset: re-arming ATR hunt',)
|
||||
('Invalid TS byte 0x07 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x07 - skipped
|
||||
('Invalid TS byte 0x95 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x95 - skipped
|
||||
('Invalid TS byte 0x7a (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x7a - skipped
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
('Invalid TS byte 0x10 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x10 - skipped
|
||||
('Invalid TS byte 0x95 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x95 - skipped
|
||||
('Invalid TS byte 0x7a (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x7a - skipped
|
||||
(23123034, '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]
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
CHKSUM ERROR count: 166
|
||||
reader exchanges : 37
|
||||
capture ATRs : 4
|
||||
capture APDUs : 10
|
||||
byte-exact matches (LCS) : 10
|
||||
reader exchanges not in capture (pre-capture / dropped): 27
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 0
|
||||
- valid exchanges (reader retries/dupes): 0
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== test_7 mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('warm reset: re-arming ATR hunt',)
|
||||
('etu recovered (samples):', 1236)
|
||||
('warm reset: re-arming ATR hunt',)
|
||||
('ENDATR', b'3b9f95801fc78031e073fe21136364035b83079000f7')
|
||||
(45481390, '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]
|
||||
(45636295, '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]
|
||||
('Invalid TS byte 0x9f (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x9f - skipped
|
||||
('Invalid TS byte 0x95 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x95 - skipped
|
||||
('Invalid TS byte 0x80 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x80 - skipped
|
||||
('Invalid TS byte 0x1f (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x1f - skipped
|
||||
('Invalid TS byte 0xc7 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xc7 - skipped
|
||||
('Invalid TS byte 0x80 (pre-activation?), resyncing',)
|
||||
CHKSUM ERROR count: 162
|
||||
reader exchanges : 37
|
||||
capture ATRs : 4
|
||||
capture APDUs : 10
|
||||
byte-exact matches (LCS) : 10
|
||||
reader exchanges not in capture (pre-capture / dropped): 27
|
||||
capture APDUs not in reader log:
|
||||
- proactive/CAT (unlogged by reader SDK): 0
|
||||
- valid exchanges (reader retries/dupes): 0
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== xiaomi_phone ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('Invalid TS byte 0xf4 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xf4 - skipped
|
||||
(11685168, '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]
|
||||
('Invalid TS byte 0xff (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xff - skipped
|
||||
(11703683, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 0, 1, 0, 0, 0, 0, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 0, 1, 0, 0, 0, 0, 0]
|
||||
('Invalid TS byte 0x0b (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x0b - skipped
|
||||
('Invalid TS byte 0xe8 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xe8 - skipped
|
||||
('Invalid TS byte 0xbc (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xbc - skipped
|
||||
(11739309, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 0, 1, 1, 1, 0, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 0, 1, 1, 1, 0, 1, 0]
|
||||
('Invalid TS byte 0xb9 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xb9 - skipped
|
||||
('Invalid TS byte 0x5c (pre-activation?), resyncing',)
|
||||
CHKSUM ERROR count: 142
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 0
|
||||
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): 0
|
||||
- valid exchanges (reader retries/dupes): 0
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== xiaomi_phone mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('etu recovered (samples):', 53)
|
||||
(11673977, '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: 10140
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 620
|
||||
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): 18
|
||||
- valid exchanges (reader retries/dupes): 602
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== samsung_phone_sample ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
(3125821, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
|
||||
('Invalid TS byte 0xfc (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xfc - skipped
|
||||
('Invalid TS byte 0x03 (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x03 - skipped
|
||||
(21909783, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 1, 1, 1, 0, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
|
||||
('Invalid TS byte 0xbc (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0xbc - skipped
|
||||
(21952849, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 1, 1, 1, 0, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 1, 1, 1, 0, 0]
|
||||
('Invalid TS byte 0x7f (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x7f - skipped
|
||||
(21969301, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 0, 0, 0, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 0, 0, 0, 1, 0]
|
||||
('Invalid TS byte 0x8f (pre-activation?), resyncing',)
|
||||
iso7816-1: Invalid TS byte 0x8f - skipped
|
||||
('Invalid TS byte 0xfe (pre-activation?), resyncing',)
|
||||
CHKSUM ERROR count: 2062
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 2
|
||||
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): 0
|
||||
- valid exchanges (reader retries/dupes): 2
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== samsung_phone_sample mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('etu recovered (samples):', 72)
|
||||
(3272987, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 1, 1, 1, 1, 1, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 1, 1, 1, 1, 1, 1, 0]
|
||||
(3273839, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 1, 0, 1, 1, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 1, 0, 1, 1, 1, 0]
|
||||
('etu recovered (samples):', 71)
|
||||
(21919147, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 1, 1, 1, 1]
|
||||
(22026940, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 1, 1, 1, 1]
|
||||
(22831386, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 1, 0, 0, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 1, 0, 0, 1, 1]
|
||||
(22850665, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 1, 1, 0, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 1, 1, 0, 1, 0]
|
||||
(22853174, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 1, 1, 0, 1, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 1, 1, 0, 1, 1, 0]
|
||||
(22855060, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 1, 1, 1, 1]
|
||||
(22873632, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 1, 1, 1, 1, 1, 1, 0])
|
||||
CHKSUM ERROR count: 1514
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 49
|
||||
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): 0
|
||||
- valid exchanges (reader retries/dupes): 44
|
||||
- GARBAGE (mis-framed/desync): 5
|
||||
GARBAGE: 00a41f04026f38ff61
|
||||
GARBAGE: 00a40004026f07ff61
|
||||
GARBAGE: 00a40004026f78ff61
|
||||
GARBAGE: 00a4000402ff6fdfff
|
||||
GARBAGE: 00a40004026fe3ff61
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 6
|
||||
in GARBAGE: 5, in retries: 1, in CAT: 0, in matched: 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: UNCLEAN - 5 mis-framed capture APDU(s)
|
||||
|
||||
=== sunrise_phone_sample ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('ENDATR', b'3b9f96801fc68031e073fe211b644127910082900037')
|
||||
CHKSUM ERROR count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 1077
|
||||
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): 5
|
||||
- valid exchanges (reader retries/dupes): 1072
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== sunrise_phone_sample mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('etu recovered (samples):', 5723)
|
||||
(88911990, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 0, 0, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 0, 0, 1, 1]
|
||||
(92269756, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(93979892, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(94115907, '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]
|
||||
(95206798, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(95329967, '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]
|
||||
(95981570, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 1, 1, 0, 1, 1, 1, 0])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 1, 1, 0, 1, 1, 1, 0]
|
||||
(99228947, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(99366855, '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]
|
||||
CHKSUM ERROR count: 1834
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 1
|
||||
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): 0
|
||||
- valid exchanges (reader retries/dupes): 1
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== sim_turnon ATR-included ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('ENDATR', b'3b9f96801fc78031e073fe211163444d2183079000e2')
|
||||
CHKSUM ERROR count: 0
|
||||
reader exchanges : 0
|
||||
capture ATRs : 1
|
||||
capture APDUs : 1390
|
||||
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): 5
|
||||
- valid exchanges (reader retries/dupes): 1385
|
||||
- GARBAGE (mis-framed/desync): 0
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: OK - capture fully explained (reader + CAT + valid retries)
|
||||
|
||||
=== sim_turnon mid-session ===
|
||||
('iso7816 decoder v1.1.3',)
|
||||
('etu recovered (samples):', 2855)
|
||||
(108117240, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 0, 0, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 0, 0, 1, 1]
|
||||
(109877662, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(113088360, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 1, 1, 1, 1]
|
||||
(113308472, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
(113339895, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 1, 0, 1, 1, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 1, 0, 1, 1, 1, 1, 1, 1]
|
||||
(113602225, '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]
|
||||
(113954522, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 0, 0, 0, 0, 0, 1, 0, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 0, 0, 0, 0, 0, 1, 0, 1, 1]
|
||||
(113983375, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 1, 1, 0, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 1, 1, 0, 0, 1, 1, 1, 1]
|
||||
(114163147, 'CHKSUM ERROR: ', 0, 'bits: ', [0, 1, 0, 1, 1, 0, 1, 1, 1, 1])
|
||||
iso7816-1: CHKSUM ERROR bits=[0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
|
||||
CHKSUM ERROR count: 5044
|
||||
reader exchanges : 0
|
||||
capture ATRs : 0
|
||||
capture APDUs : 9
|
||||
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): 0
|
||||
- valid exchanges (reader retries/dupes): 7
|
||||
- GARBAGE (mis-framed/desync): 2
|
||||
GARBAGE: 00feedc2feedeefeedeafeec00feecffe797edeafeedeafeec00feecffe797edeafeecc0fefc80e7e7e7e7e7e7e767edfafeec00feecffe797edfafeecc2f9feece6edfafeec00feecffe797edfafeecc2f9feec41fbedfafeec08feecffe797edeafeecc2f9feec02feedfafeecc2f9feec83feedfefeecc2f9feecf2edf2feecc0feec3efeedfafeecc0feec4cfbedfefeecc2f9fefc4080808080808080808088808480808080feec41feedfefeecc0feecc0ecfeec02e7e6feedeefeedeafeecc0f9feece7edfafeedeafeedeafeec82f9feedfafeec08feec18e7e7fdedfefeec8af9feedfafeec0af9feedfafeec00feec18e7e7fdedfafeec02f9feedfafeec0af9
|
||||
GARBAGE: 000667f9e7e7e7e7e7e7e7e7e7e767ec100083e7e7e7e7e7e7e7e7e7e7e7e797ec1820e7e7e7e7e7e7e7e7e7e7e7e727ec1800eae7e7e7e7e7e7e7e7e7e7e7e7e7cfedf6feec08feedeafeec0af9feec03ffe727edf6feec00feec52feedeafeec0af9feecffe7e7e7fbedf6feec80feedeafeec02f9feecffe7e7e7fbedf6feec88e6edfefeec00feedfafeec80e4edfafeec88e4edfafeec00feedfafeec08feedfafeec80feedfefeec80feedfefeec08feedfefeec00feedfafeec80e4ece4ece4edfafeec02e7e6feece23dccffec9d95dcf2edf6feec80e4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4edfafeecc2f9feedc2feedfefeedf6feec80e4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4edf2feecc0feedc6feedfafeecc0feedc27dedfeedf6feec88e4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4ece4
|
||||
|
||||
Degradation metrics:
|
||||
- BAD_FCS (desynced by decoder): 5
|
||||
in GARBAGE: 2, in retries: 3, in CAT: 0, in matched: 0
|
||||
- payload mismatches (LCS-matched, bytes differ): 0
|
||||
- suspicious CLA: 0
|
||||
|
||||
RESULT: UNCLEAN - 2 mis-framed capture APDU(s)
|
||||
|
||||
=== signal diagnostics ===
|
||||
--- test_8 ---
|
||||
First activity: sample 1
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 71858170
|
||||
Transitions: 6
|
||||
Transitions: 168
|
||||
Transitions: 7423
|
||||
--- test_7 ---
|
||||
First activity: sample 2
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 71428947
|
||||
Transitions: 16
|
||||
Transitions: 444
|
||||
Transitions: 8998
|
||||
--- xiaomi_phone ---
|
||||
First activity: sample 11497325
|
||||
Frequency: 5.33 MHz
|
||||
Period: 3.0 samples
|
||||
Rising edges: 12833950
|
||||
Transitions: 0
|
||||
Transitions: 78488
|
||||
--- samsung_phone_sample ---
|
||||
First activity: sample 3096536
|
||||
Frequency: 4.00 MHz
|
||||
Period: 4.0 samples
|
||||
Rising edges: 14002767
|
||||
Transitions: 38
|
||||
Transitions: 171694
|
||||
Transitions: 117098
|
||||
--- sunrise_phone_sample ---
|
||||
@@ -0,0 +1,363 @@
|
||||
"""Unit tests for GSMTAP flag plumbing + decoder desync helpers.
|
||||
|
||||
pd.py imports the libsigrokdecode bindings, which are not always present
|
||||
in a bare Python env, so we stub a minimal `sigrokdecode` module and load
|
||||
the real pd.py / gsmtap_stream.py sources under the `iso7816` package
|
||||
name (mirroring how libsigrokdecode loads a decoder directory).
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
|
||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def _load_iso7816():
|
||||
# Minimal sigrokdecode stub.
|
||||
srd = types.ModuleType('sigrokdecode')
|
||||
srd.Decoder = type('Decoder', (object,), {})
|
||||
srd.SRD_CONF_SAMPLERATE = 'samplerate'
|
||||
srd.OUTPUT_PYTHON = 'python'
|
||||
srd.OUTPUT_ANN = 'ann'
|
||||
srd.OUTPUT_BINARY = 'binary'
|
||||
sys.modules.setdefault('sigrokdecode', srd)
|
||||
|
||||
pkg = types.ModuleType('iso7816')
|
||||
pkg.__path__ = [REPO]
|
||||
sys.modules.setdefault('iso7816', pkg)
|
||||
|
||||
def load(name, relpath):
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
'iso7816.' + name, os.path.join(REPO, relpath))
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules['iso7816.' + name] = mod
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
return load('gsmtap_stream', 'gsmtap_stream.py'), \
|
||||
load('pd', 'pd.py')
|
||||
|
||||
|
||||
class TestGsmtapFlags(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.gs, cls.pd = _load_iso7816()
|
||||
|
||||
def test_build_packet_plain_res_zero(self):
|
||||
p = self.gs.build_packet(0x00, b'\x80\xf2')
|
||||
self.assertEqual(p[12], 0x00)
|
||||
self.assertEqual(p[15], 0x00)
|
||||
|
||||
def test_build_packet_bad_fcs_flag(self):
|
||||
p = self.gs.build_packet(0x00, b'\x80\xf2',
|
||||
flags=self.gs.GSMTAP_FLAG_BAD_FCS)
|
||||
self.assertEqual(p[12], 0x00) # sub_type preserved
|
||||
self.assertEqual(p[15], 0x01) # res = BAD_FCS
|
||||
|
||||
def test_build_packet_payload_passthrough(self):
|
||||
data = b'\x00\xa4\x00\x04\x02'
|
||||
p = self.gs.build_packet(0x00, data)
|
||||
self.assertEqual(p[16:], data)
|
||||
|
||||
def test_flag_constant_value(self):
|
||||
self.assertEqual(self.gs.GSMTAP_FLAG_BAD_FCS, 0x01)
|
||||
|
||||
|
||||
class TestDesyncHelpers(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.gs, cls.pd = _load_iso7816()
|
||||
|
||||
def test_plausible_sw_accepts_valid_classes(self):
|
||||
for sw1 in list(range(0x60, 0x70)) + list(range(0x90, 0xA0)):
|
||||
self.assertTrue(self.pd.plausible_sw(sw1), hex(sw1))
|
||||
|
||||
def test_plausible_sw_rejects_garbage(self):
|
||||
for sw1 in (0x00, 0x01, 0x3B, 0x3F, 0xA0, 0xFF, 0x88, 0x5F):
|
||||
self.assertFalse(self.pd.plausible_sw(sw1), hex(sw1))
|
||||
|
||||
def test_max_tpdu_len(self):
|
||||
self.assertEqual(self.pd.MAX_TPDU_LEN, 271)
|
||||
|
||||
def test_is_desynced_clean_packets(self):
|
||||
# 7-byte: header(5) + proc(1) + SW2(1) ending in a 9x SW1
|
||||
self.assertFalse(self.pd.is_desynced(b'\x00\xa4\x00\x04\x02\x60\x90'))
|
||||
# ACK-style GET RESPONSE (header + proc + payload + SW 9000)
|
||||
self.assertFalse(self.pd.is_desynced(
|
||||
b'\x00\xc0\x00\x00\x25\xc0\x62\x23\x90\x00'))
|
||||
|
||||
def test_is_desynced_short_fragment(self):
|
||||
# 5-byte header-only (INVALID-procedure residue) -- no SW read
|
||||
self.assertTrue(self.pd.is_desynced(b'\xa4\x3f\x00\x61\x25'))
|
||||
|
||||
def test_is_desynced_bad_sw(self):
|
||||
self.assertTrue(self.pd.is_desynced(
|
||||
b'\x00\xa4\x00\x04\x02\x3f\x00\x00\xff'))
|
||||
|
||||
def test_is_desynced_oversized(self):
|
||||
big = bytes([0x00, 0xb2, 0x00, 0x00, 0xff]) + b'\x00' * 300
|
||||
self.assertTrue(self.pd.is_desynced(big))
|
||||
|
||||
def test_is_desynced_all_zero_is_not_desync(self):
|
||||
# all-zero is idle-low (suppressed), not desync
|
||||
self.assertFalse(self.pd.is_desynced(b'\x00' * 7))
|
||||
|
||||
|
||||
class TestValidateT0Apdu(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.gs, cls.pd = _load_iso7816()
|
||||
|
||||
def _valid(self, packet):
|
||||
valid, reason = self.pd.validate_t0_apdu(packet)
|
||||
self.assertTrue(valid, 'expected valid, got: ' + str(reason))
|
||||
|
||||
def _invalid(self, packet, expected_reason_substring=None):
|
||||
valid, reason = self.pd.validate_t0_apdu(packet)
|
||||
self.assertFalse(valid, 'expected invalid')
|
||||
if expected_reason_substring:
|
||||
self.assertIn(expected_reason_substring, reason)
|
||||
|
||||
def test_valid_case1_direct_sw(self):
|
||||
# P3=0, 7 bytes, direct SW
|
||||
self._valid(b'\x00\xa4\x00\x04\x00\x90\x00')
|
||||
|
||||
def test_valid_direct_sw_with_p3_nonzero(self):
|
||||
# Card can reject a command and send SW immediately, even with P3>0
|
||||
self._valid(b'\x00\xa4\x00\x04\x02\x90\x00')
|
||||
|
||||
def test_valid_case2_with_response(self):
|
||||
# header(5) + 2 response bytes + SW(2)
|
||||
self._valid(b'\x00\xb0\x00\x00\x02\x12\x34\x90\x00')
|
||||
|
||||
def test_valid_case3_with_command_data(self):
|
||||
# header(5) + 4 command bytes + SW(2)
|
||||
self._valid(b'\x00\xd6\x00\x00\x04\x11\x22\x33\x44\x90\x00')
|
||||
|
||||
def test_valid_cla_interindustry_classes(self):
|
||||
# Interindustry CLA digits 0x/1x/4x/5x are structurally valid
|
||||
# (ISO 7816-4): includes logical channels 0x01-0x03, secure
|
||||
# messaging 0x0C, chaining 0x10, and the SM/structural 0x4x/0x5x.
|
||||
for cla in (0x01, 0x02, 0x03, 0x0C, 0x10, 0x14, 0x44, 0x50, 0x54):
|
||||
pkt = bytes([cla, 0xa4, 0x00, 0x04, 0x00, 0x90, 0x00])
|
||||
self._valid(pkt), 'CLA=0x%02x' % cla
|
||||
|
||||
def test_valid_cla_channel1_select_after_manage_channel(self):
|
||||
# The command that follows MANAGE CHANNEL (00 70 00 00 01) opening
|
||||
# logical channel 1 legitimately uses CLA 0x01. This exact type
|
||||
# (SELECT by AID) is what the A55/S21p traces carry.
|
||||
self._valid(b'\x01\xa4\x00\x04\x0c'
|
||||
b'\xa0\x00\x00\x00\x63\x50\x4b\x43\x53\x2d\x31\x35'
|
||||
b'\x6a\x82')
|
||||
|
||||
def test_invalid_cla_proprietary_unseen(self):
|
||||
# 0xC0 is proprietary but not a class these cards emit; stays invalid
|
||||
self._invalid(b'\xc0\xa4\x00\x04\x00\x90\x00', 'invalid CLA')
|
||||
|
||||
def test_invalid_too_short(self):
|
||||
self._invalid(b'\x00\xa4\x00\x04\x00\x90', 'too short')
|
||||
|
||||
def test_invalid_cla(self):
|
||||
self._invalid(b'\xff\xa4\x00\x04\x00\x90\x00', 'invalid CLA')
|
||||
|
||||
def test_invalid_ins_zero(self):
|
||||
self._invalid(b'\x00\x00\x00\x04\x00\x90\x00', 'invalid INS')
|
||||
|
||||
def test_invalid_ins_lsb_odd(self):
|
||||
self._invalid(b'\x00\xa5\x00\x04\x00\x90\x00', 'invalid INS')
|
||||
|
||||
def test_invalid_ins_sw_collision(self):
|
||||
self._invalid(b'\x00\x64\x00\x04\x00\x90\x00', 'invalid INS')
|
||||
|
||||
def test_invalid_sw1(self):
|
||||
self._invalid(b'\x00\xa4\x00\x04\x00\xff\x00', 'invalid SW1')
|
||||
|
||||
def test_invalid_oversized(self):
|
||||
big = bytes([0x00, 0xb2, 0x00, 0x00, 0xff]) + b'\x00' * 300
|
||||
self._invalid(big, 'exceeds T=0 max length')
|
||||
|
||||
|
||||
class TestNextStartFall(unittest.TestCase):
|
||||
def setUp(self):
|
||||
_, pd = _load_iso7816()
|
||||
# _next_start_fall is an instance method but does not use self.
|
||||
self.inst = object.__new__(pd.Decoder)
|
||||
|
||||
def test_finds_fall_after_threshold(self):
|
||||
edges = [(100, 0), (200, 1), (300, 0)]
|
||||
self.assertEqual(self.inst._next_start_fall(edges, 250, 100), 300)
|
||||
|
||||
def test_finds_fall_exactly_at_threshold(self):
|
||||
"""Regression test for Samsung 16 CLK/ETU concatenation.
|
||||
|
||||
When the next byte's start-bit fall lands exactly on the
|
||||
``10 * bs`` boundary, the previous strict ``>`` test missed it.
|
||||
"""
|
||||
edges = [(100, 0), (200, 1), (300, 0)]
|
||||
self.assertEqual(self.inst._next_start_fall(edges, 300, 100), 300)
|
||||
|
||||
def test_returns_none_when_no_fall(self):
|
||||
edges = [(100, 0), (200, 1)]
|
||||
self.assertIsNone(self.inst._next_start_fall(edges, 300, 100))
|
||||
|
||||
def test_rejects_phantom_sub_etu_fall(self):
|
||||
# A sub-ETU low bounce in the stop/guard time (fall at 290, quick
|
||||
# rise at 310) must NOT be accepted as the next start; the scan
|
||||
# skips it and only accepts the genuine start fall at 400.
|
||||
edges = [(100, 0), (290, 0), (310, 1), (400, 0)]
|
||||
self.assertEqual(self.inst._next_start_fall(edges, 250, 100), 400)
|
||||
|
||||
def test_accepts_full_hold_fall(self):
|
||||
# Genuine start fall: held LOW for a full ETU before the rise.
|
||||
edges = [(100, 0), (300, 0), (400, 1)]
|
||||
self.assertEqual(self.inst._next_start_fall(edges, 250, 100), 300)
|
||||
|
||||
|
||||
class TestDeglitchEdges(unittest.TestCase):
|
||||
def setUp(self):
|
||||
_, pd = _load_iso7816()
|
||||
self.inst = object.__new__(pd.Decoder)
|
||||
|
||||
def test_no_edges(self):
|
||||
self.assertEqual(self.inst._deglitch_edges([], 72), [])
|
||||
|
||||
def test_glitch_low_pulse_removed(self):
|
||||
# Real start fall at 0, real rise at 72, 1-sample LOW glitch
|
||||
# at 73-74, real fall at 144 (next byte start). bs=72, threshold=14.
|
||||
edges = [(0, 0), (72, 1), (73, 0), (74, 1), (144, 0)]
|
||||
# After removing the 1-sample LOW pulse (73,0)-(74,1),
|
||||
# the line stays HIGH until the next real edge.
|
||||
result = self.inst._deglitch_edges(edges, 72)
|
||||
self.assertEqual(result, [(0, 0), (72, 1), (144, 0)])
|
||||
|
||||
def test_real_pulses_preserved(self):
|
||||
# 72-sample real LOW pulse; should not be touched.
|
||||
edges = [(0, 0), (72, 1), (144, 0), (216, 1)]
|
||||
result = self.inst._deglitch_edges(edges, 72)
|
||||
self.assertEqual(result, edges)
|
||||
|
||||
def test_short_pulse_start_edge_preserved(self):
|
||||
# Pulse starting at the first edge: start-bit fall is preserved,
|
||||
# only the edge ending the short pulse is removed.
|
||||
edges = [(0, 0), (1, 1), (100, 0)]
|
||||
result = self.inst._deglitch_edges(edges, 72)
|
||||
self.assertEqual(result, [(0, 0), (100, 0)])
|
||||
|
||||
|
||||
class TestEmbeddedExchanges(unittest.TestCase):
|
||||
"""CONCAT detection: structurally valid packets that embed complete T=0
|
||||
exchanges (a byte-drop mis-frame swallowed several real exchanges into
|
||||
one APDU). Vectors are exact bytes from the 10-trace suite."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.gs, cls.pd = _load_iso7816()
|
||||
|
||||
def _hits(self, hexstr):
|
||||
return self.pd.embedded_exchanges(bytes.fromhex(hexstr))
|
||||
|
||||
def test_clean_get_response_fcp(self):
|
||||
# FCP response with 0x62/0x6f BER-TLV tags + p1=f0 PIN template;
|
||||
# classic false-positive candidate -- must NOT hit.
|
||||
h = ('00c000001ec0621c8202412183026f43a503920100'
|
||||
'8a01058b032f0604800200028800913e')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(self._hits(h), [])
|
||||
|
||||
def test_clean_select_611e(self):
|
||||
# Plain SELECT -> 611E (test_8/7 session) must not hit.
|
||||
self.assertEqual(self._hits('00a4080404a47fff611e'), [])
|
||||
|
||||
def test_clean_select_6a82(self):
|
||||
self.assertEqual(self._hits('00a4080404a47fff6a82'), [])
|
||||
|
||||
def test_clean_read_record_ff_run(self):
|
||||
# READ RECORD whose response is an all-FF fill run + 9000. The FF
|
||||
# data must not be mistaken for embedded headers.
|
||||
h = '00b2020440' + 'ff' * 64 + '9000'
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(self._hits(h), [])
|
||||
|
||||
def test_clean_channel1_select_after_manage_channel(self):
|
||||
# A properly decoded post-MANAGE-CHANNEL exchange (CLA 0x01,
|
||||
# SELECT by AID, 6A 82) is a CLEAN single exchange: it must
|
||||
# validate and must NOT be flagged as CONCAT.
|
||||
h = ('01a400040ca000000063504b43532d31356a82')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(self._hits(h), [])
|
||||
self.assertFalse(self.pd.is_desynced(bytes.fromhex(h)))
|
||||
|
||||
def test_clean_atr_fragment_select(self):
|
||||
# ATR-recovery fragment (header + 3F 00 + 6125) must not hit.
|
||||
self.assertEqual(self._hits('00a40004023f006125'), [])
|
||||
|
||||
def test_a55_u1_concat(self):
|
||||
# A55[296] (n=70): phantom-00 (buffer {01}) CLA + slip prefix.
|
||||
h = ('000402a43f00612501c0000025c062238202782183023f00a5098001'
|
||||
'7183040000eec18a01058b032f0601c6069001008301019000'
|
||||
'01a4000402a42f00612101c0000021621f')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(self._hits(h), [(8, 51), (53, 61)])
|
||||
|
||||
def test_a55_u4_mega_concat(self):
|
||||
# A55[281] (n=167): the CAT ENVELOPE mega-concat (7 embedded).
|
||||
h = ('04040ca4a000000063504b43532d31356a8280f2000c009000007080'
|
||||
'0100900000a4000c02a43f009000801400000c148103012500020282'
|
||||
'81830100910f801200000f12d00d8103010500820281829902031290'
|
||||
'0000a4000c02a43f009000801400000c148103010500020282818301'
|
||||
'00910f801200000f12d00d8103010300820281828402011e90008014'
|
||||
'000010148103010300020282818301000402011e90000070009000')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
hits = self._hits(h)
|
||||
self.assertEqual(len(hits), 7)
|
||||
self.assertEqual(hits[0], (32, 40))
|
||||
|
||||
def test_samsung_record_burst_concat(self):
|
||||
# samsung_phone[826] (n=247): mis-framed READ RECORD chain.
|
||||
h = ('0408b252f0990000fffe01900000b2020408b252f0430000fffe02900'
|
||||
'000b2030408b252f0230000fffe03900000b2040408b252f0450000f'
|
||||
'ffe02900000b2050408b252f0490000fffe04900000b2060408b252f'
|
||||
'0690000fffe05900000b2070408b252f0790000fffe06900000b2080'
|
||||
'408b252f0890000fffe07900000b2090408b2ffffffffffffffff900'
|
||||
'000b20a0408b2ffffffffffffffff900000a4000402a46f40611b00c'
|
||||
'000001bc0621982054221001c0283026f408a01058b036f060980020'
|
||||
'0388800900000b201041cb2fffffffffbfbfbfbfbfbfbfbfbfbfbfbf'
|
||||
'bfbfbfbfbfbfbfbfb830300b202041cb2ffff9000')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(len(self._hits(h)), 15)
|
||||
|
||||
def test_xiaomi_select_getresp_swallow(self):
|
||||
# xiaomi[613] (n=118): stacked SELECT/GET RESPONSE exchanges.
|
||||
h = ('04a47fff6f116a8200a4080404a47fff6fe3611e00c000001ec0621c'
|
||||
'8202412183026fe3a503c001808a01058b036f0609800200128801f0'
|
||||
'900000a4080404a47fff6f136a8200a4080404a47fff6fe3611e00c0'
|
||||
'00001ec0621c8202412183026fe3a503c001808a01058b036f060980'
|
||||
'020012889000')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(len(self._hits(h)), 4)
|
||||
|
||||
def test_channel1_select_after_manage_channel_swallow(self):
|
||||
# Channels: MC opens channel 1 (00 70 00 00 01 -> 01 90 00: the 01
|
||||
# response byte is the opened channel number), then a channel-1
|
||||
# SELECT-by-AID (01 A4 00 04 0C AID -> 6A 82) that a mis-frame
|
||||
# swallowed into the MC packet. The embedded exchange starts on
|
||||
# CLA 0x01 -- a real channel-1 cluster byte, not a phantom: the
|
||||
# detector must find it.
|
||||
h = ('007000000101900001a400040ca000000063504b43532d31356a829000')
|
||||
self.assertTrue(self.pd.validate_t0_apdu(bytes.fromhex(h))[0])
|
||||
self.assertEqual(self._hits(h), [(8, 25)])
|
||||
|
||||
def test_concat_packets_are_not_is_desynced(self):
|
||||
# CONCAT detection is orthogonal: these packets PASS structural
|
||||
# validation (they only look wrong when interior bytes are scanned).
|
||||
a55u1 = ('000402a43f00612501c0000025c062238202782183023f00a5098'
|
||||
'0017183040000eec18a01058b032f0601c6069001008301019000'
|
||||
'01a4000402a42f00612101c0000021621f')
|
||||
self.assertFalse(self.pd.is_desynced(bytes.fromhex(a55u1)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user