Documentation
¶
Overview ¶
Package zwave decodes classic Z-Wave MAC-layer frames per the Sigma Designs / Silicon Labs public specification (SDS-12852, Z-Wave Public API + Z-Wave Plus / 700/800-series protocol reference). Z-Wave is the dominant **sub-GHz home-automation mesh** protocol — used by Yale / Schlage / Kwikset Z-Wave door locks, Ring Z-Wave alarm, Aeotec / Fibaro sensors, GE Z-Wave dimmers, the SmartThings hub, and roughly every "smart home" controller that isn't pure Wi-Fi / Zigbee / Matter.
Operationally, Z-Wave runs around 868.42 MHz (EU) / 908.42 MHz (US) / 919.82 MHz (AU) on ITU-T G.9959 PHY at 9.6 / 40 / 100 kbit/s. The protocol is interesting to a Flipper Zero / SDR pentester because:
- **Door-lock attacks** — Yale / Kwikset Z-Wave locks respond to authenticated commands; replayed commands attack the SECURITY (S0, classic Z-Wave) and SECURITY_2 (S2, Z-Wave Plus) key-exchange stacks.
- **Mesh enumeration** — every Z-Wave network has a single HomeID (32-bit) and per-node NodeIDs; sniffing a handful of frames lets a pentester map the controller + every paired device.
- **Battery-drain DoS** — flooding "Wake Up Notification" frames against battery-powered Z-Wave sensors drives them out of FLiRS sleep mode and burns through CR2032 cells.
- **CTF + research** — DEF CON Wireless Village, Black Hat IoT track, and the SamyKam Z-Wave research have all published Z-Wave wire captures that this decoder ingests.
Wrap-vs-native judgement
Native. The Sigma Designs SDS-12852 Z-Wave Public API document is publicly available; the MAC-layer frame format is fixed (4-byte HomeID + 1-byte SourceNodeID + 2-byte Frame Control + 1-byte Length + 1-byte DestinationNodeID + payload + 1-byte XOR checksum) and the Command Class registry (Z-Wave Command Class Reference, Silicon Labs INS13954) maps the 256-entry Command Class space against documented application- layer behaviours. No crypto at the parse layer (the SECURITY / SECURITY_2 Command Classes are container classes; per-key AES-CMAC verification is out of scope).
What this package covers
**MAC-layer frame header** (SDS-12852 §3, 9 fixed bytes before the payload):
bytes 0-3: **HomeID** (uint32 BE; per-network identifier assigned by the primary controller at inclusion time).
byte 4: **SourceNodeID** (1 byte; the originating node's 8-bit address within the HomeID).
bytes 5-6: **Frame Control** (2 bytes; bit layout per below).
byte 7: **Length** (1 byte; total frame length in bytes INCLUDING the 1-byte trailing checksum).
byte 8: **DestinationNodeID** (1 byte; 0xFF = broadcast).
**Frame Control field** (2 bytes, big-endian):
byte 5 bits 0-3: **Header Type** (1 Singlecast / 2 Multicast / 3 Ack / 4 Explore).
byte 5 bit 4: Routed (set on a multi-hop relayed frame).
byte 5 bit 5: Ack Requested (sender wants a Type-3 Ack reply).
byte 5 bit 6: Low Power (transmitter is using reduced power; FLiRS-class devices).
byte 5 bit 7: Speed Modified (sender is using a non-default PHY data rate).
byte 6 bit 0: Beam Control (frame is a beam-mode poll for sleeping devices).
byte 6 bits 4-7: Sequence Number (4-bit per-pair monotonic counter used to pair Ack frames to their originating frames).
**Payload + Command Class header** (variable):
byte 0: **Command Class** (per Z-Wave Command Class Reference).
byte 1: **Command** (per-Command-Class operation code).
bytes 2+: per-Command parameters (dataset-specific).
**30+ entry Command Class name table** (selected high- runners from the Silicon Labs INS13954 registry): 0x20 `BASIC` / 0x25 `SWITCH_BINARY` / 0x26 `SWITCH_MULTILEVEL` / 0x27 `SWITCH_ALL` / 0x28 `SWITCH_TOGGLE_BINARY` / 0x2B `SCENE_ACTIVATION` / 0x30 `SENSOR_BINARY` / 0x31 `SENSOR_MULTILEVEL` / 0x32 `METER` / 0x40 `THERMOSTAT_MODE` / 0x42 `THERMOSTAT_OPERATING_STATE` / 0x43 `THERMOSTAT_SETPOINT` / 0x44 `THERMOSTAT_FAN_MODE` / 0x60 `MULTI_CHANNEL` / 0x62 `DOOR_LOCK` / 0x63 `USER_CODE` / 0x70 `CONFIGURATION` / 0x71 `ALARM` (also `NOTIFICATION`) / 0x72 `MANUFACTURER_SPECIFIC` / 0x73 `POWERLEVEL` / 0x75 `PROTECTION` / 0x77 `NODE_NAMING` / 0x80 `BATTERY` / 0x81 `CLOCK` / 0x82 `HAIL` / 0x84 `WAKE_UP` (the battery-drain attack target) / 0x85 `ASSOCIATION` / 0x86 `VERSION` / 0x87 `INDICATOR` / 0x8B `TIME_PARAMETERS` / 0x91 `MANUFACTURER_PROPRIETARY` / 0x98 `SECURITY` (S0; classic Z-Wave AES-128 wrapper) / 0x9F `SECURITY_2` (S2; Z-Wave Plus AES-CMAC + ECDH).
**4-entry Header Type name table**: 1 `Singlecast` / 2 `Multicast` / 3 `Ack` / 4 `Explore`.
**Trailing checksum** — 1-byte XOR of every byte from HomeID through the last payload byte, init 0xFF. Surfaced as hex but not re-computed.
What this package does NOT cover (deliberately out of scope)
- **PHY framing** — Z-Wave runs on ITU-T G.9959 FSK PHY at 868 / 908 / 920 MHz with Manchester / NRZ encoding plus a 4-byte preamble + 1-byte SOF. Feed this decoder the post-PHY MAC frame bytes (after preamble + SOF strip; Flipper Zero / HackRF / RTL-SDR + zwave-libs is the typical capture chain).
- **Z-Wave Long Range (LR)** — the 700/800-series LR variant (Z-Wave Plus v2) re-uses much of the classic wire format but extends NodeIDs to 16 bits and uses a different MAC layout; out of scope here.
- **SECURITY (S0) / SECURITY_2 (S2) crypto** — the `SECURITY` (0x98) and `SECURITY_2` (0x9F) Command Classes are container Command Classes that wrap an inner AES-CMAC-protected payload; this decoder surfaces the Command Class identifier + raw payload bytes but does not verify the integrity tag or decrypt the inner command (S0 attack research and S2 ECDH key-exchange analysis are higher-level work).
- **Routing-layer reasoning** — multi-hop Z-Wave frames carry repeater tables and routing source headers; the decoder surfaces the Routed bit + Length but does not walk the per-hop fields.
- **Multicast frame body** — the Multicast (Type 2) frame carries a NodeMask + per-node grouping that this decoder surfaces as raw payload (per-bit walker is future work).
- **Mesh-state reasoning** — inclusion / exclusion / wake- up FLiRS state-machine, per-NodeID sleep tracking; higher-level analysis driven by the controller log.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Result ¶
type Result struct {
TotalBytes int `json:"total_bytes"`
// MAC header
HomeIDHex string `json:"home_id_hex"`
SourceNodeID int `json:"source_node_id"`
FrameControlHex string `json:"frame_control_hex"`
HeaderType int `json:"header_type"`
HeaderTypeName string `json:"header_type_name"`
Routed bool `json:"routed"`
AckRequested bool `json:"ack_requested"`
LowPower bool `json:"low_power"`
SpeedModified bool `json:"speed_modified"`
BeamControl bool `json:"beam_control"`
SequenceNumber int `json:"sequence_number"`
Length int `json:"length"`
DestinationNodeID int `json:"destination_node_id"`
// Payload
CommandClass int `json:"command_class,omitempty"`
CommandClassName string `json:"command_class_name,omitempty"`
Command int `json:"command,omitempty"`
ParametersHex string `json:"parameters_hex,omitempty"`
// Trailing checksum (XOR; not re-computed here).
ChecksumHex string `json:"checksum_hex,omitempty"`
}
Result is the structured decode of a Z-Wave MAC-layer frame.