Mesh Access Module (ID 10)

Purpose

The MeshAccessModule with ModuleId 10 provides a simple way of accessing the mesh through a smartphone, it also provides a way to connect to other meshes or access other devices that support the functionality. It works over unencrypted GAP connections and uses a custom encryption on top to bypass operating system limitations of different devices.

Functionality

The MeshAccessModule works in conjunction with the MeshAccessConnection. The module provides the necessary services while the connection handles an individual connection after it was set up.

The MeshAccessConnection is able to handle standard mesh packets - including packet splitting - and will relay these packets to receivers in the mesh if security permits it (if the partner authenticated itself with the correct key during the initial encryption procedure). A MeshAccessConnection performs first a security handshake before data can be transmitted. After the handshake passes, a node may transmit information about its cluster and update this information once the cluster changes. Cf. ClusterInfoUpdate packet for more information.

There are different types of connection keys used to authenticate when connecting with a node. The keyId is then sent through the mesh depending on the packet type to authenticate the user over the mesh.

If parts of the documentation are obscure, it is advisable to take a look at the thoroughly documented relution/fruitymesh repository at the classes MeshAccessConnection.cpp and MeshAccessModule.cpp.

Connection Keys

For the different key types used, refer to the encryption types in the Specification.

Virtual Partner

Ids Because mesh packets need a sender and receiver id, it would not be possible to connect to another mesh as the nodeIds might clash. When a node connects to another device using a MeshAccessConnection, it will assign a virtualPartnerId to the other device, which is unique in the mesh. This partnerId is temporary (only while the connection lasts) and can be used to address the device during its connection.

The device does not get to know this virtual id and can operate with its own nodeId while using the connection. The node with modify received packets and will replace the devices id by the temporary id. It will also inspect packets before sending them to the other device and will translate the virtual id back into the id of the device before transmitting the packet over the MeshAccessConnection.

Tunnel Types

A node that connects to another mesh will need to specify the tunnelType, it can be on of the following:

  • MESH_ACCESS_TUNNEL_TYPE_PEER_TO_PEER = 0: The two nodes can communicate directly, but broadcast messages will not be relayed to the mesh on either side.

  • MESH_ACCESS_TUNNEL_TYPE_REMOTE_MESH = 1: Each node can be accessed in the remote mesh from the local node, but cannot relay packets from the remote mesh to the local mesh.

  • MESH_ACCESS_TUNNEL_TYPE_LOCAL_MESH = 2: All nodes from the local mesh can send and receive data to/from the node but can’t send data to the remote mesh.

The tunnel type PEER_TO_PEER is always used when fmKeyId NODE_KEY is specified while establishing a MeshAccessConnection. It will silently override any other tunnel type (such as REMOTE_MESH or LOCAL_MESH) when establishing the connection.

Authorization

Depending on the key type, certain messages are not allowed to be sent through a MeshAccessConnection. The key type can be any of the EncryptionKeys. To find out which key provides which level of access check CheckmeshAccessPacketAuthorization function. It return access level base on key type. Assuming the key is correct set of access right will be granted for the request with that key. The MeshAccessConnection passes on an incoming message to all modules. The modules can then decide if they want to whitelist or blacklist a message. It is also possible to restrict the message to be sent to the local node only. Blacklisting always has preference over whitelisting. If a message got blacklisted by a module, it cannot get whitelisted by another module. The reason behind is that someone who knows e.g. NodeKey should be able to communicate with given node but not with other nodes from that network. For that use case NetworkKey also exists that allows to communicate with whole network. But this access can be limited and granted to e.g. only 1 person.

Endianess

All of the MeshAccess data is encoded in little endian.

MeshAccess Broadcast Message

The Mesh Configuration and Access Service is broadcasted by active nodes that provide mesh access to smartphones or other devices through GATT. The MeshAccessBroadcast also has a scan response that reports the serial number of the node as the "Short Local Name". This is helpful for identifying devices when using 3rd party applications.

Bytes Type Description

3

Flags

(len, type, flags byte)

4

16bit Service UUID complete list

(len, type 0x03, 2 byte UUID 0xFE12)

4

Service Data header

(len, type 0x15, 2 byte UUID 0xFE12)

1

messageType

0x03 Mesh Configuration and Access Service (with a new service version, the messageType can be changed.

1

reserved

Must be 0

2

networkId

Mesh network ID

1 bit

isEnrolled

1 if the node is enrolled, 0 otherwise

1 bit

isSink

1 if the node is communicating with a MeshGateway, 0 otherwise

1 bit

isZeroKeyConnectable

1 if the node is connectable using the zero key, 0 otherwise

1 bit

hasFreeInConnection

1 if the node currently has unused incoming connections, 0 otherwise

1 bit

interestedInConnection

1 if asset wants to send a component_sense message, 0 otherwise

3 bit

reserved

Must be 0

4

serialNumberIndex

cf. Specification

1

moduleId0

ID of a module that is available over the mesh access connection (0 for invalid module)

1

moduleId1

ID of a module that is available over the mesh access connection (0 for invalid module)

1

moduleId2

ID of a module that is available over the mesh access connection (0 for invalid module)

Everything below is optional! Older versions of BlueRange Mesh (< 0.8.5000) may not support it.

1

DeviceType

The device type of the sender.

7

Reserved

Mesh access emergency connect mode

It is possible to disable the normal advertising of a node if it has an active mesh connection. This can be necessary for increasing energy efficiency or free radio time. In this case the node advertises with an interval of 2 seconds and the meshAccessBroadcastMessage type is EMERGENCY_MESH_ACCESS. Through this it is possible to connect to the node and enroll it without access to the connected network.

GATT Service

The Mesh Access Service is offered under a different UUID (a 128-bit UUID) in order to seperate different services from each other.

  • Base Service UUID 00000001-ACCE-423C-93FD-0C07A0051858

  • RX Characteristic Handle: 00000002-ACCE-423C-93FD-0C07A0051858

  • TX Characteristic Handle: 00000003-ACCE-423C-93FD-0C07A0051858

After a connection is made, it is necessary to register notifications on the TX characteristic in order to receive data from the node. Do not send any data before notifications are enabled!

Encryption Handshake

To establish a connection, the following steps need to be performed:

  • Central connects to peripheral

  • Central discovers the MeshAccessService of the peripheral with its rx/tx characteristics and the cccd of the tx characteristic

  • Central enables notifications on cccd of tx characteristic

    • The peripheral will notice the enabled notification and will instantiate a MeshAccessConnection throught the ResolverConnections

  • Central starts handshake by requesting a nonce

  • Peripheral anwers with ANonce

  • Central answers with SNonce in an encrypted packet (enables auto encrypt/decrypt)

  • Peripheral checks encrypted packet, sends encrypted HandshakeDone packet and enables auto encrypt/decrypt

Encryption and MIC calculation uses three AES encryptions at the moment to prevent a discovered packet forgery attack under certain conditions. Future versions of the handshake may employ different encryption.

The Encryption Handshake is explained in a lot more detail with example data as part of the MeshAccessConnection documentation.

Encryption

Once a connection is set to encrypted state - during the initial encryption handshake - all messages must be encrypted with a trailing Message Integrity Check (MIC). The data has the following format:

Bytes Type Name Description

1…​16

u8[]

encryptedData

Encrypted data that must be decrypted first, using the key determined during the handshake together with the decryptionNonce.

4

u32

mic

Message integrity check that protects the message against forgery or replay attacks, added at the end of the variable sized encryptedData field.

Because an encrypted packet has only 16 bytes of payload, message splitting must account for this. A connection with an MTU of 20 will first split packets into chunks of 20 bytes (2 byte splitting overhead, 18 byte content). After encryption is activated, the chunks have a size of 16 bytes.

  • Encryption is done by generating a key stream with the encryptionNonce. A 16-byte plaintext is created with 0x00 padding and the encryptionNonce is copied into the first 8 bytes. This plaintext is encrypted using the sessionEncryptionKey to produce a key stream.

  • Next, data to be sent is XOR-ed with the key stream. The data can be from 1 to 16 bytes long.

  • The last 4 bytes of the encryptionNonce (encryptionNonce[1]) are used as a counter and are now incremented.

  • A new key stream is generated with the increased nonce as explained above.

  • This key stream is again XOR-ed with the plaintext data to be sent.

  • The resulting cipher text is encrypted once more. The first 4 bytes can now be used as a MIC.

If the first message were to be encrypted with a nonce of 1, then the mic would have been generated with a nonce of 2. The next message to be sent must by encrypted with a nonce of 3.

Session Key Generation

A session key (sessionKey) is generated by creating a 16-byte plaintext message padded with 0x00. The first two bytes (1-2) must contain the nodeId of the central device. Bytes 3-10 must contain the nonce. This plaintext is then encrypted using the chosen key. In case the key is a user key, the key must first be derived from the userBaseKey. This works by creating a 0x00 padded 16-byte cleartext, storing the keyId in the first 4 bytes of the message and encrypting the cleartext with the userBaseKey. The resulting ciphertext is the derived user key.

Terminal Commands

Connection Establishment via BLE Address

Instructs a node to build a MeshAccessConneciton to another node. The connection state will be notified back to the requester. Refer to Specification for the key types.

//Establish a connection to another device using a MeshAccessConnection
action [nodeId] ma connect [bleAddress] {keyId=FM_NODE_KEY} {keyHex=<same as Local Key>} {tunnelType=PEER_TO_PEER} {requestHandle=0}

//E.g. Connect to device 00:11:.. with node key 11:22:...
action this ma connect 00:11:22:33:44:55 1 11:22:33:44:11:22:33:44:11:22:33:44:11:22:33:44

The node responds with information about the connection state changes. In this message, the node provides the virtual partner ID that was assigned to the node connected over the MeshAccessConnection.

//Example response where nodeId 1 is now connected and handshaked with another node
{"nodeId":1,"type":"ma_conn_state","module":10,"requestHandle":0,"partnerId":2001,"state":4}

Connection Establishment via Serial Number

Instructs a node to build a MeshAccessConneciton to another node. The connection state will be notified back to the requester. Refer to Specification for the key types. If no BLE address is given, the node will first scan for broadcast messages and will try to connect after it receives a matching one. If the BLE address is given, the connection will be established without any additional scanning, which is faster. Even if the BLE address given does not match, the node will still try to scan for the serial number as a fallback.

//Establish a connection to another device using a MeshAccessConnection
action [nodeId] ma serial_connect [serial number] [keyId] [key] [nodeId_of_partner_after_connect] [initial_keep_alive] {requestHandle=0} {bleAddress=""} {forceMode=0}

//E.g. Connect to device BBBBQ with node key 00:11:22:...
action 6 ma serial_connect BBBBQ 1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF 33012 20 13

//Same as above but with the BLE address given as well
action 6 ma serial_connect BBBBQ 1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF 33012 20 13 AA:BB:CC:DD:EE:FF

//Same as above but with a hint to the node that the connection should be established faster and more reliably (forceMode)
action 6 ma serial_connect BBBBQ 1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF 33012 20 13 AA:BB:CC:DD:EE:FF 1

serial number is the serial number of the other device that the connection will be established to.

keyId should start at 1.

nodeId_of_partner_after_connect must be inside the range of organization wide unique nodeIds [33000, 57999].

initial_keep_alive is merely a suggestion to the node. There are a lot of cases where a connection can also be prematurely disconnected.

forceMode is merely a suggestion to the node. The node is supposed to use parameters for the connection which lead to faster connections and connect more reliably.

Once the connection was established or cancelled, it is answered with the following JSON:

{"type":"serial_connect_response","module":10,"nodeId":6,"requestHandle":13,"code":0,"partnerId":33012}

where code can have the following values:

Value Name Description

0

SUCCESS

The Connection was successfully opened.

1

TIMEOUT_REACHED

It was impossible to build a connection due to a timeout.

2

OVERWRITTEN_BY_OTHER_REQUEST

A node never tries to build a connection to more than one node. If the connection is currently in the process of trying to connect to a serial number, but is then interfered by another serial connect message, the first connect attempt is cancelled, notifying the requester with this error code. NOTE: This code is not sent if both serial connect messages contain the same values. In such a case, the only thing that changes is that the timeout of the connection is replenished.

If the connection state changes, the sender of this message is informed about the new state with this message. In this message, the node provides the virtual partner ID that was assigned to the node connected over the MeshAccessConnection.

//Example response where nodeId 1 is now connected and handshaked with another node
{"nodeId":1,"type":"ma_conn_state","module":10,"requestHandle":0,"partnerId":2001,"state":4}

Disconnection

Disconnect from a device if it is connected via a MeshAccessConnection to that node.

//Disconnect a previously connected MeshAccessConnection
action [nodeId] ma disconnect [bleAddress] {requestHandle}

//E.g. disconnect device 00:11:... if connected to this node
action this ma disconnect 00:11:22:33:44:55

Messages

Message Types

#define MESSAGE_TYPE_ENCRYPT_CUSTOM_START 25
#define MESSAGE_TYPE_ENCRYPT_CUSTOM_ANONCE 26
#define MESSAGE_TYPE_ENCRYPT_CUSTOM_SNONCE 27
#define MESSAGE_TYPE_ENCRYPT_CUSTOM_DONE 28

Start Handshake

The central starts the encryption process by sending the following unencrypted packet:

Bytes Type Name Description

1

u8

messageType

MESSAGE_TYPE_ENCRYPT_CUSTOM_START

2

u16

senderId

Either a nodeId in the own mesh, or in case of a smartphone, this must be NODE_ID_APP_BASE (32000)

2

u16

receiverId

Set to 0 or if known, the ID of the partner

1

u8

version

Set to 1

4

u32

keyId

Set to the keyId that should be used for this connection

2 bit

u8:2

tunnelType

Tunnel type that should be used for this connection, cf. TunnelType. The invalid type must not be sent. E.g., if a Smartphone connects to a mesh, it should use REMOTE_MESH. If it just wants to interact with a single node and not with the mesh, it can use PEER to PEER.

6 bit

u8:6

reserved

Handshake ANonce

The peripheral will generate a random nonce with a length of 8 bytes and answer with an unencrypted packet. The peripheral can also start to generate the session decryption key at this time (cf. Session Key Generation generation chapter). After sending this packet, the peripheral only accepts encrypted packets.

Bytes Type Name Description

1

u8

messageType

MESSAGE_TYPE_ENCRYPT_CUSTOM_ANONCE

2

u16

senderId

nodeId of the peripheral in the mesh

2

u16

receiverId

Replay of the central id.

4

u32

anonce[0]

First part of the ANonce

4

u32

anonce[1]

Second part of the Anonce

Handshake SNonce

The central must now generate a random 8 byte nonce as well. It is then able to calculate both session keys, the key for encryption and the key for decryption. It will then send the following packet, but in encrypted form. The ANonce is used to generate the session encryption key for sending packets and the SNonce is used to calculate the session decryption key for receiving packets.

Bytes Type Name Description

1

u8

messageType

MESSAGE_TYPE_ENCRYPT_CUSTOM_SNONCE

2

u16

senderId

Sender ID

2

u16

receiverId

Receiver ID

4

u32

snonce[0]

First part of the SNonce

4

u32

snonce[1]

Second part of the SNonce

Handshake Done

The peripheral answers with the final handshake packet to confirm that the handshake was completed successfully. This packet is encrypted before transmission.

Bytes Type Name Description

1

u8

messageType

MESSAGE_TYPE_ENCRYPT_CUSTOM_DONE

2

u16

senderId

Sender ID

2

u16

receiverId

Receiver ID

1

u8

status

0: OK

Dead Data

If a connection receives undecryptable data it informs the connection partner by sending this unencrypted message and resets the handshake. After this happens, the connection does not process any data other than a new handshake. To send more data, the handshake has to be completed successfully. This is done in case the Nonces run out of sync between the connection partners. This can happen if the stack, e.g. of a mobile device silently drops packets and does not inform the application.

Bytes Type Name Description

1

u8

messageType

MessageType::DEAD_DATA (0x3D)

2

u16

senderId

Sender ID

2

u16

receiverId

Receiver ID

8

u8[8]

magic number

0xDE:AD:DA:DA:00:FF:77:33