Skip to content

Latest commit

 

History

History
987 lines (790 loc) · 31.4 KB

linklayer.md

File metadata and controls

987 lines (790 loc) · 31.4 KB
title description icon
iown-homecontrol - Data Link and Network Layer
io-homecontrol protocol and data format specification
material/layers-triple

io-homecontrol Link Layer: Protocol and Format Specification

There are at least two known protocols with some kind of third protocol especially for OEMs ("EMS2) that gets triggered with a long unmodulated carrier wave...

---
title: io-homecontrol Frame
---
%%{init:{"fontFamily":"monospace"}}%%
block-beta
columns 9
  Frame["Frame: 1W"]:9
  block:frame:9
    cb1["CTRL<br/>BYTE1"]
    cb2["CTRL<br/>BYTE2"]
    sa["SOURCE<br/>NodeID"]
    da["DEST<br/>NodeID"]
    cmd["Command<br/>ID"]
    data["Main<br/>Parameter"]
    rc["Rolling<br/>Code"]
    hmac["HMAC"]
    crc["CRC"]
  end
  space:1
  size["Size"]:7
  space:1
  checksum["CRC-16/KERMIT"]:8
Loading

Protocol Frame

  • Length
    • Minimum: 11 bytes (i.e. without any DATA)
    • Maximum: 32 bytes (i.e. maximum 21 bytes of DATA)
      • 0b11111 = 31 = 0x1F

getMasterAddress getRSSILevel getNodeAddress getFrameType getCtrlByte0 getCtrlByte1 getCtrlByteManufCode getCtrlByteProtocolVersion getCommand getData isOneWay getDataLen

Message_GetBroadcastType (node_id_t NODE_ID) {

  if(NODE_ID[0]) return 0xD;

  if(NODE_ID[1] || (NODE_ID[2] & 0xC0) != 0) {
  switch (NODE_ID[2] & 0x3F) {
    case 0x3B:   return 0x7;
    case 0x3C:   return 0x8;
    case 0x3D:   return 0x9;
    case 0x3E:   return 0xA;
    case 0x3F:   return 0xB;
      default:   return 0xC;
  }}

  else if (NODE_ID[2]) {
  if(NODE_ID[2] <= 0x3A) {
                 return 1; }
  else {
  switch (NODE_ID[2]) {
    case 0x3B:   return 2;
    case 0x3C:   return 3;
    case 0x3D:   return 4;
    case 0x3E:   return 5;
      default:   return 6;
  }}}
                 return NODE_ID[2]

―———————————R T F M―———————————

Control Byte 0 - Basic Frame Information

BIT 7-6 5 4-0
NAME Order Protocol
Mode
Size
  • Order: Indicates Relationship (Logical and/or Temporal) between transmitted Orders.

    # bit[7] bit[6] Command Order Relationship
    (First/Last Frame in Session)
    0 0 0 Single = Last/First Session Frame: No/No
    1 0 1 Next in Series = Last/First Session Frame: No/Yes
    2 1 0 Next in Parallel = Last/First Session Frame: Yes/No
    3 1 1 Command Group End = Last/First Session Frame: Yes/Yes
  • isOneWay: Protocol Mode

    • 0 = 2W = Two Way
    • 1 = 1W = One Way
  • Size: Frame Length in byte excluding Control Byte 0 and the CRC

Control Byte 1 - Extended Frame Information

BIT 7 6 5 4 3 2 1-0
NAME Use Beacon Routed Low Power Mode Ack ? ? Protocol Version
  • Use Beacon (isBeacon): Repeater Mode = Allow routing the Frame through the Network
    • 0: Do Not use Beacon
    • 1: Use Beacon
    • Note: Max. 1 Hop!
  • Routed (isRouted): Indicates if Frame has already been routed through a Beacon (Repeater)
    • 0 frame has not been routed
    • 1 frame has been routed
  • Low Power Mode (PowerSaveMode): Indicates if Frame is sent to Device in Low Power Mode (LPM):
    • 0 = Destination Device is not in Low Power Mode
    • 1 = Destination Device is in Low Power Mode
  • Ack indicates if a response can be handled (2W Devices Only)
    • 0 = NACK: 1W Device
    • 1 = ACK: 2W Device

Addresses (NodeId)

Addresses are 3 bytes long and can range from 01 00 00 to ff ff ff.

Addresses are also refered to as Node ID which resembles the MS-B of a typical MAC-Address. The Node Type is a numerical value defines which

Destination Address

  • 00 00 3F is broadcast
  • 00 00 3B ??
  • 00 00 00 = Group
  • FF FF FF = Broadcast

Source (Sender) Address

  • FFFFFE = SRC for P2P and BROADCAST
  • FFFDFF = RS485 (SDN) Setting Tool

Command and Command Parameter

Go further down the rabbit hole -> Commands

Data

  • Data Length
    • Minimum = 0 bytes (no DATA)
    • Maximum = 21 bytes

Depending on the frame type it can correspond to raw data, io-homecontrol command parameters or manufacturer-specific data.

Note that in authenticated 1-Way frames, a 2-bytes sequence number and a 6-byte long message authentication code are appended:

X - Y (Y + 1) - (Y + 2) (Y + 3) - (Y + 9)
Data Sequence number MAC

CRC

After the payload, there is a 16-bit CRC with polynomial 0x(1)8408 over the data packet (i.e. length byte + payload). The CRC's initial value is 0 and it does not employ an XOR/NOT. The least significant byte of the CRC is transmitted first.

The C# implementation shown here can be found in the Starter Kit software from Semtech for their SX12xx line of radios. Just use a .NET decompiler (dotPeek, ILSpy, etc.) and search for it:

  // dumped ioHC CRC code from the Semtech SX12xx series software
  ushort Crc(byte[] packet){
    for (i=0; i<packet.Length; i++) {
      packet[i]=(byte)((packet[i] * 0x802 & 0x22110 | packet[i] * 0x8020 & 0x88440) * 0x10101 >> 0x10);
    }
    return 0;
  }

Keys and Discovery

... and now for something completely different^^

System / Stack Key

Each two-ways controller has a stack or system key. This is an AES-128 key used to sign io frames with the trailing MAC.

Advanced Encryption Standard (AES) 128-bit block encryption/decryption with 128 bits key size. Electronic Code Book (ECB) and Cipher Block Chaining Mode 1 (CBC Mode 1) are supported. The AES engine is enabled on the ADF7022 by downloading the AES software module to program RAM.

The key generation on Kizboxes is insecure because it relies on the LuaJIT math.random:

  -- seed
  kizbox_id = "xxxx-xxxx-xxxx" -- Kizbox serial
  math.randomseed(tonumber(string.gsub("1" .. kizbox_id, "-", "")) - os.time())
  -- key generation
  key = {}
  keySize = 16 -- 128 bits
  for i = 1, keySize do
      key[i] = math.random(0, 255)
  end

This mechanism only leaves roughly 31,536,000 keys possible if the serial number of the TaHoma and the year of setup are known (other calls to math.random are made so the actual number may be a bit above this figure but once the seed and the number of times math.random is called are figured out by an attacker, they can completly determine the key).

1W Discovery

sequenceDiagram
    participant 1W as Remote
    participant 2W as Actuator
    Note over 1W,2W: 1-Way Discovery
    1W->>2W: Command ID: 0x39<br/>Remove 1-Way Remote
    activate 2W
    loop 0x39
        2W->>2W: Remove<br/>1-Way Key
    end
    deactivate 2W
    1W-->>2W: Command ID: 0x30<br/>Send Encrypted 1-Way Key
    activate 2W
    loop 0x30
        2W->>2W: Store<br/>1-Way Key
    end
    deactivate 2W
Loading

In 1-way mode, the controller does not get any answer. So this is the simplest discovery mode, it asks for exclusion using 0x39 and then sends its 1-W encrypted key using 0x30.

2W Discovery: Simple Discover

  • Controller sends command 0x28 (discover) when entering discover mode
  • Device in pairing mode answers with command 0x29 (discover answer) and gives metadata to identify itself to the controller
  • Controller confirms having received discovery information from device
  • Devices acks confirmation
  # Discover request
  C8 00 00003B F00F00 28 1234
  # Discover answer: node type REMOTE_CONTROLLER, subtype 0, node address feefee, manufacturer Atlantic
  # multi info byte 0xcc, timestamp 0000
  D1 00 F00F00 FEEFEE 29 FFC0 FEEFEE 0C CC 0000 1234
  # Discover confirmation
  48 00 FEEFEE F00F00 2C 1234
  ## Discover confirmation ack
  88 00 F00F00 FEEFEE 2D 1234

Must send a 0x38 key transfer, just after the 0x2D discover confirmation ack to effectivly have the device added

  # Controller ask for key transfer using challenge 123456789ABC
  4E 04 FEEFEE F00F00 38 123456789ABC 23B6

2W Discovery: Specialized Discover

  • Controller sends command 0x2a (specialized discover) with specific data not identified to this day
  • Device in pairing mode answers with 0x2b and same information fields as in 0x29
  • Controller and device exchange 0x2c and 0x2d just like in the general discover
  • Controller sends command 0x36, authenticates and expects an address in 0x37 answer (see 2-way key exchange push below)

Pairing and Authentication

Before being able to communicate with authentication, io-homecontrol nodes must share a common secret as the security of the protocol relies on a symmetric encryption algorithm.

The pairing process consists in transmitting the key. Authentication in adding and verifying challenges.

In both modes (1W/2W), the transferred key is obfuscated using different methods. But both methods imply a shared key probably specified in the protocol documentation.

This key is referred to as transfer key and has the following value, probably hardcoded in the specification:

Important

34C3466ED88F4E8E16AA473949884373

Other Keys found in the io Gateway:

  • SDNP io Actuator 1W Control Key = FD534F4D4659 = 0xFD SOMFY
  • 4275696C64696E6720436F6E74726F6C = Building Control

Tip

All Crypto functions are available in Iown-ioCrypto.py

Initial Vector (IV) and AES-128 Encryption

In all cases implying encryption, an Initial Vector (IV) is required to feed the AES algorithm using a mode similar to OFB or CFB. In fact, as all payloads are 128-bit long, there is no block chaining and the encryption process is the following:

  1. Generate IV
  2. Encrypt IV with Transfer or System Key (depending on data) using AES-128
  3. XOR encrypted IV with the Secret for Transmission
  • How To Generate IV:

    Mode 0-7 8-9 10-11 12-15
    1W First 8 byte of Payload Checksum Sequence Number Padding (0x55)
    2W First 8 byte of Payload Checksum Challenge (sent beforehand)
    • Pad Payload if less than 8 byte with 0x55
    • Compute Checksum for each Frame byte:
      # Checksum Return Value: 2 byte Tuple (byte 0 & byte 1)
      # Initialization Value for both bytes: 0
      def computeChecksum(frame_byte, chksum1, chksum2):
        tmpchksum = frame_byte ^ chksum2
        chksum2 = ((chksum1 & 0x7f) << 1) & 0xff
      
        if chksum1 & 0x80 == 0:
          if tmpchksum >= 128: chksum2 |= 1
          return (chksum2, (tmpchksum << 1) & 0xff)
      
        if tmpchksum >= 128: chksum2 |= 1
      
        return (chksum2 ^ 0x55, ((tmpchksum << 1) ^ 0x5b) & 0xff)

Example

  • Frame: F6 00 00003F 385762 000143D2000000 0599 123456789ABC 5FB0
    • Payload: 000143D2000000 (7 byte => needs 1 byte padding with 0x55)
    • Sequence Number: 0599

Padded Payload: 000143D200000055

IV will be: `000143D2000000550500059955555555`

In 2-way Mode, for the following exchange:

Ask Challenge: 480012195FD35C18 31 28E3

Challenge Request: 0E00D35C1812195F 3C 123456789ABC A9C7

IV will be: 31555555555555550062123456789ABC

Encryption with AES-128 follows this process:

  • IV is generated as described above
  • IV is encrypted using AES-128 and depending on the use case with either:
    • Transfer Key
    • Stack Key
  • If encrypting a Secret: The Secret is XORed with the Output of the previous Step.
  • If creating a MAC: The output of the previous Step is truncated to 6 bytes.

Key Exchange

1W Key Exchange

In 1-way mode, the controller will send several times the command 0x30 with its key encrypted with the transfer key listed above and an initial value that consists in its address repeated several times to build a 16-byte long value.

For node with address ABCDEF, the initial value will be:

ABCDEF ABCDEF ABCDEF ABCDEF ABCDEF AB

Furthermore, the 0x30 message is authenticated using a 1W MAC embedded in the command itself. For the node ABCDEF with key 01020304050607080910111213141516 and sequence number 0x1234 the 0x30 message will be:

FC 00 00003F ABCDEF 30 7E60491F976ADF653DB0ED785E49A201 02 01 1234 19E81EC43D5E 9BF2

2W Key Exchange

Note

In the examples below, the stack key used is 01020304050607080910111213141516

In 2-Way mode, there are 2 ways keys can be exchanged:

  • By pulling the key from a remote node
    • RCM: Receive Controller Mode
  • By pushing a key to a remote node
    • TCM: Transmit Controller Mode

It seems that when a new device is added to a stack, both methods are used. First, the controller will collect the already set device key and then use it to authenticate requests to push its stack key to the device.

Pull
  • Actions:
    • Right after initial discovery, the controller issues command 0x38 (launch key transfer). This command is not authenticated but submits a 6-byte long initial value to be used to encrypt the key used by the device
    • The device answers with command 0x32 (key transfer) and sends its key encrypted using the transfer key
    • The controller issues a command 0x3c (challenge request) to authenticate the previous command
    • The device answers to the challenge with command 0x3d and a response encrypted using the key transmitted just before in command 0x32

Example Device key is ABCDEF01020304050607080910111213

  // Controller ask for key transfer using challenge 123456789ABC
  4E 04 FEEFEE F00F00 38 123456789ABC 23B6

  // Device creates an initial value based on last frame and the specified challenge
  // and use this initial value to encrypt its key before transmission
  18 04 F00F00 FEEFEE 32 EA425A7A182885D4EAEEFD416D625E01 6379

  // Controller challenges the device for command 0x32 (see authentication below)
  // Note: in real life, challenge will be different to the one specified in 0x38
  0E 00 FEEFEE F00F00 3C 123456789ABC 5EB1
  4E 00 FEEFEE F00F00 3C 123456789ABC EC2A

  // Device creates an initial value based on the 0x32 frame and challenge specified in 0x3c
  // It will use its own key to authenticate as it has not received one from the controller
  8E 00 F00F00 FEEFEE 3D 0AE519A73C99 2400
Push

Note

Stack key push to device has been observed after a bit of information exchange such as general information (0x54 and 0x56). Most of these commands are authenticated using 0x3C and 0x3D (see authentication below) and so are the push commands.

  • Actions:
    • First, the controller asks the device a challenge using command 0x31 (ask challenge)
    • The device then answers using command 0x3C (challenge request) and a 6-byte long challenge
    • The controller sends the encrypted stack key to the device node with command 0x32 (key transfer) using the transfer key
    • The device asks the controller for authentication using 0x3C
    • The controller authenticates using its stack key (not the device key anymore) and command 0x3d
    • The device answers with command 0x33 to confirm that stack key has been received
    • To check if the stack key has properly been transferred, the controller sends the 0x36 command and authenticate to finally receive the address of the device in the 0x37 command

Example

  // Send challenge request
  48 00 FEEFEE F00F00 31 FB60

  // Challenge request
  0E 00 F00F00 FEEFEE 3C 123456789ABC 19DB

  // Controller creates an initial value based on last frame and the specified challenge
  // and use this initial value to encrypt the stack key before transmission
  18 00 F00F00 FEEFEE 32 102E49A16D3B69726F3192CF17534AD9 8043

  // Device challenges the controller for command 0x32 (see authentication below)
  // Note: in real life, challenge will be different to the one specified in previous 0x3c
  0E 00 F00F00 FEEFEE 3C 123456789ABC 19DB

  // Controller answers to the challenge using the stack key
  0E 00 FEEFEE F00F00 3D 8DC9D40DC7A4 F9E5

  // Device saves the stack key and sends a confirmation
  88 00 F00F00 FEEFEE 33 5BFB

  // Controller checks if the device received the stack key by issuing a control command
  48 04 FEEFEE F00F00 36 9A02

  // Device sends a challenge to the controller
  0E 00 F00F00 FEEFEE 3C 123456789ABC 19DB

  // Controller answers the challenge for command 0x36
  0E 04 FEEFEE F00F00 3D C7FDC0668818 B1E3

  // Device sends its address as answer in 0x37
  0B 04 F00F00 FEEFEE 37 FEEFEE 7CCF

Authentication

io-homecontrol frames are authenticated using AES-128 based MACs. There are differences between 1W and 2W modes. In both cases, the MAC is created by truncating the output of the AES-128 algorithm down to 6 bytes.

1W Authentication

In 1W mode, a signature of the frame is made using the stack key burned into the 1W controller during manufacturing. This signature is appended to the frame along with a sequence number to prevent replay attacks.

The data and sequence number are handled separately in the initial vector generation (i.e. the sequence number is not handled as frame data and not taken into account in the first part of the IV).

2W Authentication

In 2W mode, a node will issue a 0x3C command frame with a 6-byte long challenge to commands received from other nodes. The asking node must provide a 0x3D command with the answer to the 6-byte long challenge (the answer is also 6-byte long). 2W frames do not have any MAC or sequence number incorporated in the frame itself.

For example, here is a 2W sequence of request/answer type:

  // Send address request
  48 04 FEEFEE F00F00 36 9A02

  // Challenge request
  0E 00 F00F00 FEEFEE 3C 123456789ABC 19DB

  // Challenge answer
  0E 04 FEEFEE F00F00 3D C7FDC0668818 B1E3

  // Address answer
  0B 04 F00F00 FEEFEE 37 FEEFEE 7CCF

Caution

There is a race condition vulnerability: In fact, it would be possible to spoof answers by answering faster than the legitimate device after a 0x3d frame. This allows authentication of the asking node but not authentication of the answering node...

The MAC is generated using a shared key between both nodes. This is referred as Stack Key in the firmware and System Key in the user interface. The same key is shared throughout the installation (by a 2-Way controller).

Below is an extract of the MAC generation code:

  def create_2W_hmac(challenge, system_key, frame_data):

    iv = constructInitialValue(frame_data, challenge)

    cipher = aes.AES(system_key)

    return cipher.encrypt_block(iv)[:6]

The initial value is always created using data from the requesting command (see above for details on initial value generation).

Test/Calibration Data and EMS Frames

EMS Frames

Sadly little is known about the EMS2 device and the protocol that is used in the factory to program io-homecontrol devices.

EMS frames in binary, first 0 is D then frame in binary EMS_STOP_BECAUSE_SINE_WAVE_IS_MISSING

Calibration Data

Those values get send directly to the radio. Since they are always 3 byte and the numbers are just the right amount apart my assumption is that those values hold the frequencies.

io Tests Terminal: 0E5EC4 0ECD0B 0F5AD4 ideal RF STM32 CAL: 0E4EC4 0EBD0B 0F4AD4000000 io-homecontrol CAL: 0E4EC4 0EBD0B 0F4AD4 (default)

More information can be found in STM32 section...