ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
SpeckLowMemory.cpp
1 /*
2  * Copyright (C) 2015 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "SpeckLowMemory.h"
24 #include "Crypto.h"
25 #include "utility/RotateUtil.h"
26 #include "utility/EndianUtil.h"
27 #include <string.h>
28 
57 // The "avr-gcc" compiler doesn't do a very good job of compiling
58 // code involving 64-bit values. So we have to use inline assembly.
59 // It also helps to break the state up into 32-bit quantities
60 // because "asm" supports register names like %A0, %B0, %C0, %D0
61 // for the bytes in a 32-bit quantity, but it does not support
62 // %E0, %F0, %G0, %H0 for the high bytes of a 64-bit quantity.
63 #if defined(__AVR__)
64 #define USE_AVR_INLINE_ASM 1
65 #endif
66 
74  : rounds(32)
75 {
76 }
77 
78 SpeckLowMemory::~SpeckLowMemory()
79 {
80  clean(k);
81 }
82 
84 {
85  return 16;
86 }
87 
89 {
90  // Also supports 128-bit and 192-bit, but we only report 256-bit.
91  return 32;
92 }
93 
94 // Pack/unpack byte-aligned big-endian 64-bit quantities.
95 #define pack64(data, value) \
96  do { \
97  uint64_t v = htobe64((value)); \
98  memcpy((data), &v, sizeof(uint64_t)); \
99  } while (0)
100 #define unpack64(value, data) \
101  do { \
102  memcpy(&(value), (data), sizeof(uint64_t)); \
103  (value) = be64toh((value)); \
104  } while (0)
105 
106 bool SpeckLowMemory::setKey(const uint8_t *key, size_t len)
107 {
108 #if USE_AVR_INLINE_ASM
109  // Determine the number of rounds to use and validate the key length.
110  if (len == 32) {
111  rounds = 34;
112  } else if (len == 24) {
113  rounds = 33;
114  } else if (len == 16) {
115  rounds = 32;
116  } else {
117  return false;
118  }
119 
120  // Copy the bytes of the key into the "k" array in reverse order to
121  // convert big endian into little-endian.
122  __asm__ __volatile__ (
123  "1:\n"
124  "ld __tmp_reg__,-Z\n"
125  "st X+,__tmp_reg__\n"
126  "dec %2\n"
127  "brne 1b\n"
128  : : "x"(k), "z"(key + len), "r"(len)
129  );
130 #else
131  if (len == 32) {
132  rounds = 34;
133  unpack64(k[3], key);
134  unpack64(k[2], key + 8);
135  unpack64(k[1], key + 16);
136  unpack64(k[0], key + 24);
137  } else if (len == 24) {
138  rounds = 33;
139  unpack64(k[2], key);
140  unpack64(k[1], key + 8);
141  unpack64(k[0], key + 16);
142  } else if (len == 16) {
143  rounds = 32;
144  unpack64(k[1], key);
145  unpack64(k[0], key + 8);
146  } else {
147  return false;
148  }
149 #endif
150  return true;
151 }
152 
153 void SpeckLowMemory::encryptBlock(uint8_t *output, const uint8_t *input)
154 {
155 #if USE_AVR_INLINE_ASM
156  uint64_t l[4];
157  uint32_t xlow, xhigh, ylow, yhigh;
158  uint32_t slow, shigh;
159  uint8_t li_in = 0;
160  uint8_t li_out = (rounds - 31) * 8;
161 
162  // Copy the "k" array into "s" and the "l" array.
163  __asm__ __volatile__ (
164  "ldd r25,%4\n" // r25 = li_out
165 
166  "ld __tmp_reg__,Z+\n"
167  "std %A0,__tmp_reg__\n"
168  "ld __tmp_reg__,Z+\n"
169  "std %B0,__tmp_reg__\n"
170  "ld __tmp_reg__,Z+\n"
171  "std %C0,__tmp_reg__\n"
172  "ld __tmp_reg__,Z+\n"
173  "std %D0,__tmp_reg__\n"
174  "ld __tmp_reg__,Z+\n"
175  "std %A1,__tmp_reg__\n"
176  "ld __tmp_reg__,Z+\n"
177  "std %B1,__tmp_reg__\n"
178  "ld __tmp_reg__,Z+\n"
179  "std %C1,__tmp_reg__\n"
180  "ld __tmp_reg__,Z+\n"
181  "std %D1,__tmp_reg__\n"
182 
183  "1:\n" // l[0..] = k[1..]
184  "ld __tmp_reg__,Z+\n"
185  "st X+,__tmp_reg__\n"
186  "dec r25\n"
187  "brne 1b\n"
188  : "=Q"(slow), "=Q"(shigh)
189  : "z"(k), "x"(l), "Q"(li_out)
190  : "r25"
191  );
192 
193  // Unpack the input into the x and y variables, converting
194  // from big-endian into little-endian in the process.
195  __asm__ __volatile__ (
196  "ld %D1,Z\n"
197  "ldd %C1,Z+1\n"
198  "ldd %B1,Z+2\n"
199  "ldd %A1,Z+3\n"
200  "ldd %D0,Z+4\n"
201  "ldd %C0,Z+5\n"
202  "ldd %B0,Z+6\n"
203  "ldd %A0,Z+7\n"
204  "ldd %D3,Z+8\n"
205  "ldd %C3,Z+9\n"
206  "ldd %B3,Z+10\n"
207  "ldd %A3,Z+11\n"
208  "ldd %D2,Z+12\n"
209  "ldd %C2,Z+13\n"
210  "ldd %B2,Z+14\n"
211  "ldd %A2,Z+15\n"
212  : "=r"(xlow), "=r"(xhigh), "=r"(ylow), "=r"(yhigh)
213  : "z"(input)
214  );
215 
216  // Perform all encryption rounds while expanding the key schedule in-place.
217  __asm__ __volatile__ (
218  "mov r23,__zero_reg__\n" // i = 0
219  "1:\n"
220 
221  // Adjust x and y for this round using the key schedule word s.
222 
223  // x = (rightRotate8_64(x) + y) ^ s;
224  "mov __tmp_reg__,%A0\n" // x = rightRotate8_64(x)
225  "mov %A0,%B0\n"
226  "mov %B0,%C0\n"
227  "mov %C0,%D0\n"
228  "mov %D0,%A1\n"
229  "mov %A1,%B1\n"
230  "mov %B1,%C1\n"
231  "mov %C1,%D1\n"
232  "mov %D1,__tmp_reg__\n"
233 
234  "add %A0,%A2\n" // x += y
235  "adc %B0,%B2\n"
236  "adc %C0,%C2\n"
237  "adc %D0,%D2\n"
238  "adc %A1,%A3\n"
239  "adc %B1,%B3\n"
240  "adc %C1,%C3\n"
241  "adc %D1,%D3\n"
242 
243  "ldd __tmp_reg__,%A4\n" // x ^= s
244  "eor %A0,__tmp_reg__\n"
245  "ldd __tmp_reg__,%B4\n"
246  "eor %B0,__tmp_reg__\n"
247  "ldd __tmp_reg__,%C4\n"
248  "eor %C0,__tmp_reg__\n"
249  "ldd __tmp_reg__,%D4\n"
250  "eor %D0,__tmp_reg__\n"
251  "ldd __tmp_reg__,%A5\n"
252  "eor %A1,__tmp_reg__\n"
253  "ldd __tmp_reg__,%B5\n"
254  "eor %B1,__tmp_reg__\n"
255  "ldd __tmp_reg__,%C5\n"
256  "eor %C1,__tmp_reg__\n"
257  "ldd __tmp_reg__,%D5\n"
258  "eor %D1,__tmp_reg__\n"
259 
260  // y = leftRotate3_64(y) ^ x;
261  "lsl %A2\n" // y = leftRotate1_64(y)
262  "rol %B2\n"
263  "rol %C2\n"
264  "rol %D2\n"
265  "rol %A3\n"
266  "rol %B3\n"
267  "rol %C3\n"
268  "rol %D3\n"
269  "adc %A2,__zero_reg__\n"
270 
271  "lsl %A2\n" // y = leftRotate1_64(y)
272  "rol %B2\n"
273  "rol %C2\n"
274  "rol %D2\n"
275  "rol %A3\n"
276  "rol %B3\n"
277  "rol %C3\n"
278  "rol %D3\n"
279 
280  "adc %A2,__zero_reg__\n"
281  "lsl %A2\n" // y = leftRotate1_64(y)
282  "rol %B2\n"
283  "rol %C2\n"
284  "rol %D2\n"
285  "rol %A3\n"
286  "rol %B3\n"
287  "rol %C3\n"
288  "rol %D3\n"
289  "adc %A2,__zero_reg__\n"
290 
291  "eor %A2,%A0\n" // y ^= x
292  "eor %B2,%B0\n"
293  "eor %C2,%C0\n"
294  "eor %D2,%D0\n"
295  "eor %A3,%A1\n"
296  "eor %B3,%B1\n"
297  "eor %C3,%C1\n"
298  "eor %D3,%D1\n"
299 
300  // On the last round we don't need to compute s so we
301  // can exit early here if (i + 1) == rounds.
302  "mov __tmp_reg__,r23\n" // temp = i + 1
303  "inc __tmp_reg__\n"
304  "cp __tmp_reg__,%9\n" // if (temp == rounds) ...
305  "brne 2f\n"
306  "rjmp 3f\n"
307  "2:\n"
308 
309  // Save x and y on the stack so we can reuse registers for t and s.
310  "push %A0\n"
311  "push %B0\n"
312  "push %C0\n"
313  "push %D0\n"
314  "push %A1\n"
315  "push %B1\n"
316  "push %C1\n"
317  "push %D1\n"
318  "push %A2\n"
319  "push %B2\n"
320  "push %C2\n"
321  "push %D2\n"
322  "push %A3\n"
323  "push %B3\n"
324  "push %C3\n"
325  "push %D3\n"
326 
327  // Compute the key schedule word s for the next round.
328 
329  // l[li_out] = (s + rightRotate8_64(l[li_in])) ^ i;
330  "ldd r24,%6\n" // Z = &(l[li_in])
331  "add %A8,r24\n"
332  "adc %B8,__zero_reg__\n"
333 
334  "ld %D1,Z+\n" // t = rightRotate8_64(l[li_in])
335  "ld %A0,Z+\n"
336  "ld %B0,Z+\n"
337  "ld %C0,Z+\n"
338  "ld %D0,Z+\n"
339  "ld %A1,Z+\n"
340  "ld %B1,Z+\n"
341  "ld %C1,Z+\n"
342 
343  "ldd %A2,%A4\n" // load s
344  "ldd %B2,%B4\n"
345  "ldd %C2,%C4\n"
346  "ldd %D2,%D4\n"
347  "ldd %A3,%A5\n"
348  "ldd %B3,%B5\n"
349  "ldd %C3,%C5\n"
350  "ldd %D3,%D5\n"
351 
352  "add %A0,%A2\n" // t += s
353  "adc %B0,%B2\n"
354  "adc %C0,%C2\n"
355  "adc %D0,%D2\n"
356  "adc %A1,%A3\n"
357  "adc %B1,%B3\n"
358  "adc %C1,%C3\n"
359  "adc %D1,%D3\n"
360 
361  "eor %A0,r23\n" // t ^= i
362 
363  // Z = Z - li_in + li_out
364  "ldi r25,8\n" // li_in = li_in + 1
365  "add r24,r25\n"
366  "sub %A8,r24\n" // return Z to its initial value
367  "sbc %B8,__zero_reg__\n"
368  "andi r24,0x1f\n" // li_in = li_in % 4
369  "std %6,r24\n"
370  "ldd r24,%7\n" // Z = &(l[li_out])
371  "add %A8,r24\n"
372  "adc %B8,__zero_reg__\n"
373 
374  "st Z+,%A0\n" // l[li_out] = t
375  "st Z+,%B0\n"
376  "st Z+,%C0\n"
377  "st Z+,%D0\n"
378  "st Z+,%A1\n"
379  "st Z+,%B1\n"
380  "st Z+,%C1\n"
381  "st Z+,%D1\n"
382 
383  "add r24,r25\n" // li_out = li_out + 1
384  "sub %A8,r24\n" // return Z to its initial value
385  "sbc %B8,__zero_reg__\n"
386  "andi r24,0x1f\n" // li_out = li_out % 4
387  "std %7,r24\n"
388 
389  // s = leftRotate3_64(s) ^ l[li_out];
390  "lsl %A2\n" // s = leftRotate1_64(s)
391  "rol %B2\n"
392  "rol %C2\n"
393  "rol %D2\n"
394  "rol %A3\n"
395  "rol %B3\n"
396  "rol %C3\n"
397  "rol %D3\n"
398  "adc %A2,__zero_reg__\n"
399 
400  "lsl %A2\n" // s = leftRotate1_64(s)
401  "rol %B2\n"
402  "rol %C2\n"
403  "rol %D2\n"
404  "rol %A3\n"
405  "rol %B3\n"
406  "rol %C3\n"
407  "rol %D3\n"
408  "adc %A2,__zero_reg__\n"
409 
410  "lsl %A2\n" // s = leftRotate1_64(s)
411  "rol %B2\n"
412  "rol %C2\n"
413  "rol %D2\n"
414  "rol %A3\n"
415  "rol %B3\n"
416  "rol %C3\n"
417  "rol %D3\n"
418  "adc %A2,__zero_reg__\n"
419 
420  "eor %A2,%A0\n" // s ^= l[li_out]
421  "eor %B2,%B0\n"
422  "eor %C2,%C0\n"
423  "eor %D2,%D0\n"
424  "eor %A3,%A1\n"
425  "eor %B3,%B1\n"
426  "eor %C3,%C1\n"
427  "eor %D3,%D1\n"
428 
429  "std %A4,%A2\n" // store s
430  "std %B4,%B2\n"
431  "std %C4,%C2\n"
432  "std %D4,%D2\n"
433  "std %A5,%A3\n"
434  "std %B5,%B3\n"
435  "std %C5,%C3\n"
436  "std %D5,%D3\n"
437 
438  // Pop registers from the stack to recover the x and y values.
439  "pop %D3\n"
440  "pop %C3\n"
441  "pop %B3\n"
442  "pop %A3\n"
443  "pop %D2\n"
444  "pop %C2\n"
445  "pop %B2\n"
446  "pop %A2\n"
447  "pop %D1\n"
448  "pop %C1\n"
449  "pop %B1\n"
450  "pop %A1\n"
451  "pop %D0\n"
452  "pop %C0\n"
453  "pop %B0\n"
454  "pop %A0\n"
455 
456  // Bottom of the loop.
457  "inc r23\n"
458  "rjmp 1b\n"
459  "3:\n"
460 
461  : "+r"(xlow), "+r"(xhigh), "+r"(ylow), "+r"(yhigh),
462  "+Q"(slow), "+Q"(shigh), "+Q"(li_in), "+Q"(li_out)
463  : "z"(l), "r"(rounds)
464  : "r23", "r24", "r25"
465  );
466 
467  // Pack the results into the output and convert back to big-endian.
468  __asm__ __volatile__ (
469  "st Z,%D1\n"
470  "std Z+1,%C1\n"
471  "std Z+2,%B1\n"
472  "std Z+3,%A1\n"
473  "std Z+4,%D0\n"
474  "std Z+5,%C0\n"
475  "std Z+6,%B0\n"
476  "std Z+7,%A0\n"
477  "std Z+8,%D3\n"
478  "std Z+9,%C3\n"
479  "std Z+10,%B3\n"
480  "std Z+11,%A3\n"
481  "std Z+12,%D2\n"
482  "std Z+13,%C2\n"
483  "std Z+14,%B2\n"
484  "std Z+15,%A2\n"
485  : : "r"(xlow), "r"(xhigh), "r"(ylow), "r"(yhigh), "z"(output)
486  );
487 #else
488  uint64_t l[4];
489  uint64_t x, y, s;
490  uint8_t round;
491  uint8_t li_in = 0;
492  uint8_t li_out = rounds - 31;
493  uint8_t i = 0;
494 
495  // Copy the input block into the work registers.
496  unpack64(x, input);
497  unpack64(y, input + 8);
498 
499  // Prepare the key schedule.
500  memcpy(l, k + 1, li_out * sizeof(uint64_t));
501  s = k[0];
502 
503  // Perform all encryption rounds except the last.
504  for (round = rounds - 1; round > 0; --round, ++i) {
505  // Perform the round with the current key schedule word.
506  x = (rightRotate8_64(x) + y) ^ s;
507  y = leftRotate3_64(y) ^ x;
508 
509  // Calculate the next key schedule word.
510  l[li_out] = (s + rightRotate8_64(l[li_in])) ^ i;
511  s = leftRotate3_64(s) ^ l[li_out];
512  li_in = (li_in + 1) & 0x03;
513  li_out = (li_out + 1) & 0x03;
514  }
515 
516  // Perform the final round and copy to the output.
517  x = (rightRotate8_64(x) + y) ^ s;
518  y = leftRotate3_64(y) ^ x;
519  pack64(output, x);
520  pack64(output + 8, y);
521 #endif
522 }
523 
537 void SpeckLowMemory::decryptBlock(uint8_t *output, const uint8_t *input)
538 {
539  // Decryption is not supported.
540 }
541 
543 {
544  clean(k);
545 }
void encryptBlock(uint8_t *output, const uint8_t *input)
Encrypts a single block using this cipher.
size_t keySize() const
Default size of the key for this block cipher, in bytes.
size_t blockSize() const
Size of a single block processed by this cipher, in bytes.
void clear()
Clears all security-sensitive state from this block cipher.
void decryptBlock(uint8_t *output, const uint8_t *input)
Decrypts a single block using this cipher.
SpeckLowMemory()
Constructs a low-memory Speck block cipher with no initial key.
bool setKey(const uint8_t *key, size_t len)
Sets the key to use for future encryption and decryption operations.