Documentation
¶
Overview ¶
Package ble decodes BLE advertisement payloads — currently just Apple Continuity manufacturer-data — into operator-facing structures. Pure offline parser; no transport, no hardware.
Wrap-vs-native judgement: the Apple Continuity format is a reverse-engineered public spec — furiousMAC's project, hexway's AppleJuice writeups, AppleBleee and AppleAir-style scanners all document the same TLV layout. The dissector is a short walker (~150 LoC of bytes-to-field) over a byte slice. Wrapping a FAP for this would require an SD-card install + a firmware-fork dependency for a pure parser. Native delivers host-side analysis (paste a captured Apple manufacturer-data hex from a forum post or a Wireshark capture and decode without a Flipper attached), inline test coverage against published vectors, and a table the operator can extend without rebuilding firmware.
What this package covers:
- Apple Continuity TLV walker (with separator-tolerant hex intake; auto-strip of optional 4C00 manufacturer prefix and full AD-structure prefix).
- Named action types per furiousMAC's catalog (0x02 iBeacon through 0x12 Find My).
- Per-type field decoding for the well-documented action types — Nearby Info, Nearby Action, Handoff, Tethering, Proximity Pairing, AirDrop, Magic Switch.
What this package does NOT cover (deliberately out of scope):
- Decryption of encrypted bodies (Handoff, ProximityPairing past the public prefix) — Apple's session keys are not publicly recoverable.
- Tag-name lookup for the 0xDF range or any vendor-private types beyond Apple's set.
- Round-trip re-encode — happy to add if a caller materialises.
Index ¶
Constants ¶
const AppleManufacturerID = 0x004C
AppleManufacturerID is Apple's Bluetooth SIG company identifier. See https://www.bluetooth.com/specifications/assigned-numbers/.
const EddystoneServiceUUID uint16 = 0xFEAA
EddystoneServiceUUID is the 16-bit UUID Google assigned to Eddystone. Service-data fields prefixed with this UUID carry Eddystone frames.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ActionTLV ¶
type ActionTLV struct {
// Type is the Action Type byte — 0x05 (AirDrop), 0x10 (Nearby
// Info), etc. Stored as an int rather than byte so JSON renders
// as a plain integer.
Type int `json:"type"`
// TypeHex is the operator-facing form of Type — uppercase,
// always 2 chars, no 0x prefix ("05", "10"). Matches every
// public reference's format.
TypeHex string `json:"type_hex"`
// Name is the canonical name for the action type, or
// "Unknown" when the type isn't in our catalog. The walker
// still records and returns unknown types so operators can
// flag novel signatures.
Name string `json:"name"`
// Length is the declared payload length in bytes (the L byte
// of the TLV).
Length int `json:"length"`
// Hex is the operator-facing hex rendering of Value — always
// uppercase, no separators.
Hex string `json:"hex"`
// Value is the raw payload bytes.
Value []byte `json:"-"`
// Fields carries documented-action-type field decodes, keyed
// by short snake_case names. Nil for unknown types or for
// types whose body we don't dissect further.
Fields map[string]any `json:"fields,omitempty"`
// DecodeWarning is non-empty when the per-type decoder hit a
// recoverable shape issue (e.g. payload shorter than the
// documented minimum for that action). The TLV is still
// returned so the operator can see the raw bytes; the warning
// flags that Fields may be partial or missing.
DecodeWarning string `json:"decode_warning,omitempty"`
}
ActionTLV is one decoded Continuity TLV entry. For documented action types Fields carries the parsed sub-fields by name; for unknown types Fields is nil and the operator has Hex.
type Continuity ¶
type Continuity struct {
// TLVs is the ordered list of action-type entries pulled from
// the payload. May be empty when the payload is empty after
// prefix-stripping (legal but unusual — an empty 0x004C
// manufacturer record).
TLVs []ActionTLV `json:"tlvs"`
// Count is len(TLVs) — surfaced for callers that consume the
// JSON directly without needing to compute it.
Count int `json:"count"`
// StrippedPrefix records what the parser stripped from the
// front of the input before walking ("none", "manufacturer",
// "ad_structure"). Useful to confirm the parser interpreted
// the input as the operator expected.
StrippedPrefix string `json:"stripped_prefix"`
}
Continuity is the top-level decode result.
func Decode ¶
func Decode(hexBlob string) (Continuity, error)
Decode parses a hex-encoded Apple Continuity payload. Three input shapes are accepted; the parser strips any prefix it recognises:
- Bare TLVs: 10 02 1B 00
- With manufacturer ID: 4C 00 10 02 1B 00
- Full AD structure: 06 FF 4C 00 10 02 1B 00
Separators (':' '-' '_' whitespace) are tolerated so callers can paste hex from Wireshark, btmon, etc., without preprocessing.
func DecodeBytes ¶
func DecodeBytes(b []byte) (Continuity, error)
DecodeBytes is the byte-slice variant of Decode for callers that already have raw bytes. Same prefix-detection and walking logic.
type Eddystone ¶ added in v0.210.0
type Eddystone struct {
// FrameType is the frame-type byte at offset 0 of the
// service-data payload.
FrameType int `json:"frame_type"`
// FrameTypeHex is the operator-facing hex form ("00", "10",
// "20", "30").
FrameTypeHex string `json:"frame_type_hex"`
// FrameName is the canonical name ("UID", "URL", "TLM",
// "EID") or "Unknown" for out-of-catalog types.
FrameName string `json:"frame_name"`
// Fields carries the decoded per-frame-type fields. Nil for
// unknown types or when DecodeWarning is set.
Fields map[string]any `json:"fields,omitempty"`
// Hex is the operator-facing hex rendering of the full
// service-data payload (including the frame-type byte).
Hex string `json:"hex"`
// DecodeWarning is non-empty when the payload is shorter than
// the documented minimum for its frame type. The frame type
// and raw hex are still surfaced.
DecodeWarning string `json:"decode_warning,omitempty"`
}
Eddystone is the top-level decode result.
func DecodeEddystone ¶ added in v0.210.0
DecodeEddystone parses a hex-encoded Eddystone service-data payload. Three input shapes are accepted; the parser strips any recognised prefix:
- Bare service data: 10 02 00 03 67 6F 6F (URL frame for "goo")
- With UUID prefix: AA FE 10 02 00 03 67 6F 6F
- Full AD structure: 0A 16 AA FE 10 02 00 03 67 6F 6F
(AD-type 0x16 = ServiceData16Bit; the UUID 0xFEAA is little-endian on the wire as AA FE.)
Separators (':' '-' '_' whitespace) are tolerated.
type EddystoneFrameType ¶ added in v0.210.0
type EddystoneFrameType byte
EddystoneFrameType enumerates the four documented frame types.
const ( // FrameUID — 16-byte beacon ID (10-byte namespace + 6-byte // instance). 18-byte total payload. FrameUID EddystoneFrameType = 0x00 // FrameURL — encoded URL up to ~17 bytes after the scheme // byte. Variable-length payload. FrameURL EddystoneFrameType = 0x10 // FrameTLM — telemetry: battery voltage, temperature, advert // count, time-since-boot. 14-byte payload. FrameTLM EddystoneFrameType = 0x20 // FrameEID — Ephemeral ID (rotating 8-byte token, requires // a server-side key to resolve). 10-byte payload. FrameEID EddystoneFrameType = 0x30 )