Commit Graph
11 Commits
Author SHA1 Message Date
catarrh 8e36419143 README: add the project website next to the Telegram channel 2026-10-26 22:11:37 +03:00
catarrh 50fe1bc51b README: add the project Telegram channel link 2026-10-23 21:43:36 +03:00
catarrh 1cbafe715c Release packaging: archives, workflow, download-first docs, Windows notes
release / release (push) Waiting to run
- tools/build_release.sh: builds the Linux tar.gz and Windows zip from a
  git ref (default HEAD) using an explicit `git archive` pathspec
  allowlist: decoder (__init__.py, pd.py, gsmtap_stream.py), setup/start
  scripts, tools/vs_reader.py, READMEs, LICENSE and the README-referenced
  resources/ images.  v* tags must match pd.py VERSION.  Both archives
  are listed and extracted and checked for VERSION, py_compile, bash -n,
  .bat CRLF / .sh LF and tar exec bits; SHA256SUMS uses sha256sum or
  shasum.  Builds into a staging dir and only then replaces dist/.
- .github/workflows/release.yml: on a v* tag push run the unit tests,
  verify tag == pd.py VERSION, build, then upload into an existing
  release or create a draft and publish it; release-<tag> concurrency.
- README/RUS: Installation documents both ways (release download
  recommended; source checkout for development), SHA256SUMS verification,
  and the Windows prerequisites verified against sigrok.org (0.7.2
  release installer as the tested version, nightly caveat, VC++ 2010
  redist, bundled fx2lafw firmware and Zadig, per-port driver binding).
- start.bat: accept --opt=value alongside the original --opt value form
  (the usage text advertised the = form while the parser required a
  space); usage corrected and notes the core subset.
- .gitignore: dist/.
- AGENTS: release packaging, build verification and release process
  (commit docs/workflow first, then tag and push, auto-generated notes).

No decoder behavior change; VERSION stays 1.9.7.
2026-10-20 23:32:05 +03:00
catarrh 6e6f465841 v1.9.7: spec fixes, T=1/parity, EOF guards, dead code + rate scaling
The audit's P2 items:

- ODD_INS: add 0x73 MANAGE SECURE CHANNEL and 0x75 TRANSACT DATA
  (TS 102 221 Table 10.5); the tools/vs_reader.py mirror and the pinned
  test are updated.
- 9E/9F are SW1: ISO 7816-3 10.3.3 Table 11 makes every 9X value SW1.
  Remove the fabricated "extended-length procedure byte" that consumed
  SW2 as a length and lost the status word.
- T=1: EOF-guard every block read, bound the M-bit chain with
  T1_CHAIN_MAX, and drop the caller's duplicate chain loop -- it read the
  next block after every I-block (usually the peer's R-block) and merged
  the following I-block's INF into the packet.  R/S blocks emit nothing;
  the (possibly chained) I-block INF is the APDU payload.
- Parity: a packet containing a parity-error byte is emitted with
  GSMTAP_FLAG_BAD_FCS (the CHKSUM annotation was invisible downstream),
  with a log note when the exchange is otherwise clean.
- EOF guards: handle_atr (TA/TB/TC/TD, historical, TCK), handle_pps
  (request/response fields incl. PPS1-3) and the T=1 block parser return
  cleanly on a truncated capture instead of raising on None.
- Dead code: _edge_read was initialized True and never cleared, so the
  ATR-based T=0 procedure loop (58 lines) and the 1.5-ETU guard branch
  were unreachable.  Remove the flag, both dead paths and the branch;
  dedent the live loop (behavior unchanged).
- Samplerate scaling: _samples_for_us() replaces the hard-coded 16 MHz
  thresholds -- start-bit deglitch (0.125 us), min_high floor (0.25 us)
  and the ETU glitch floor (0.75 us).  At 16 MHz the values are
  unchanged.

Validation: 130/130 unit tests (13 new: 9E/9F SW1, parity BAD_FCS,
T=1 I/R/S blocks + M chain + truncations + one-block-per-step, PPS
truncations, truncated ATR TA, samplerate/ETU-floor vectors); the
10-trace suite is unchanged from v1.9.6 (identical APDU/CHKSUM/CONCAT/
BAD_FCS counts, RESULT: OK); compare_baseline.sh reports IDENTICAL.
VERSION -> 1.9.7.
2026-10-20 19:02:20 +03:00
catarrh 3e39032802 v1.9.6: ETU lock validation, hunt recovery, resync + ATR acceptance
The audit's P1 robustness findings:

- _etu_candidate() validates a measured burst: requires >=2 usable
  spacings, rejects a guard-dominated burst (all-zero data: 10-ETU LOW
  runs + a 2-5 ETU HIGH guard) and a lone 10-ETU spacing.  A failed
  measurement leaves bit_samples unset instead of committing the
  clock_skip*3 fallback.
- _confirm_etu() no longer counts a dropped frame (empty bits) as
  parity-OK, and a rejected measurement clears bit_samples,
  _etu_measured and _clock_skip_confident (was: kept sampling at the
  rejected rate).
- Hunt ETU recovery keys off _etu_measured: handle_atr pre-seeds
  bit_samples, so the old "bit_samples is None" condition never fired.
- resync_idle() waits on the DATA idle level (plus RST/VCC edges,
  bounded) instead of spinning on wait({'skip': 0}) at one sample.
- ATR acceptance: a DATA-state 0x3B/0x3F is a candidate only before the
  first APDU (_apdu_seen); a parsed ATR is ignored on any parity error
  or when it is a bare TS+T0.  A data run misread at a frame boundary
  could fabricate an ATR and force clock_skip=372 for the capture.
- Inverse convention per ISO 7816-3 7.1/8.1: idle HIGH and a falling
  start bit in both conventions, msb-first inverse data, raw moments
  5-7 TS-pattern detection (a real inverse TS reads as 0x03 through
  the direct reader), and a TS parity exemption (the inverse pattern
  is odd over moments 2-10).  Spec-verified; no inverse capture in the
  corpus.

Validation: 117/117 unit tests (18 new); the 10-trace suite is
unchanged from v1.9.5 (APDU/CHKSUM/CONCAT/BAD_FCS identical,
RESULT: OK everywhere); compare_baseline.sh reports IDENTICAL.
VERSION -> 1.9.6.
2026-10-20 18:02:14 +03:00
catarrh b574f900a1 v1.9.5: T=0 framing — immediate SW, short responses, Le=256
- Decode the first post-header byte as SW1 before the ACK/data decision:
  a case-2 rejection (6A82/6CXX) carries no ACK and used to be read as
  command data, silently consuming the exchange and the next one.
- Short responses (Lr < Le): after the P3-byte read, split the buffer at
  the first plausible SW1 followed by a plausible CLA, replay the tail and
  flag BAD_FCS; previously the SW scan discarded the real SW, merged the
  following exchanges and lost the exchange at EOF.
- P3=0 is Le=256 for an outgoing command: keep the response data (read
  until SW) instead of discarding it, including data bytes equal to INS.
- ~INS ACK: one data byte per ACK via the new helpers; case-4 inline data
  is appended (permissive; ISO 7816-3 §12.2 maps case 4 to GET RESPONSE).
- tests: four TestDecodeStep vectors (immediate SW, short read + split +
  replay, Le=256 data incl. an INS-valued byte); 99/99.
- AGENTS v1.9.5 + T=0 section, README/README_RUS bullets; baseline
  regenerated (xiaomi_mi_a1_coldboot 473 -> 475 APDUs, all else identical);
  compare_baseline: IDENTICAL.
2026-10-20 17:07:02 +03:00
catarrh 54dfc6586d v1.9.3 review fixes: CLA doc ranges, README_RUS, mirror/ODD_INS pins
- pd.py/AGENTS: the rejected '8X' range now reads 0x81-0x83/0x85-0x8F
  (0x84 stays accepted); the 0x40-0x4F vs 0x50-0x5F citations are split
  (chaining is ISO-only, TS 102 221 keeps b5=0); the bit-dense CLA list
  gains 0xE7 (exact set verified).
- README_RUS.md: the invalid-INS note mirrored (the removed odd-LSB rule,
  citation corrected to 5.1.2).
- tests: test_vs_reader_mirror (pd.py <-> tools/vs_reader.py drift guard),
  test_odd_ins_set_pinned, the 0xE7 noise vector, and the 'EX' vector now
  uses STATUS (e2 f2) as TS 102 221 Table 10.5 defines.
- tests/baseline.txt regenerated (test listing only); compare_baseline:
  IDENTICAL.
2026-10-18 00:00:08 +03:00
catarrh ad3ff6b940 docs: point at SIMtrace Analyser (repo renamed from simtrace2-pysniff) 2026-09-19 12:41:10 +03:00
catarrh 250c09c9b7 v1.9.0: CLK rate in RST events; v1.9.1: T=0 NULL is not an SW1
v1.9.0 -- CLK frequency in RST events:
- RST events (sub_type 0x10) append the measured CLK frequency when known:
  [direction, level, flags, clk_hz_be32], flags bit 0 = rate present; the
  3-byte form is kept when the rate is not yet measured.  Value is
  samplerate / _samples_per_clock from the existing 16-edge measurement (PPS
  changes F/D, not the CLK rate).  VCC events unchanged.  Backward
  compatible (old consumers read the first two bytes only); consumers can
  compute data_rate = clk_hz x D / F from the ATR/PPS Fi/Di.
- Unit tests for the payload codec; README/README_RUS/AGENTS updated.

v1.9.1 -- T=0 NULL is not an SW1:
- The 'scan for SW1' loops accepted any 0x6X byte, including the NULL
  procedure byte 0x60 (card busy), so 'NULL + real SW1' was mis-framed as
  the status word: the phone traces' SELECT MF came out as ...3f00 6061 —
  real SW1 0x61 eaten as SW2, real SW2 0x29 left on the wire and discarded
  as a suspicious CLA (desync cascade).
- plausible_sw() now accepts 0x61-0x6F and 0x90-0x9F only, per
  ISO 7816-3:2006 10.3.3 Table 11 ('6X' or '9X', EXCEPT '60'; ISO 7816-4
  enforces '60' as an invalid SW1) and UICC_SPECS.md 1.8.  All seven
  SW1-detection sites use plausible_sw(); NULLs met while scanning are
  logged as rate-limited wait extensions (_note_pre_sw) instead of
  suspicious bytes.
- tools/vs_reader.py::_plausible_sw() mirrors the fix; regression vector
  for the 00a40004023f006061 artifact.

Validation: 74/74 unit tests; baseline regenerated and IDENTICAL (the SW
fix is count-neutral -- only the SW bytes change); all 9 example pcaps
regenerated -- vs_reader OK, zero APDUs ending in 60 61, SELECT MF now
61 29 / 61 25.
2026-09-19 00:55:33 +03:00
catarrh e5a2b7c83b v1.8.0: PPS as standard request/response packets; fix res-byte flag docs
- Split the PPS exchange into the two standard GSMTAP-SIM subtypes:
  0x02 (PPS_REQ, raw request bytes, span ss..es_req) and 0x03 (PPS_RSP,
  raw response bytes, span es_req..end), replacing the single 0x02 packet
  with concatenated request+response that deviated from libosmocore's
  gsmtap.h.  Wireshark now dissects both halves; simtrace2-pysniff needs
  no change (its importer already maps 0x03 to 'pps' and _decode_pps()
  parses the 0xFF-leading response frame).  PPS annotation/log unchanged.
- gsmtap_stream.py: GSMTAP_SIM_PPS_REQ/RSP constants; header field labels
  aligned with libosmocore's struct (signal_dbm/snr_db).
- Docs: GSMTAP event tables (EN/RU) updated, PPS deviation note removed;
  corrected the res-byte flag attribution -- upstream simtrace2 never
  sets res, so the GSMTAP_FLAG_BAD_FCS carriage is this decoder's own
  extension (semantics mirror simtrace2's USB data flags).
- Tests: assert the PPS subtype values; baseline regenerated (only the
  version line and test listing differed -- no decode-metric changes).
  68/68 unit tests.
2026-09-17 23:01:40 +03:00
catarrh c222eecb3c Add README_RUS.md: Russian translation of the README
Full translation following the otaman README_RUS.md convention (prose and
code-block comments translated; commands, flags and paths unchanged).
Links back to the English README at the top.
2026-09-11 20:39:31 +03:00