Skip to content

Latest commit

 

History

History
187 lines (141 loc) · 7.26 KB

File metadata and controls

187 lines (141 loc) · 7.26 KB

PAX 3 BLE Protocol Notes

Compiled from public reverse engineering research. All information is derived from:


BLE Services

Device Information Service (standard)

UUID Description
0x180A Device Information Service
0x2A25 Serial Number String — critical for key derivation
0x2A24 Model Number String
0x2A26 Firmware Revision String
0x2A29 Manufacturer Name String

PAX Proprietary Service

UUID Description Properties
8E320200-64D2-11E6-BDF4-0800200C9A66 PAX Service
8E320201-64D2-11E6-BDF4-0800200C9A66 Read characteristic Read
8E320202-64D2-11E6-BDF4-0800200C9A66 Write characteristic Write
8E320203-64D2-11E6-BDF4-0800200C9A66 Notify characteristic Notify

The 8E32 prefix and 64D2-11E6-BDF4-0800200C9A66 suffix are shared across all PAX service UUIDs.


Packet Format

All packets exchanged on the read/write characteristics are 32 bytes:

[ ciphertext: 16 bytes ][ IV: 16 bytes ]
  • Encryption: AES-128 in OFB mode
  • IV: the last 16 bytes of the 32-byte packet
  • The IV is randomly generated by the sender for each packet

Decrypted plaintext structure (16 bytes):

[ message type: 1 byte ][ payload: up to 15 bytes ][ zero padding ]

All multi-byte values are little-endian.


Key Derivation

  1. Read the serial number from characteristic 0x2A25 (8 ASCII characters, e.g. A1B2C3D4)
  2. Concatenate it with itself: A1B2C3D4A1B2C3D4 → 16 characters
  3. Encode as UTF-8 → 16 bytes
  4. Encrypt with the shared key using AES-128 ECB mode
  5. The result is the 16-byte session key

Shared key (publicly documented in multiple open-source projects):

8A EC F0 AB DE F0 4E 30 B8 00 A0 D7 64 C0 9E 65

This key is hardcoded in all versions of the PAX mobile app and is not a secret — it is documented in multiple public security research publications.


Notification Flow

  1. Subscribe to the Notify characteristic (8E320203)
  2. When data is available, the device sends a notification on 8E320203
  3. The notification value itself is not the data — it signals readiness only (may contain first byte of pending data, but this is discarded)
  4. In response to a notification, the host reads the Read characteristic (8E320201)
  5. The 32-byte read value is then decrypted as described above

Message Types

Value Name Direction Payload
0x01 ActualTemp Device → Host 2 bytes LE uint16: temperature in °C × 10
0x02 HeaterSetPoint Both 2 bytes LE uint16: temperature in °C × 10
0x03 Battery Device → Host 1 byte: 0–100%
0x04 Usage Device → Host Unknown format
0x05 UsageLimit Both Unknown
0x06 LockStatus Device → Host 1 byte: 0 = unlocked, 1 = locked
0x07 ChargeStatus Device → Host 1 byte: charge state (not fully decoded)
0x08 PodInserted Device → Host 1 byte (PAX Era only)
0x09 Time Both Unknown format
0x0A DisplayName Both 1 byte length + UTF-8 string
0x11 HeaterRanges Device → Host Unknown format
0x13 DynamicMode Both 1 byte mode ID (PAX 3)
0x14 ColorTheme Both Unknown
0x15 Brightness Both 1 byte (0–100?)
0x17 HapticMode Both 1 byte mode ID
0x18 SupportedAttributes Device → Host 8 bytes LE uint64 bitfield; bit N set = attribute N supported
0x19 HeatingParams Both Unknown
0x1B UiMode Both 1 byte
0x1C ShellColor Both Unknown
0x1E LowSoCMode Both Unknown
0x1F CurrentTargetTemp Device → Host 2 bytes LE uint16: PID target in °C × 10 (PAX 3)
0x20 HeatingState Device → Host 1 byte; see table below (PAX 3)
0x28 Haptics Both Unknown
0xFE StatusUpdate Host → Device 8 bytes LE uint64 bitfield; request device send current values of indicated attributes

HeatingState values

Byte State
0x00 Off
0x01 Standby
0x02 Heating
0x03 Ready (at temperature)
0x05 Cooling
0x08 Boost mode

Temperature encoding

encoded_value = celsius * 10
celsius = encoded_value / 10.0

Example: 180°C → 0x0708 (1800 decimal, little-endian: 08 07)


StatusUpdate Request (0xFE)

To request current values, send a packet with type 0xFE and an 8-byte little-endian bitfield payload where each bit corresponds to the attribute number you want:

// Request battery (bit 3) and actual temp (bit 1)
var bitfield: UInt64 = 0
bitfield |= (1 << 3)   // battery
bitfield |= (1 << 1)   // actual temp

The device will respond with individual notification packets for each requested attribute.


Implemented in This App

Feature Status
Service discovery
Serial number read → key derivation
Notify subscription
Full status request (0xFE)
ActualTemp (0x01) decode
HeaterSetPoint read (0x02)
HeaterSetPoint write (0x02)
Battery (0x03)
HeatingState (0x20)
LockStatus (0x06)
CurrentTargetTemp (0x1F)
DisplayName (0x0A)
Firmware / model from Device Info

Open Uncertainties

  1. Maximum packet length: All tested packets are 16 bytes plaintext (32 bytes encrypted). Whether longer payloads are supported is not confirmed.

  2. ChargeStatus (0x07): The exact byte encoding is not publicly documented. The app ignores this value.

  3. HeaterRanges (0x11): Format unknown. Possibly encodes min/max allowed temperature bounds.

  4. DynamicMode (0x13): Known PAX 3 values are Standard (0x00), Boost (0x01), Efficiency (0x02), Stealth (0x03), and Flavor (0x04). Their exact heating algorithms remain device-controlled and are not fully documented.

  5. SupportedAttributes (0x18): Should be queried first to know which attributes a device supports, but has been omitted from the initial implementation for simplicity.

  6. Pax Era / Era Pro compatibility: Most message types are documented as "All devices" but this app has only been designed around PAX 3. Era-specific messages (PodInserted, etc.) are parsed but not displayed.

  7. Endianness of HeatingState: Assumed 1-byte, no multi-byte values documented. Some unknown states may exist between the known values.

  8. Write response timing: Whether the device requires a delay between consecutive write commands is not documented. This app writes one packet at a time and relies on CoreBluetooth's .withResponse write type.

  9. Session re-keying: Whether the session key changes between connections (it shouldn't, as it is derived purely from a static serial number) is unconfirmed.

  10. Firmware versions: Protocol may have changed between firmware versions. All documentation is based on app analysis circa 2021.