I did a bunch of experiments with different AES implementations today. There seems to be a lot of opportunity to save on flash space by switching to a different AES implementation. I tried these:
This is the code I nicked from the things4u git repository linked above. The files say it is code from Ideetron. The things4u repository used the high-level functions, which take care of CTR and CMAC, but also take care of building the initialization vectors (Block A and Block B in the LoRaWAN spec). The latter was superfluous, since lmic.c already built these blocks. Also, since building these blocks needs access to the various internal details (devaddress, keys, frame counter), the code in the things4u repo was a bit convoluted (bypassing existing LMIC datastructures and functions).
I ended up only using Ideetron’s low-level AES cipher, and wrote the higher level CTR and CMAC code from scratch, to better fit with the LMIC structure (and it ended up a bit more effcient too).
This code uses a problematic GPL license (see below).
This is a AES128 implementation taken from https://github.com/kokke/tiny-AES128-C. This is just an AES cipher, so it uses the same CTR and CMAC code as above. This code was modified to use PROGMEM to reduce the memory usage. The structure of this code looks very similar to the Ideetron code, so I suspect they share a common ancestor (perhaps some pseudocode in the AES specification?).
This library is stated to be in the public domain, but I’m not sure if the casual mention in the README suffices as a proper public domain dedication.
Avr-crypto-lib / AESLib
AESLib is an Arduino library that includes a few selected parts of the more complete AVR-Crypto-lib and can be found here: https://github.com/DavyLandman/AESLib It uses heavily optimized handcoded assembly code. The AESLib version only includes assembly versions, so it only works on AVR (the original AVR-crypto-lib also has more portable C versions). Again, this is just an AES cipher, so this uses my CTR and CMAC code again.
This code uses a problematic GPL license (see below).
This is a AES library intended to be small, licensed under an MIT license. It can be found here:
It allows using a normal sbox lookup table, or a “small” version that calculates things instead of using a lookup table. This is slower, but should save a 256-byte lookup table. In practice, the flash size gain is only 64 bytes on an Atmega328p, presumably because the calculations use a lot of bitshifting, which AVR isn’t really good at (so it needs a lot of instructions to implement the calculations).
Furthermore, this library allows doing “on the fly” key schedule calculation, or precalculating the key schedule. Both have been tested. With precalculation, the key schedule calculation was redone on every AES request, even when the key didn’t change, to make the implementation simpler. The time used for this precalculation was not included in the table, since it is expected that, in a final implementation, it would only happen rarely.
This is a library specifically meant for the AVR architecture, using (presumably) hand-coded assembly. It comes in a five different flavours, all of which were tested. There are some additional toggles, which have just been left at the defaults.
The FAST and FURIOUS versions use precalculated keyschedules, and the same remarks and caveats about this as with aes-min apply.
Again, this library is GPL-licensed, making it incompatible.
Of course I also looked at the original AES implementation in LMIC. This implementation has the AES cipher and CTR and CMAC implementations heavily integrated. I modified the code to use PROGMEM for reduced memory usage, and applied some fixes to make it work on 8/16-bit hardware too.
I also tried without any AES support, to have a baseline for comparison.
Some of these libraries use the GPL license, which is unfortunately not compatible with the EPL license used by the LMIC library. I think that this means you can use them for personal use, but cannot distribute the result (so my github repository might actually be violating the license right now, I’m not entirely sure). This is a pity, since especially AESLib / AVR-Crypto-Lib is fast and small… Given the licenses, the only plausible alternative of the above is really Tiny-AES128-C, which is small, but also fairly slow.
One alternative I haven’t tried yet is https://github.com/spaniakos/AES (which looks like it’s RAM-hungry).
Here’s some benchmarks with the above libraries:
| License | Flash | RAM | AES (16 bytes) | CTR (16 bytes) | MIC (25 bytes) |
No AES | | 20944 | 1103 | | | |
LMIC | EPL | 30040 | 1103 | 744μs | 692μs | 2 356μs |
Ideetron | GPL | 22678 | 1119 | 1 544μs | 1 704μs | 6 652μs |
Tiny-AES128-C | PD | 22912 | 1283 | 1 368μs | 1 508μs | 5 940μs |
AESLib | GPL | 22748 | 1109 | 308μs | 356μs | 1 360μs |
aes-min | MIT | 22462 | 1103 | 1 292μs | 1 444μs | 5 664μs |
aes-min small | MIT | 22398 | 1113 | 23 304μs | 23 772μs | 27 948μs |
aes-min precalc | MIT | 22336 | 1279 | 1 060μs | 1 196μs | 4 736μs |
aes-min small + precalc| MIT | 22272 | 1289 | 18 684μs | 18 712μs | 9 260μs |
avr-aes SMALL | GPL | 22328 | 1104 | 664μs | 744μs | 2 916μs |
avr-aes FANTASTIC | GPL | 23214 | 1103 | 304μs | 344μs | 1 336μs |
avr-aes FURIOUS | GPL | 23232 | 1279 | 188μs | 232μs | 860μs |
avr-aes FAST | GPL | 23032 | 1279 | 184μs | 216μs | 832μs |
avr-aes MINI | GPL | 21950 | 1124 | 912μs | 1 020μs | 4 016μs |
The first two columns indicate flash and RAM sizes as indicated by the Arduino IDE. The AES column indicates the time used for a single block plain AES encryption. CTR is the time for encryption regular payload using CTR and MIC is a MIC calculation using CMAC. All times are in microseconds.
The code used for these tests can be found here: https://github.com/matthijskooijman/arduino-lmic/tree/aes-experiment (pull carefully, branch is prone to rebases / force pushes).