Skip to content

Instantly share code, notes, and snippets.

@ananimas-ligivon
Last active February 14, 2019 01:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ananimas-ligivon/8eb824f9c0d4704bd47b57946d10e9e2 to your computer and use it in GitHub Desktop.
Save ananimas-ligivon/8eb824f9c0d4704bd47b57946d10e9e2 to your computer and use it in GitHub Desktop.

Encrypted NRF25L01 Transport Protocol

This memo describes an idea for secure protocol used for communication with NRF25L01 chips in pair with STM32F103 chips.

This "format" used instead of standard TLS (like HTTPS over TLS over TCP/IP) because you can't really afford having these with something like 20K of RAM and 64K of ROM.

NRF devices should turn off the NRF's packet confirmation, configure address size to 3 bytes, switch to default channel and listen on default address.

Communication uses RSA encryption (2048 bit) to exchange AES key (128 bit), the latter then is used to encrypt the messages.

There is a master key pair, each device has a public key of it (ca.pub). Also, each device has a key pair for itself (device.pub, device.pem).

Eahc device has device.sig which is a signature of a Public Key + DEVICE_ID with private master key. Signature is calculated using RSA.sign(device.pub + device_id, ca.pem). It is used as a proof that device's public key is legit within the network.

Communication Flow

In ourder to achieve successfull encrypted communication, devices should:

  1. Find each other (0x1 Discover / 0x2 Hello)
  2. Exchange RSA public keys (0x3 Get Public Key / 0x4 Public Key)
  3. Negotiate on a AES key (0x5 Key Negotiate / 0x6 Key Data)

Then they could do requests:

  1. Open a connection (0x7 Request)
  2. Approve the connection (0x8 Proceed)
  3. Send the data (0x9 Request Body)
  4. Receive the response (0xA Response / 0xB Response Body)

Packet format

Kind Length Description
CRC 4 bytes CRC32 hash of packet including flags
FLAGS 1 byte Packet Flags
  • The rest of the packet depends on the packet type (TYP)
  • Packet size is handled by NRF's protocol itself
  • If CRC does not match, the packet should be dropped

Packet Flags

Name Bits Description
TYP first 4 bits, 0x0F Kind of the packet
Reserved Last 4 bits, 0xF0 Reserved

Packet Types

First 4 bits of the packet flags (TYP) contain packet type

0x1 Discover

This packet is sent to find devices nearby. By default, NRFs should turn off packet confirmation, switch to default channel and listed to default address.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x1
V 1 byte Protocol version = 0x01
DEVICE_ID 5 bytes ID of the current device

Everyone who receives the packet should respond within specific time window with packet 0x2 Hello. The time to wait is picked randomly.

0x2 Hello

A response to 0x1 Discover packet. Normally, everyone sho receive the message should respond:

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x2
DEVICE_ID 5 bytes ID of the current device
PUB 4 bytes CRC32 of Public Key + DEVICE_ID + Public Key Signature of the device
PUB_K 2 bytes Size of Public Key in bytes
PUB_SIG 2 bytes Size of Public Key Signature in bytes

0x3 Get Public Key

If device chooses to connect to another device, but do not yet have a public key of it, it needs to download it first.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x3
DEVICE_ID 5 bytes ID of the target device
ADDR_T 3 bytes Random NRF Address
ADDR_S 3 bytes Random NRF Address
CH 1 byte Random NRF Channel

After receiving this message, the sender will switch to randomly picked channel (CH) and start listening on random address ADDR_S for limited time. This is implemented to reduce traffic issues.

If a target device received this message, it should switch to the channel CH, listen on address ADDR_T and respond with 0x4 Public Key message to address ADDR_S. If the target device is busy, it should ignore this message, and the sender will fail with timeout.

0x4 Public Key

If a device received a 0x3 Get Public Key message it should switch on channel/address required and respond with this message.

Public key of 2048 bits has length of ~259 bytes, so considering limitation of NRF's max packet size (32 bytes), it has to be delivered sequentially.

The number of packets (and lenght of the Public Key + DEVICE_ID + Public Key Signature) is received with 0x2 Hello. It is calculated by formula (Public Key size + Public Key Signature size) / 26 (rounding up).

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x4
SEQ 1 byte Number of the packet in the requence
DATA up to 26 bytes Part of Public Key / Public Key Signature data

Packets are sent one by one, without confirmation for the receiving end. If a packet is received out of order, or same packet received twice, the receiver should just drop the request and switch to default channel / default address.

If the last packet is received in order, the receiving party should:

  1. Ensure the CRC32 of the Public Key + DEVICE_ID + Public Key Signature matches the one stated in 0x2 Hello.
  2. Verify the Public Key signature doing RSA.verify(public_key.pub + DEVICE_ID, ca.pub, signature)

If the public key is validated, the public key should be stored in ROM by DEVICE_ID. The sender should immeadetely switch back to the default channel / address after sending the last packet out of sequesnce.

0x5 Negotiate

If a device wants to send a request to another device, but they have not yet negotiated on Key Data, the device should send this message first.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x5
DEVICE_SRC 5 bytes ID of the current device
DEVICE_TGT 5 bytes ID of the target device
PUB 4 bytes CRC32 of Public Key of the sender + DEVICE_SRC + Public Key Signature of the sender device
ADDR_T 3 bytes Random NRF Address
ADDR_S 3 bytes Random NRF Address
CH 1 byte Random NRF Channel

If the message was received by device with different DEVICE_ID from DEVICE_TGT, the packet should be ignored. The Public Key of the target device should be obtained first (see 0x3 Get Public Key).

After receiving this message, the sender will switch to randomly picked channel (CH) and start listening on random address ADDR_S for limited time. This is implemented to reduce traffic issues.

If a target device received this message, it should switch to the channel CH, listen on address ADDR_T. If the target device is busy, it should ignore this message, and the sender will fail the request with timeout.

If the target device does not have the Public Key of the current device yet, it should respond with 0x3 Get Public Key on the address ADDR_S channel CH.

If the target device has the Public Key of the current device, it should generate a random Key Data, store it into the RAM, and respond with 0x6 Key Data on the address ADDR_S / channel CH.

0x6 Key Data

After receiving the 0x5 Key Negotiate message, the receiver should

  1. Generate random AES key (128 bits)
  2. Generate random Initial Vector (IV) (128 bits)
  3. Generate a random channel NG_CH (1 byte)
  4. Generate random pair SND_ADDRESS / RCV_ADDRESS (3 + 3 bytes)

Referred as Key Data, then:

  1. Encrypt Key Data (39 bytes) with Public Key of the sender
  2. Store the data into RAM
  3. Respond with this message on the address ADDR_S / channel CH.

Because of length of the encrypted data (245 bytes), it should be delivered sequentially.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x6
SEQ 1 byte Number of the packet in the requence
DATA up to 26 bytes Part of encrypted Key Data

Packets are sent one by one, without confirmation for the receiving end. If a packet is received out of order, or same packet received twice, the receiver should just drop the request and switch to default channel / default address.

If the last packet is received in order, the receiving party should:

  1. Ensure the Signature PUB matches the one stated in 0x5 Key Negotiate.
  2. Decrypt the encrypted Key Data with Private key of the current device

If the RSA key was successfully decrypted, the Key Data should be stored in RAM by DEVICE_ID.

Regardless if key was delivered or not, bot device should switch back to the default address / channel.

Key Data should be automatically invalidated in reasonable amount of time.

0x7 Request

If the two devices have exchanged the Public Keys (0x3 Get Public Key / 0x4 Public Key) and negotiated on Key Data (0x5 Negotiate / 0x6 Key Data) they might open a request exchange.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x7
DEVICE_SRC 5 bytes ID of the current device
DEVICE_TGT 5 bytes ID of the target device
KEY_CRC 4 bytes CRC32 of a Key Data + sender's DEVICE_ID
REQ_CRC 4 bytes CRC32 of encrypted Request Body
REQ_SZ 1 byte Size of the Request Body in chunks of 128 bit

If the target device has negotiated on Key Data, it should switch to channel NG_CH, listen on RCV_ADDRESS from SND_ADDRESS. If the target device has not yet negotiated with the current device, it should ignore this message.

The currenct device should also switch to channel NG_CH, listen on SND_ADDRESS for message 0x8 Proceed for short time limit. If the device does not receive a 0x8 Proceed confirmation (or receives one that does not compute) within the time window, it should several retries, and then discard the current negotiated Key Data and start the 0x5 Key Negotiate / 0x6 Key Data process again.

0x8 Proceed

If a device received a 0x7 Request request, and the current device has negotiated the Key Data with the sender device, the device should:

  1. Validate the CRC32 of the KEY_CRC with CRC32 of a negotiated Key Data + current device's DEVICE_ID
  2. Calculate the CRC32 of a negotiated Key Data + target address DEVICE_ID
  3. Respond back with this message
Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x8
PR_CRC 4 bytes CRC32 of a Key Data + IV + receiver's DEVICE_ID

After receiving this message, the original sender of 0x7 Request should start sending the request body 0x9 Request Body within specific time window.

0x9 Request Body

After receiving the 0x8 Proceed message, the original sender of 0x7 Request should:

  1. Validate the PR_CRC of the 0x8 Proceed message.
  2. Encrypt the body with AES key / IV (actually it whould be encrypted way back at 0x7 Request because of REQ_CRC)
  3. Start sending the request body within specific time window.

Because of length of the encrypted data (REQ_SZ * 16, up to 256 bytes), it should be delivered sequentially.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0x9
SEQ 1 byte Number of the packet in the requence
DATA 16 bytes 128 bits of encrypted data

Packets are sent one by one, without confirmation for the receiving end. If a packet is received out of order, or same packet received twice, the receiver should just drop the request and switch to default channel / default address.

After delivering the message, the sender should wait on same channel / address for 0xA Response Body

0xA Response

If a device received a 0x9 Request Body request, and the current device has negotiated the key with the sender device, the device should:

  1. Decrypt the received Request Body with RSA key / Initial Vector (IV)
  2. Process the request
  3. Send this message back
Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0xA
RSP_CRC 4 bytes CRC32 of encrypted Response Body
RSP_SZ 1 byte Size of the Response Body in chunks of 128 bit

After receiving this message, the original sender of 0x9 Request Body should listen form following messages 0xB Response Body within specific time window.

0xB Response Body

After receiving the 0xA Response message, the original sender will follow up with this message.

Because of length of the encrypted data (REQ_SZ * 16, up to 256 bytes), it should be delivered sequentially.

Kind Length Description / Value
CRC 4 bytes CRC32
FLAGS 1 byte TYP = 0xB
SEQ 1 byte Number of the packet in the requence
DATA 16 bytes 128 bits of encrypted data

Packets are sent one by one, without confirmation for the receiving end. If a packet is received out of order, or same packet received twice, the receiver should just drop the request and switch to default channel / default address.

After receiving the last message of sequence, the receiver should validate the RSP_CRC of the 0xA Response message, then decrypt the Response Body, process it.

After delivering the response, both the sender and receiver should switch back to the default channel / default address.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment