tpms

package
v0.613.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package tpms decodes TPMS (Tire Pressure Monitoring System) Sub-GHz bit-streams into the format-independent fields a pentester can trust: the Manchester line-decoded payload bytes, the 32-bit sensor ID, and CRC-8 validity. TPMS sensors beacon on 315 MHz (North America) and 433.92 MHz (Europe/Asia) and are a prime Flipper Sub-GHz target — they uniquely identify (and so let you track) a specific vehicle, and the wire format has no authentication.

Wrap-vs-native judgement

Native. The hard, reusable part — recovering bytes from a Manchester-coded FSK/OOK bit-stream — is a public, deterministic transform (IEEE 802.3 and G.E. Thomas conventions). Operators bring a pre-demodulated bit-stream (rtl_433, a Flipper FSK Sub-GHz capture pre-extracted to bits, or Universal Radio Hacker) and decode offline, exactly as subghz_pocsag_decode does for paging.

What this package covers

  • Manchester line decoding under both conventions (IEEE 802.3: data 0 = "10", data 1 = "01"; G.E. Thomas: the inverse) at both bit alignments, auto-selecting the convention/alignment that yields the longest clean (no illegal "00"/"11" transition) decode — the correct one runs clean while the wrong one trips an illegal pair almost immediately.
  • Leading-preamble tolerance: the alternating "0101…" preamble that most TPMS sensors send before the payload decodes to a run of data bits and is reported, not rejected.
  • The decoded payload as hex, plus the 32-bit sensor ID — the first four payload bytes, which across virtually every TPMS family is the unique per-sensor identifier (the field-independent signal an operator uses to fingerprint or track a vehicle).
  • CRC-8 validity probing against the polynomials TPMS sensors commonly use (0x07 CCITT, 0x2F, 0x13), reported as a confidence/alignment hint.

What this package does NOT cover (deliberately out of scope)

  • Manufacturer-specific pressure / temperature / status field interpretation: the byte offsets and scaling differ per family (Schrader, Toyota, Ford, Renault, Citroën, GM, Hyundai, …) and cannot be verified here without per-model captures. The raw decoded bytes are surfaced so an operator can apply the relevant rtl_433 model layout. Encoding this without sample data would risk a confidently-wrong reading — worse than none for a security tool.
  • FSK/OOK demodulation (bring a pre-demodulated bit-stream).
  • Differential-Manchester / biphase-mark variants used by a minority of sensors (standard Manchester is the common case here).

Index

Constants

View Source
const DefaultExpectedSensors = 4

DefaultExpectedSensors is the TPMS sensor count of a typical passenger vehicle (one direct sensor per wheel).

Variables

This section is empty.

Functions

func Synth added in v0.377.0

func Synth(in SynthInput) (string, error)

Synth builds the Manchester bit-stream for a TPMS frame — the inverse of Decode. It lays out [sensor ID:4][payload…][CRC-8], computes the CRC over everything before it, and Manchester-line-codes the result. Round-trip: Decode recovers the same 32-bit sensor ID, the same payload bytes as DecodedHex, and lists the chosen polynomial in CRC8Matches.

Wrap-vs-native judgement

Native, and the inverse of the existing Decode. TPMS framing is a public, deterministic transform (the same rtl_433-derived Manchester + CRC-8 families Decode covers); generation is pure bit + CRC maths, no crypto, no state, no hardware. It produces the bits behind a TPMS-spoof payload — it does NOT transmit (pair with a Sub-GHz TX stage), so it carries the same Low risk as Decode. Correctness is verifiable two ways: round-trip against Decode, and hand-computed CRC over a known payload.

Per-model pressure/temperature field interpretation is deliberately NOT imposed — the caller supplies the data bytes verbatim, mirroring Decode's refusal to guess scaling it cannot verify (a confidently-wrong reading is worse than none).

Types

type Analysis added in v0.367.0

type Analysis struct {
	FramesAnalyzed    int            `json:"frames_analyzed"`
	FramesDecoded     int            `json:"frames_decoded"`
	ExpectedSensors   int            `json:"expected_sensors"`
	UniqueSensorIDs   []string       `json:"unique_sensor_ids"`
	SensorFrameCounts map[string]int `json:"sensor_frame_counts"`
	CRCValidFrames    int            `json:"crc_valid_frames"`
	CRCInvalidFrames  int            `json:"crc_invalid_frames"`
	Frames            []FrameSummary `json:"frames"`
	Anomalies         []Anomaly      `json:"anomalies"`
	Notes             []string       `json:"notes,omitempty"`
}

Analysis is the structured result of AnalyzeFrames.

func AnalyzeFrames added in v0.367.0

func AnalyzeFrames(bitStreams []string, expectedSensors int) (*Analysis, error)

AnalyzeFrames decodes a sequence of TPMS bit-streams (each the same '0'/'1' input Decode accepts) and reports defensive anomaly signals over the set. expectedSensors is the vehicle's direct-TPMS wheel count (<=0 uses DefaultExpectedSensors).

Two deterministic signals are surfaced, both with their benign explanation stated so the operator correlates rather than concludes:

  • excess_sensors: more unique 32-bit sensor IDs than a single vehicle carries. Consistent with sensor-ID injection/spoofing (the TPMS attack class) OR simply more than one vehicle in radio range.
  • crc_invalid: frames matching no covered CRC-8 polynomial. Consistent with a crafted/injected frame OR a TPMS family whose CRC poly is not among the covered set (0x07/0x2F/0x13). Not conclusive on its own.

Pressure/temperature anomaly detection is intentionally absent: the field layouts are per-family and unverifiable here (see decode.go), so flagging on them would risk the confidently-wrong reading this package refuses to produce.

type Anomaly added in v0.367.0

type Anomaly struct {
	Kind     string `json:"kind"`     // "excess_sensors" | "crc_invalid"
	Severity string `json:"severity"` // "info" | "warning"
	Detail   string `json:"detail"`
}

Anomaly is one observation flagged during sequence analysis. It is an OBSERVATION with interpretation, never a definitive attack verdict — every flagged condition has a benign explanation that the detail spells out, in keeping with the package's "a confidently-wrong reading is worse than none" stance.

type FrameSummary added in v0.367.0

type FrameSummary struct {
	Index       int    `json:"index"`
	SensorID    string `json:"sensor_id,omitempty"`
	CRCValid    bool   `json:"crc_valid"`
	DecodedHex  string `json:"decoded_hex,omitempty"`
	DecodeError string `json:"decode_error,omitempty"`
}

FrameSummary is the per-frame view used by Analysis.

type Result

type Result struct {
	InputBits       int      `json:"input_bits"`
	LineCoding      string   `json:"line_coding"`
	BitAlignment    int      `json:"bit_alignment"`
	DecodedBits     int      `json:"decoded_bits"`
	DecodedBytes    int      `json:"decoded_bytes"`
	DecodedHex      string   `json:"decoded_hex"`
	SensorID        string   `json:"sensor_id,omitempty"`
	SensorIDDecimal *uint32  `json:"sensor_id_decimal,omitempty"`
	CRC8Matches     []string `json:"crc8_matches,omitempty"`
	Notes           []string `json:"notes,omitempty"`
}

Result is the structured decode of a TPMS bit-stream.

func Decode

func Decode(bitStr string) (*Result, error)

Decode parses a TPMS Sub-GHz bit-stream (a string of '0'/'1' characters; ':' '-' '_' / whitespace separators tolerated).

type SynthInput added in v0.377.0

type SynthInput struct {
	SensorID uint32 `json:"sensor_id"`
	Payload  []byte `json:"payload,omitempty"` // data bytes after the 4-byte ID, before the CRC
	CRCPoly  byte   `json:"crc_poly"`          // 0x07 / 0x2F / 0x13; 0 → default 0x07
	GEThomas bool   `json:"ge_thomas"`         // false = IEEE 802.3 (default), true = G.E. Thomas
}

SynthInput describes a TPMS frame to generate: a 32-bit sensor ID, any data bytes that follow it (pressure / temperature / status — left to the caller, since their layout is per-model and unverifiable here, exactly as Decode declines to interpret them), the CRC-8 polynomial to seal it with, and the Manchester line convention.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL