Computing stuff tied to the physical world

Secure transmissions

In Software on Feb 23, 2010 at 00:01

For some time, I’ve been thinking about adding optional encryption to the RF12 wireless driver. I’ll leave it to the cryptography experts to give hard guarantees about the resulting security of such a system…

The basic goal is to provide a mechanism which lets me get messages across with a high level of confidence that an outsider cannot successfully do the same.

The main weakness which most home automation systems such as FS20 and KAKU completely fail to address is message replay. The “house code” used by FS20 has 16 bits and the address has 8 bits, with the naive conclusion being that it takes millions of attempts to send out messages to which my devices respond. Unfortunately, that’s a huge fallacy: all you have to do is sit outside the house for a while, eaves-dropping on the radio packets, and once you’ve got them, you’ve figured out the house code(s) and adresses in use…

I don’t care too much about people picking up signals which turn the lights on or close the curtains. You don’t need technology to see those events from outside the house anyway. I do care about controlling more important things, such as a server or a door opener.

Here are my design choices for optional encryption in the RF12 driver:

  • The cipher used is David Wheeler’s XXTEA, which takes under 2 Kb of code.
  • The keys are 128 bits, they have to be stored in EEPROM on all nodes involved.
  • All nodes in the same net group will use either no encryption or a shared encryption key.
  • A sequence number of 6, 14, 22, or 30 bits is added to each packet.

To start with the latter: XXTEA requires padding of packets to a multiple of 4 bytes. What I’ve done is add the sequence number at the end, using as many bytes as needed to pad to the proper length, with 2 bits to indicate the sequence number size. Encrypted packets must be 4..62 bytes long. It’s up to the sender to decide what size packets to send out, and implicitly how many bits of the sequence number to include. Each new transmission bumps the sequence number.

To enable encryption, call the new rf12_encrypt() function with a pointer to the 16-byte key (in EEPROM):

Screen shot 2010-02-21 at 18.37.36.png

Encryption will then be transparently applied to both sending and receiving sides. This mechanism also works in combination with the easy transmission functions. To disable encryption, pass a null pointer instead.

The received sequence number is available as a new “rf12_seq” global variable. It is up to the receiver (or in the case of acks: the originator) to ascertain that the sequence number is in the proper range. Bogus transmissions will decrypt to an inappropriate sequence number. To make absolutely certain that the packet is from a trusted source, include some known / fixed bytes – these will only be correct if the proper encryption key was used.

This new functionality has been implemented in such a way that the extra code is only included in your sketch if you actually have a call to rf12_encrypt(). Without it, the RF12 driver still adds less than 3 Kb overhead.

I’ve added two sample sketches called “crypSend” and “crypRecv” to the RF12 library. The test code sends packets with 4..14 bytes of data, containing “ABC 0123456789” (truncated to the proper length). The receiving end alternates between receiving in encrypted mode for 10 packets, then plaintext for another 10, etc:

Screen shot 2010-02-21 at 22.42.27.png

As expected, the encrypted packets look like gibberish and are always padded to multiples of 4 bytes. Note also that the received sequence number is only 6 bits on every 4th packet, when the packet size allows for only one byte padding. The strongest protection against replay attacks will be obtained by sending packets which are precisely a multiple of 4 bytes (with a 30-bit sequence number included in the 4 bytes added for padding).

So this should provide a fair amount of protection for scenarios that need it. Onwards!

  1. Just an idea: if the cipher does not have to be really strong you could try to base the encryption on a pseudorandom number generator with a known seed. I have no idea how good the pseudorandom algorithm on Arduino is but it may be worth checking since it could cost less memory. Maybe close to 0 if the psudorandom number generator (math?) library is included already.

    P.S. Thanks for sharing this project but much bigger and never ending thanks for tclkit and stakit ideas! :-)

    • The memory cost right now is around 1800 bytes of code and 22 bytes of RAM. I haven’t measured the runtime penalty, but for line-powered low-rate devices as found in the house, it may not be a big deal. With a stream of values, such as a pseudo-random generator, you have to be careful since the comms channel can lose packets. It’d be interesting to compare it though – perhaps this could be tried at the level above the RF12 driver.

      P.S. Thank you – hey, there’s someone else using Tcl and Tclkits and Starkits! :)

  2. Yes, lost packets have to dealt with but since you introduced sequence numbers anyway it is probably part of the solution here as well. Just from the top of my head: using sequence numbers as an offset in the pseudorandom stream or as a part of the seed could be a start.

    P.S. Of course there is, I’m a huge fan and I know at least two more people :)

    • I must admit that my knowledge of encryption techniques is quite limited. I’ll gladly defer to someone else to design a better/simpler solution. With the seqnum used as you say, does that mean the seqnum has to be sent in plaintext? If so, would that affect the strength of the system?

      P.S. Heh, me too. Let’s throw a party! ;)

  3. I’m not a proper expert either but: 1) I think sending seqnum in the open would weaken the cipher. At least in theory because we disclose (leak) more information. At the same time, we are not trying to design a strong encryption so the change is not necessarily significant. 2) What if the sequence number is also encrypted? If we loose a packet we will do decryption and get rubbish (I assume here that the seqnum was used to generate the seed). If we get rubbish we know that we had lost one or more packets, so we can ask to retransmit everything since the last correct one (that is probably not different from the retransmission mechanism without encryption). The last question is: how do we know that what we decrypted is rubbish? I would add a known pattern to check that—the pattern could very well be a message name you expect + seqnum or/and a few additional bytes/characters. The chance of getting correct pattern from a wrong packet is minimal.

  4. Concerning replay, FWIW: What I learned from the SNMPv3 world is that before secure transmission there is a discovery to get to know the current time at the receiver side. If you want to send to that side, you need to add the correct current time of the receiver to your encrypted content. The receiver will reject all timestamps off time (with window) and thus avoid replay. Downside is that you need to discover and maintain per receiver you want to send to a time offset to your local clock.

    PS: tclkit – all the time!

    • Sounds like an excellent way to get strong security. Does require nodes to track time, and to continue increasing clock time across reboots and down-time. Encryption is clearly just one part of the solution. I hope to reach a decent solution, by taking one step at a time…

Comments are closed.