This memo describes an idea for secure protocol used for communication with NRF24L01 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 128K 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.
Every device has: a copy of CA root certificate (ca
), a signed certificate for the device itself (crt
) and a private key to said certificate (priv
).
Upon communication, devices verify certificates using ca
, encrypt data with crt
of the remote party, and then remote party decrypts the data with priv
.
In ourder to achieve successfull encrypted communication, devices should:
0x1 Discover
/ 0x2 Hello
)0x3 Get Certificate
/ 0x4 Certificate
)0x5 Key Negotiate
/ 0x6 Negotiate Proceed
/ 0x7 Key Data
/ 0x8 Key Finish
/ 0x9 Negotiate Confirm
)Then they could do requests:
0xA Request
)0xB Proceed
)0xC Request Body
)0xD Response
/ 0xE Response Finish
/ 0xF Close Request
)It looks like this:
Kind | Length | Description |
---|---|---|
CRC |
4 bytes | CRC32 hash of packet including flags |
FLAGS |
1 byte | Packet Flags |
TYP
)CRC
does not match, the packet should be droppedName | Bits | Description |
---|---|---|
TYP |
first 4 bits, 0x0F | Kind of the packet |
Reserved | Last 4 bits, 0xF0 | Reserved |
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 listen to default address.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x1 |
V |
1 byte | Protocol version = 0x01 |
DEVICE_ID |
3 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. If the device is busy, the packet should be ignored.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x2 |
DEVICE_ID |
3 bytes | ID of the current device |
CA_CRC |
4 bytes | CRC32 of the CA certificate + DEVICE_ID |
CRT_CRC |
4 bytes | CRC32 of the certificate of the device |
CRT_SIZ |
2 bytes | Size of the certificate of the devices |
PRIORITY |
1 byte | Used-defined priority for the device |
The data specified in PRIORITY
shouldn't be trusted and must be only used to optimize network.
0x3 Get Certificate
If device chooses to connect to another device, but do not yet have its certificate, it needs to download it first.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x3 |
DEVICE_ID |
3 bytes | ID of the target device |
ADDR |
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
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
and respond with 0x4 Certificate
message to the same address ADDR
. If the target device is busy, it should ignore this message, and the sender will fail with timeout.
0x4 Certificate
If a device received a 0x3 Get Certificate
message it should switch on channel/address required and respond with this message.
A certificate has length up to 1K 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 certificate) is received with 0x2 Hello
. It is calculated by formula (certificate 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 Certificate |
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:
0x2 Hello
.x509_crt_verify(certificate, ca)
Common Name
)0x2 Hello
.If the certificate is validated, the certificate 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_TGT |
3 bytes | ID of the target device |
DEVICE_SRC |
3 bytes | ID of the current device |
ADDR |
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 Certificate of the target device should be obtained first (see 0x3 Get Certificate
).
After receiving this message, the sender will switch to randomly picked channel (CH
) and start listening on random address ADDR
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
. 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 Certificate of the current device yet, it should respond with 0x3 Get Certificate
on the address ADDR
channel CH
.
If the target device has the Certificate of the current device, it should respond back with 0x6 Negotiate Proceed
, inticating that it is ready for the negotiation process.
0x6 Negotiate Proceed
This message is used as a confirmation for beginning of the negotiation process.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x6 |
0x7 Key Data
After receiving the 0x6 Negotiate Proceed
message, the receiver should
NG_CH
(1 byte)NG_ADDR
(3 bytes)Referred as Key Data, then:
ADDR
/ channel CH
(stated in 0x5 Negotiate
).Because of length of the encrypted data (256 + 256 bytes), it should be delivered sequentially
(encrypted Key Data and Key Data signature should be concatenated).
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x7 |
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.
After sending out this sequence, the sender should follow up with message 0x8 Key Finish
.
0x8 Key Finish
After sending out the 0x7 Key Data
sequence, the sender should follow up with this message.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x8 |
SEQ |
1 byte | Number of the last (finish) packet in the requence |
KEY_SZ |
2 bytes | Size of the encrypted key was sent before |
SIG_SZ |
2 bytes | Size of the encrypted key key signature was sent before |
The data looks like this:
0 .. KEY_SZ - 1
Encrypted Key DataKEY_SZ .. SIG_SZ - 1
Signature of Encrypted Key DataIf the last packet is received in order, the receiving party should:
0x7 Key Data
as a source of data and 0x8 Key Finish
as a source of data length) with Private key of the current deviceDEVICE_ID
.0x9 Negotiate Confirm
to indicate the key has been successfully received.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.
0x9 Negotiate Confirm
This message sent as an indication of successfully received and processed Key Data (0x7 Key Data
/ 0x8 Key Finish
)
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0x9 |
DEVICE_TGT |
3 bytes | ID of the target device |
DEVICE_SRC |
3 bytes | ID of the current device |
If the message was received by device with different DEVICE_ID
from DEVICE_TGT
, the packet should be ignored.
This message should only processed in short time window after sending out the Key data to indicate the data was received and the session can be marked as negotiated.
0xA Request
If the two devices have exchanged the Certificates (0x3 Get Certificate
/ 0x4 Certificate
) and negotiated on Key Data (0x5 Negotiate
/ 0x6 Negotiate Confirm
/ 0x7 Key Data
) they might open a request exchange.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xA |
DEVICE_TGT |
3 bytes | ID of the target device |
DEVICE_SRC |
3 bytes | ID of the current device |
GCM_IV |
12 bytes | Random IV of the GCM for this request |
GCM_TAG |
8 bytes | GCM tag for current requst |
REQ_SZ |
1 bytes | Size of the Request Body divided by 16 (4080 max) |
The request should use a AES-128-GCM algorithm to encrypt the request body with GCM_IV
and produce a valid GCM_TAG
.
If the target device has negotiated on Key Data, it should switch to channel NG_CH
, listen on NG_ADDR
. 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 NG_ADDR
for message 0x9 Proceed
for short time limit. If the device does not receive a 0x9 Proceed
confirmation (or receives one that does not compute) within the time window, it should do several retries (on both defaul and NG_CH
channels), and then declare the request failed.
0xB Proceed
If a device received a 0xA Request
request, and the current device has negotiated the Key Data with the sender device, the device should respond back with this message .
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xB |
STATUS |
1 byte | Status flag |
SEQ |
1 byte | In case the STATUS = 0x01 Please retry contains last aknowledged packet |
Status flag cat take these values:
0x00
OK proceed0x01
Please retry (should restart sending out request body starting from SEQ
)0x02
BusyAfter receiving this message, the original sender of 0xA Request
should start sending the request body 0xC Request Body
within specific time window. If the response isn't received in time, unless the device is busy, the sender shourld retry.
0xC Request Body
After receiving the 0xB Proceed
message, the original sender of 0xA Request
should start sending previously encrypted request body within specific time window.
Because of length of the encrypted data (up to 256 bytes), it should be delivered sequentially.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xC |
SEQ |
1 byte | Number of the packet in the requence |
DATA |
upt to 26 bytes | 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.
SEQ
with value of 0
(zero) is received again, the process of receiving the request should be starter over again.After delivering the message, the sender should wait on same channel / address either for 0xD Response
or for 0xF Close Request
0xD Response
If a device received a 0xC Request Body
request, and the current device has negotiated the key with the sender device, the device should:
GCM_IV
) / Authentication Tag GCM_TAG
GCM_IV
/ GCM_TAG
0xE Response Finish
The request response should use a AES-128-GCM algorithm to encrypt the response body with GCM_IV
and produce a valid GCM_TAG
.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xD |
SEQ |
1 byte | Number of the packet in the requence |
DATA |
upt to 26 bytes | encrypted data |
0xE Response Finish
After receiving the 0xD Response
message, the original sender will follow up with this message.
Because of length of the encrypted data (SZ
, up to 240 bytes), it should be delivered sequentially.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xE |
SEQ |
1 byte | Number of the packet in the requence |
GCM_IV |
12 bytes | Random IV of the GCM for this response |
GCM_TAG |
8 byte | GCM tag of the response |
SZ |
1 byte | Accurate size of the Response Body divided by 16 (4080 max) |
After delivering the response, the sender should wait for some time window for:
0xA Request
in case the device that made current request wants to make more.0xF Close Request
in case the device that made current request has no more requests.In case nothing's received, the device should switch back to the default channel / default address.
0xF Close Request
Issued by the party that made the original request to indicate that there is no future requests and the request-response session should be closed.
Kind | Length | Description / Value |
---|---|---|
CRC |
4 bytes | CRC32 |
FLAGS |
1 byte | TYP = 0xF |