Optimized RF24Network Layer v2.3.0
2024 - Optimized RF24 Network Layer for NRF24L01 & NRF52x radios
Loading...
Searching...
No Matches
RF24CCM.h
Go to the documentation of this file.
1
20#ifndef __RF24CCM_H__
21#define __RF24CCM_H__
22
23#include <stdint.h>
24#include <stddef.h>
25#include <string.h>
26#include <sys/random.h>
27#if !defined(USE_RF24_LIB_SRC)
28 #include <RF24/RF24.h>
29#else
30 #include <RF24.h>
31#endif
32
33#define RF24CCM_KEY_SIZE 16
34#define RF24CCM_IV_SIZE 5 // must match CCM_IV_SIZE in nrf_to_nrf.h
35#define RF24CCM_COUNTER_SIZE 3 // must match CCM_COUNTER_SIZE in nrf_to_nrf.h
36#define RF24CCM_MIC_SIZE 4 // must match CCM_MIC_SIZE in nrf_to_nrf.h
37#define RF24CCM_NONCE_SIZE 13
38#define RF24CCM_OVERHEAD (RF24CCM_IV_SIZE + RF24CCM_COUNTER_SIZE + RF24CCM_MIC_SIZE)
39#define RF24CCM_FRAME_SIZE 32 // max dynamic payload size of the nRF24L01
40
41namespace rf24ccm {
42
43inline uint8_t xtime(uint8_t x)
44{
45 return (uint8_t)((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00));
46}
47
48inline uint8_t rotl8(uint8_t x, uint8_t shift)
49{
50 return (uint8_t)((x << shift) | (x >> (8 - shift)));
51}
52
53// The AES S-box, generated once instead of typed out as a table
54inline const uint8_t* sbox()
55{
56 static uint8_t box[256];
57 static bool ready = false;
58 if (!ready) {
59 uint8_t p = 1, q = 1;
60 do {
61 p = (uint8_t)(p ^ (p << 1) ^ ((p & 0x80) ? 0x1B : 0x00)); // multiply p by 3
62 q ^= (uint8_t)(q << 1); // divide q by 3
63 q ^= (uint8_t)(q << 2);
64 q ^= (uint8_t)(q << 4);
65 if (q & 0x80) {
66 q ^= 0x09;
67 }
68 box[p] = (uint8_t)(q ^ rotl8(q, 1) ^ rotl8(q, 2) ^ rotl8(q, 3) ^ rotl8(q, 4) ^ 0x63);
69 } while (p != 1);
70 box[0] = 0x63;
71 ready = true;
72 }
73 return box;
74}
75
76inline void aesExpandKey(const uint8_t key[16], uint8_t roundKeys[176])
77{
78 const uint8_t* sb = sbox();
79 uint8_t rcon = 0x01;
80 memcpy(roundKeys, key, 16);
81 for (uint8_t i = 16; i < 176; i += 4) {
82 uint8_t t[4] = {roundKeys[i - 4], roundKeys[i - 3], roundKeys[i - 2], roundKeys[i - 1]};
83 if (i % 16 == 0) {
84 uint8_t first = t[0];
85 t[0] = (uint8_t)(sb[t[1]] ^ rcon);
86 t[1] = sb[t[2]];
87 t[2] = sb[t[3]];
88 t[3] = sb[first];
89 rcon = xtime(rcon);
90 }
91 for (uint8_t j = 0; j < 4; j++) {
92 roundKeys[i + j] = (uint8_t)(roundKeys[i - 16 + j] ^ t[j]);
93 }
94 }
95}
96
97inline void aesEncryptBlock(const uint8_t roundKeys[176], const uint8_t in[16], uint8_t out[16])
98{
99 const uint8_t* sb = sbox();
100 uint8_t s[16];
101 for (uint8_t i = 0; i < 16; i++) {
102 s[i] = (uint8_t)(in[i] ^ roundKeys[i]);
103 }
104 for (uint8_t round = 1; round <= 10; round++) {
105 for (uint8_t i = 0; i < 16; i++) {
106 s[i] = sb[s[i]];
107 }
108 // ShiftRows: row r is rotated left by r bytes
109 uint8_t t = s[1];
110 s[1] = s[5];
111 s[5] = s[9];
112 s[9] = s[13];
113 s[13] = t;
114 t = s[2];
115 s[2] = s[10];
116 s[10] = t;
117 t = s[6];
118 s[6] = s[14];
119 s[14] = t;
120 t = s[15];
121 s[15] = s[11];
122 s[11] = s[7];
123 s[7] = s[3];
124 s[3] = t;
125 if (round < 10) {
126 for (uint8_t c = 0; c < 16; c += 4) {
127 uint8_t a0 = s[c], a1 = s[c + 1], a2 = s[c + 2], a3 = s[c + 3];
128 uint8_t all = (uint8_t)(a0 ^ a1 ^ a2 ^ a3);
129 s[c] = (uint8_t)(a0 ^ all ^ xtime((uint8_t)(a0 ^ a1)));
130 s[c + 1] = (uint8_t)(a1 ^ all ^ xtime((uint8_t)(a1 ^ a2)));
131 s[c + 2] = (uint8_t)(a2 ^ all ^ xtime((uint8_t)(a2 ^ a3)));
132 s[c + 3] = (uint8_t)(a3 ^ all ^ xtime((uint8_t)(a3 ^ a0)));
133 }
134 }
135 for (uint8_t i = 0; i < 16; i++) {
136 s[i] ^= roundKeys[round * 16 + i];
137 }
138 }
139 memcpy(out, s, 16);
140}
141
142// CBC-MAC part of CCM (RFC 3610 / NIST SP 800-38C). nonceLen is 7..13, the AAD must be < 0xFF00 bytes
143inline void ccmMac(const uint8_t roundKeys[176], const uint8_t* nonce, uint8_t nonceLen, const uint8_t* aad, size_t aadLen,
144 const uint8_t* msg, size_t msgLen, uint8_t micLen, uint8_t mac[16])
145{
146 const uint8_t L = (uint8_t)(15 - nonceLen);
147 uint8_t block[16] = {0};
148 block[0] = (uint8_t)((aadLen ? 0x40 : 0x00) | (((micLen - 2) / 2) << 3) | (L - 1));
149 memcpy(&block[1], nonce, nonceLen);
150 for (uint8_t i = 0; i < L; i++) {
151 block[15 - i] = (uint8_t)(i < sizeof(size_t) ? (msgLen >> (8 * i)) : 0);
152 }
153 aesEncryptBlock(roundKeys, block, mac);
154
155 if (aadLen) {
156 memset(block, 0, sizeof(block));
157 block[0] = (uint8_t)(aadLen >> 8);
158 block[1] = (uint8_t)aadLen;
159 uint8_t pos = 2;
160 for (size_t i = 0; i < aadLen; i++) {
161 block[pos++] = aad[i];
162 if (pos == 16) {
163 for (uint8_t j = 0; j < 16; j++) {
164 mac[j] ^= block[j];
165 }
166 aesEncryptBlock(roundKeys, mac, mac);
167 memset(block, 0, sizeof(block));
168 pos = 0;
169 }
170 }
171 if (pos) {
172 for (uint8_t j = 0; j < 16; j++) {
173 mac[j] ^= block[j];
174 }
175 aesEncryptBlock(roundKeys, mac, mac);
176 }
177 }
178
179 for (size_t i = 0; i < msgLen; i += 16) {
180 size_t n = (msgLen - i < 16) ? msgLen - i : 16;
181 for (size_t j = 0; j < n; j++) {
182 mac[j] ^= msg[i + j];
183 }
184 aesEncryptBlock(roundKeys, mac, mac);
185 }
186}
187
188// CTR part of CCM: XORs `len` bytes with key stream blocks S_1, S_2, ... and returns S_0 in s0
189inline void ccmCtr(const uint8_t roundKeys[176], const uint8_t* nonce, uint8_t nonceLen, const uint8_t* in, size_t len,
190 uint8_t* out, uint8_t s0[16])
191{
192 const uint8_t L = (uint8_t)(15 - nonceLen);
193 uint8_t ctr[16] = {0};
194 uint8_t stream[16];
195 ctr[0] = (uint8_t)(L - 1);
196 memcpy(&ctr[1], nonce, nonceLen);
197 aesEncryptBlock(roundKeys, ctr, s0);
198
199 uint32_t counter = 1;
200 for (size_t i = 0; i < len; i += 16, counter++) {
201 for (uint8_t j = 0; j < L; j++) {
202 ctr[15 - j] = (uint8_t)(j < 4 ? (counter >> (8 * j)) : 0);
203 }
204 aesEncryptBlock(roundKeys, ctr, stream);
205 size_t n = (len - i < 16) ? len - i : 16;
206 for (size_t j = 0; j < n; j++) {
207 out[i + j] = (uint8_t)(in[i + j] ^ stream[j]);
208 }
209 }
210}
211
213inline void ccmEncrypt(const uint8_t roundKeys[176], const uint8_t* nonce, uint8_t nonceLen, const uint8_t* aad, size_t aadLen,
214 const uint8_t* in, size_t len, uint8_t* out, uint8_t* mic, uint8_t micLen)
215{
216 uint8_t mac[16], s0[16];
217 ccmMac(roundKeys, nonce, nonceLen, aad, aadLen, in, len, micLen, mac);
218 ccmCtr(roundKeys, nonce, nonceLen, in, len, out, s0);
219 for (uint8_t i = 0; i < micLen; i++) {
220 mic[i] = (uint8_t)(mac[i] ^ s0[i]);
221 }
222}
223
225inline bool ccmDecrypt(const uint8_t roundKeys[176], const uint8_t* nonce, uint8_t nonceLen, const uint8_t* aad, size_t aadLen,
226 const uint8_t* in, size_t len, const uint8_t* mic, uint8_t micLen, uint8_t* out)
227{
228 uint8_t mac[16], s0[16];
229 ccmCtr(roundKeys, nonce, nonceLen, in, len, out, s0);
230 ccmMac(roundKeys, nonce, nonceLen, aad, aadLen, out, len, micLen, mac);
231 uint8_t diff = 0;
232 for (uint8_t i = 0; i < micLen; i++) {
233 diff |= (uint8_t)(mac[i] ^ s0[i] ^ mic[i]);
234 }
235 if (diff) {
236 memset(out, 0, len);
237 return false;
238 }
239 return true;
240}
241
242} // namespace rf24ccm
243
254class RF24CCM : public RF24
255{
256public:
257 using RF24::RF24;
258
260 bool enableEncryption = false;
261
263 uint32_t decryptFailures = 0;
264
266 void setKey(const uint8_t key[RF24CCM_KEY_SIZE])
267 {
268 rf24ccm::aesExpandKey(key, roundKeys);
269 keySet = true;
270 }
271
273 void setCounter(uint32_t counter)
274 {
275 packetCounter = counter;
276 }
277
279 void setRetries(uint8_t delay, uint8_t count)
280 {
281 if (enableEncryption) {
282 delay = delay > minRetryDelay ? delay : minRetryDelay;
283 count = count > minRetryCount ? count : minRetryCount;
284 }
285 RF24::setRetries(delay, count);
286 }
287
289 {
290 if (!enableEncryption) {
291 return RF24::getDynamicPayloadSize();
292 }
293 if (rxPending) {
294 return rxLength;
295 }
296 uint8_t size = RF24::getDynamicPayloadSize(); // 0 means RF24 flushed a corrupt payload
297 if (!size) {
298 return 0;
299 }
300 uint8_t frame[RF24CCM_FRAME_SIZE];
301 RF24::read(frame, size);
302 if (!decryptFrame(frame, size)) {
304 return 0;
305 }
306 rxPending = true;
307 return rxLength;
308 }
309
310 void read(void* buf, uint8_t len)
311 {
312 if (!enableEncryption) {
313 RF24::read(buf, len);
314 return;
315 }
316 if (!rxPending && !getDynamicPayloadSize()) {
317 memset(buf, 0, len);
318 return;
319 }
320 memcpy(buf, rxPlain, len < rxLength ? len : rxLength);
321 rxPending = false;
322 }
323
324 bool write(const void* buf, uint8_t len)
325 {
326 return write(buf, len, false);
327 }
328
329 bool write(const void* buf, uint8_t len, const bool multicast)
330 {
331 if (!enableEncryption) {
332 return RF24::write(buf, len, multicast);
333 }
334 uint8_t frame[RF24CCM_FRAME_SIZE];
335 if (!encryptFrame(buf, len, frame)) {
336 return false;
337 }
338 return RF24::write(frame, (uint8_t)(len + RF24CCM_OVERHEAD), multicast);
339 }
340
341 bool writeFast(const void* buf, uint8_t len)
342 {
343 return writeFast(buf, len, false);
344 }
345
346 bool writeFast(const void* buf, uint8_t len, const bool multicast)
347 {
348 if (!enableEncryption) {
349 return RF24::writeFast(buf, len, multicast);
350 }
351 uint8_t frame[RF24CCM_FRAME_SIZE];
352 if (!encryptFrame(buf, len, frame)) {
353 return false;
354 }
355 return RF24::writeFast(frame, (uint8_t)(len + RF24CCM_OVERHEAD), multicast);
356 }
357
358private:
359 uint8_t roundKeys[176] = {0};
360 bool keySet = false;
361 uint32_t packetCounter = randomCounter();
362 uint8_t rxPlain[RF24CCM_FRAME_SIZE] = {0};
363 uint8_t rxLength = 0;
364 bool rxPending = false;
365
366 static uint32_t randomCounter()
367 {
368 uint32_t value = 0;
369 if (getrandom(&value, sizeof(value), 0) != sizeof(value)) {
370 value = 0;
371 }
372 return value & 0xFFFFFF;
373 }
374
375 static void buildNonce(const uint8_t* iv, const uint8_t* counter, uint8_t nonce[RF24CCM_NONCE_SIZE])
376 {
377 // Bytes 0-4: 39 bit packet counter + direction bit (always 0 in nrf_to_nrf), bytes 5-12: IV
378 memset(nonce, 0, RF24CCM_NONCE_SIZE);
379 memcpy(nonce, counter, RF24CCM_COUNTER_SIZE);
380 memcpy(&nonce[5], iv, RF24CCM_IV_SIZE);
381 }
382
383 bool encryptFrame(const void* buf, uint8_t len, uint8_t frame[RF24CCM_FRAME_SIZE])
384 {
385 if (!keySet || !len || len > RF24CCM_FRAME_SIZE - RF24CCM_OVERHEAD) {
386 return false;
387 }
388 // Never reuse a nonce: refuse to send if the random IV can't be generated
389 if (getrandom(frame, RF24CCM_IV_SIZE, 0) != RF24CCM_IV_SIZE) {
390 return false;
391 }
392 uint32_t counter = packetCounter;
393 packetCounter = (packetCounter + 1) & 0xFFFFFF;
394 uint8_t* counterBytes = &frame[RF24CCM_IV_SIZE];
395 for (uint8_t i = 0; i < RF24CCM_COUNTER_SIZE; i++) {
396 counterBytes[i] = (uint8_t)(counter >> (8 * i));
397 }
398
399 uint8_t nonce[RF24CCM_NONCE_SIZE];
400 buildNonce(frame, counterBytes, nonce);
401 const uint8_t aad = 0x00;
402 uint8_t* cipher = &frame[RF24CCM_IV_SIZE + RF24CCM_COUNTER_SIZE];
403 rf24ccm::ccmEncrypt(roundKeys, nonce, RF24CCM_NONCE_SIZE, &aad, 1, static_cast<const uint8_t*>(buf), len, cipher,
404 &cipher[len], RF24CCM_MIC_SIZE);
405 return true;
406 }
407
408 bool decryptFrame(const uint8_t* frame, uint8_t size)
409 {
410 if (!keySet || size <= RF24CCM_OVERHEAD || size > RF24CCM_FRAME_SIZE) {
411 return false;
412 }
413 const uint8_t* iv = frame;
414 const uint8_t* counter = &frame[RF24CCM_IV_SIZE];
415 const uint8_t* cipher = &frame[RF24CCM_IV_SIZE + RF24CCM_COUNTER_SIZE];
416 uint8_t cipherLength = (uint8_t)(size - RF24CCM_OVERHEAD);
417
418 uint8_t nonce[RF24CCM_NONCE_SIZE];
419 buildNonce(iv, counter, nonce);
420 const uint8_t aad = 0x00;
421 if (!rf24ccm::ccmDecrypt(roundKeys, nonce, RF24CCM_NONCE_SIZE, &aad, 1, cipher, cipherLength, &cipher[cipherLength],
422 RF24CCM_MIC_SIZE, rxPlain)) {
423 return false;
424 }
425 rxLength = cipherLength;
426 return true;
427 }
428
429 // New data members go below this line, so existing members keep their offsets and a program or
430 // library built against an older RF24CCM.h still finds the key and buffers where it expects them.
438 uint8_t minRetryDelay = 15;
439 uint8_t minRetryCount = 15;
440};
441
442#endif // __RF24CCM_H__
#define RF24CCM_NONCE_SIZE
Definition RF24CCM.h:37
#define RF24CCM_MIC_SIZE
Definition RF24CCM.h:36
#define RF24CCM_COUNTER_SIZE
Definition RF24CCM.h:35
#define RF24CCM_OVERHEAD
Definition RF24CCM.h:38
#define RF24CCM_KEY_SIZE
Definition RF24CCM.h:33
#define RF24CCM_FRAME_SIZE
Definition RF24CCM.h:39
#define RF24CCM_IV_SIZE
Definition RF24CCM.h:34
Definition RF24CCM.h:255
void setKey(const uint8_t key[RF24CCM_KEY_SIZE])
Definition RF24CCM.h:266
uint32_t decryptFailures
Definition RF24CCM.h:263
bool write(const void *buf, uint8_t len)
Definition RF24CCM.h:324
void setCounter(uint32_t counter)
Definition RF24CCM.h:273
void read(void *buf, uint8_t len)
Definition RF24CCM.h:310
bool writeFast(const void *buf, uint8_t len, const bool multicast)
Definition RF24CCM.h:346
uint8_t getDynamicPayloadSize(void)
Definition RF24CCM.h:288
bool writeFast(const void *buf, uint8_t len)
Definition RF24CCM.h:341
bool write(const void *buf, uint8_t len, const bool multicast)
Definition RF24CCM.h:329
void setRetries(uint8_t delay, uint8_t count)
Definition RF24CCM.h:279
bool enableEncryption
Definition RF24CCM.h:260
Definition RF24CCM.h:41
uint8_t rotl8(uint8_t x, uint8_t shift)
Definition RF24CCM.h:48
void aesEncryptBlock(const uint8_t roundKeys[176], const uint8_t in[16], uint8_t out[16])
Definition RF24CCM.h:97
void ccmCtr(const uint8_t roundKeys[176], const uint8_t *nonce, uint8_t nonceLen, const uint8_t *in, size_t len, uint8_t *out, uint8_t s0[16])
Definition RF24CCM.h:189
const uint8_t * sbox()
Definition RF24CCM.h:54
uint8_t xtime(uint8_t x)
Definition RF24CCM.h:43
void ccmEncrypt(const uint8_t roundKeys[176], const uint8_t *nonce, uint8_t nonceLen, const uint8_t *aad, size_t aadLen, const uint8_t *in, size_t len, uint8_t *out, uint8_t *mic, uint8_t micLen)
Definition RF24CCM.h:213
bool ccmDecrypt(const uint8_t roundKeys[176], const uint8_t *nonce, uint8_t nonceLen, const uint8_t *aad, size_t aadLen, const uint8_t *in, size_t len, const uint8_t *mic, uint8_t micLen, uint8_t *out)
Definition RF24CCM.h:225
void aesExpandKey(const uint8_t key[16], uint8_t roundKeys[176])
Definition RF24CCM.h:76
void ccmMac(const uint8_t roundKeys[176], const uint8_t *nonce, uint8_t nonceLen, const uint8_t *aad, size_t aadLen, const uint8_t *msg, size_t msgLen, uint8_t micLen, uint8_t mac[16])
Definition RF24CCM.h:143