Felix Rusu from lowpowerlab.com has created an amazing library for the Arduino/ATMega platform to use cheap RFM69 wireless modules for wireless transmissions between nodes. For a list of all the features, see his readme: https://github.com/LowPowerLab/RFM69. His Moteino platform and library are perfect for roll-your-own home automation systems, which is just what I am doing!
However, in my mind the library was lacking one critical piece – being able to prevent wireless replay attacks. Using Felix’s library, RFM69 transmissions can be encrypted in hardware, but the receiving node had no way of verifying that the incoming transmission was from a legitimate node and should be processed. If an attacker captured a sensitive packet as it flew through the air, such as one to open your garage door, they could replay it at a time of their choosing and trick your home automation system into opening the door.
TomWS on the lowpowerlab forum and I did some brain storming and he proposed modifications to the base RFM69 library to virtualize some functions, allowing them to be overloaded to create ‘extensions’. Using his modification, I wrote a transmission session key extension.
More after the break…
The exchange between wireless nodes looks like this
- The Node makes a connection request by setting one of the bits of the control byte built into the RFM69 base library. The control bit indicates a session is requested.
- The Gateway receives the incoming packet and using the control bit flag, recognizes the packet as a session request. It then generates a random 1 byte session key and stores it. It sets another control bit indicating a key is included in the packet, adds the session key as the first byte of DATA, then sends that all back to the node.
- The Node receives the incoming packet, parses the control bits and session key, then prepares the data it wants to send. It adds the session key as the first byte of DATA to be sent back, then appends whatever other data needs to be sent after the key. The control bit indicating a session key is included is set. All of that is sent back to the Gateway.
- The Gateway receives the response and sees the control bit that says a session key is included. It checks to make sure the first byte of DATA where the incoming session key is stored matches the expected key, then processes the rest of the payload if it does. If not, DATALEN is set to 0, indicating to the sketch that there is no data available that should be processed.
Note: when using a session key, the amount of user accessible data that can be sent between nodes is decreased by 1 byte. This byte is used by the session key.
Of course, this is all useless if encryption isn’t enabled 🙂
Configurations that work, configurations that don’t
One of my goals with this extension was to allow session keys to be used for sensitive transactions but also let dumb sensor nodes that only send environment data still ‘fire and forget’ data without a key. Sensor data is likely not critical and therefore does not warrant the overhead of a session key.
Here is a description of each possible combination of gateway and nodes having session keys enabled.
- Both gateway and node with session key disabled – transmissions occur as they would if using the base RFM69 library only.
- Both gateway and node with session key enabled – the existing ‘send’ and ‘sendWithRetry’ methods will automatically use session keys protection.
- Gateway enabled, node disabled – works and allows “fire and forget” messages from nodes that don’t require session keys. However, the gateway cannot initiate a new connection to send data to the node because the gateway will try to initiate a new session, but the node will not be enabled, so it will never handshake a key. This configuration should only be used with nodes that simply send data to the gateway and never need to receive incoming data from the gateway.
- Gateway disabled, node enabled – does not work, as expected, because the node can’t handshake a key with the gateway.
How to use it
I designed this extension so few changes to the main sketch are needed. It’s as simple as adding my extension library with an ‘include’ statement and adding
as part of the sketch setup. Any ‘send’ or ‘sendWithRetry’ commands that already exist within your code will now use session keys without any other modifications.
CAUTION: I do not use wireless node programming as is supported by the original RFM69 library, so I did not test if wireless programming still works with session key support enabled.
Step by step:
The below assumes you already have a working gateway/node setup using Felix’s base RFM69 library.
- Download TomWS’s ‘virtualized’ branch of the RFM69 library that allows overriding functions. Put it in your Arduino Sketchbook Libraries directory. If you already have Felix’s base library, you should remove that and this should replace it. Available from here: https://github.com/dewoodruff/RFM69/tree/virtualized
- Download my RMF69_SessionKey extension from here: https://github.com/dewoodruff/RFM69_SessionKey. Also place this in the Sketchbook Libraries directory.
- Edit your existing sketches, both nodes and gateway, as follows.
- Right before the existing #include <RFM69.h> statement, add this:
#include <RFM69_SessionKey.h> // enable session key support
- Where you previously had something like:
- Lastly, in your setup function, simply add:
Session keys are now seamlessly enabled for the rest of the sketch – no need to change anything else.
- Provides reasonable protection against replay attacks. Helpful for sensitive transactions such as opening a lock or garage door.
- The gateway only maintains a session key for one node at a time. If one node starts a session with a gateway, then a second node starts a new session before the gateway gets data from the first node, the second node’s session will override the first. This would probably only be a problem on a very busy network. Using sendWithRetry will help mitigate this risk.
- Less efficient than simple ‘fire and forget’ transmissions which can be desirable for battery powered nodes. Instead of 1-2 total packets per session (depending on if an ACK is requested), you’re now looking at 3-4 total, two of which come from the battery powered node. This can be avoided for simple sensor nodes that only gather data rather than control anything important by just not enabling session key support on that node.
- It’s not perfect. While low risk, an attacker could theoretically still continuously try to ‘guess’ the session key by sending a new connection request, then replaying the stored packet, hoping that the session key in the stored packet ends up matching what was just generated by the gateway.
Felix’s library is great, and this extension adds just a little bit extra security to sensitive transmissions. You should weigh if this security is needed in your environment, as there are some cons to this setup. It’s pretty unlikely that someone who wants to break into your house will try to hack your home automation system – it’s much easier to just throw a rock through the window. But you never know! 😉