5

What is the most simple, but yet well-known "protocol" to protect a unidirectional channel over air?

Requirements

The protocol should provide common properties like privacy, integrity and replay protection.

Forward secrecy is not really necessary in our thread model, nor do I need advanced handshakes as I can trivially setup a random per-device session key securely out-of-band.

Background

I'm working on a "IoT"-solution where we use LoRa for packet exchange, which is pretty resource constrained even in embedded context. For example on lower coding rates, even 19 bytes take around 1.3 seconds to transmit (with a total of ~512 bytes per hour due to duty-cycle regulations).

Edit: Currently I'm calculating with 2 bytes implicit nonce + 8 bytes authentication tag => 10 bytes cryptographic overhead. If there's a good reason, I could probably shave things to allow up to 16 additional bytes (total 26 bytes cryptographic overhead), but more would be a pain. The maximum allowed message size (absolute maximum, including payload and really everything) is 64 bytes, more is not possible.

This means that things like TLS or SSH are completely out, and even Noise has a pretty high overhead given that we don't need forward secrecy or periodic rekeying.

Things I have thought of

My first approach would be to simply use AES128-CCM8 with a semi-implicit1 64 bit counter nonce due to hardware acceleration, and the comparatively small tag size.

The use of AES-CCM should provide privacy and reasonable authentication for each message. The use of a 64 bit counter should protect against replay attacks, while still giving enough room to allocate a larger number of counters to avoid wear-leveling of flash storage2.

As far as I know, most protocols do something very similar once the handshake phase is over, but if there is an RFC or paper that formalizes this a bit, I'd prefer that over a self-designed solution.


1 Semi-implicit means that I transmit the least-significant two bytes of the nonce to allow the receiver to re-sync in case of a few lost packets. However, I still use the full 64 bit counter to generate the nonce.

2 The standard idea is to "allocate" a bunch of counters so you don't need to write to flash on every increment. Example: You start at 0. To allocate the next 1024 counters, we write 1024 to flash. In normal operation, we increment the counter in memory until we hit 1024, then we allocate the next bunch and so on. But in the unlikely event of power loss, we recover at 1024, meaning we loose some of the possible counters, but can be sure we don't have catastrophic nonce reuse.

K. Biermann
  • 586
  • 7
  • 16

0 Answers0