ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
Speck.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 "Speck.h"
24 #include "Crypto.h"
25 #include "utility/RotateUtil.h"
26 #include "utility/EndianUtil.h"
27 #include <string.h>
28 
50 // The "avr-gcc" compiler doesn't do a very good job of compiling
51 // code involving 64-bit values. So we have to use inline assembly.
52 // It also helps to break the state up into 32-bit quantities
53 // because "asm" supports register names like %A0, %B0, %C0, %D0
54 // for the bytes in a 32-bit quantity, but it does not support
55 // %E0, %F0, %G0, %H0 for the high bytes of a 64-bit quantity.
56 #if defined(__AVR__)
57 #define USE_AVR_INLINE_ASM 1
58 #endif
59 
67  : rounds(32)
68 {
69 }
70 
71 Speck::~Speck()
72 {
73  clean(k);
74 }
75 
76 size_t Speck::blockSize() const
77 {
78  return 16;
79 }
80 
81 size_t Speck::keySize() const
82 {
83  // Also supports 128-bit and 192-bit, but we only report 256-bit.
84  return 32;
85 }
86 
87 // Pack/unpack byte-aligned big-endian 64-bit quantities.
88 #define pack64(data, value) \
89  do { \
90  uint64_t v = htobe64((value)); \
91  memcpy((data), &v, sizeof(uint64_t)); \
92  } while (0)
93 #define unpack64(value, data) \
94  do { \
95  memcpy(&(value), (data), sizeof(uint64_t)); \
96  (value) = be64toh((value)); \
97  } while (0)
98 
99 bool Speck::setKey(const uint8_t *key, size_t len)
100 {
101 #if USE_AVR_INLINE_ASM
102  uint64_t l[4];
103  uint8_t m, mb;
104  if (len == 32) {
105  m = 4;
106  mb = 3 * 8;
107  } else if (len == 24) {
108  m = 3;
109  mb = 2 * 8;
110  } else if (len == 16) {
111  m = 2;
112  mb = 8;
113  } else {
114  return false;
115  }
116  rounds = 30 + m;
117 
118  // Copy the first (m - 1) * 8 bytes of the key into the "l" array
119  // in reverse order to convert big endian into little-endian.
120  __asm__ __volatile__ (
121  "1:\n"
122  "ld __tmp_reg__,-Z\n"
123  "st X+,__tmp_reg__\n"
124  "dec %2\n"
125  "brne 1b\n"
126  : : "x"(l), "z"(key + len - 8), "r"(mb)
127  );
128 
129  // Copy the final 8 bytes of the key into k[0] in reverse order.
130  __asm__ __volatile__ (
131  "1:\n"
132  "ld __tmp_reg__,-Z\n"
133  "st X+,__tmp_reg__\n"
134  "dec %2\n"
135  "brne 1b\n"
136  : : "x"(k), "z"(key + len), "r"(8)
137  );
138 
139  // Expand the key to the full key schedule.
140  __asm__ __volatile__ (
141  "1:\n"
142  // l[li_out] = (k[i] + rightRotate8_64(l[li_in])) ^ i;
143  "add %A1,%2\n" // X = &(l[li_in])
144  "adc %B1,__zero_reg__\n"
145  "ld r8,X+\n" // x = l[li_in]
146  "ld r9,X+\n"
147  "ld r10,X+\n"
148  "ld r11,X+\n"
149  "ld r12,X+\n"
150  "ld r13,X+\n"
151  "ld r14,X+\n"
152  "ld r15,X+\n"
153 
154  "mov __tmp_reg__,r8\n" // x = rightRotate8_64(l[li_in])
155  "mov r8,r9\n"
156  "mov r9,r10\n"
157  "mov r10,r11\n"
158  "mov r11,r12\n"
159  "mov r12,r13\n"
160  "mov r13,r14\n"
161  "mov r14,r15\n"
162  "mov r15,__tmp_reg__\n"
163 
164  "ld r16,Z+\n" // y = k[i]
165  "ld r17,Z+\n"
166  "ld r18,Z+\n"
167  "ld r19,Z+\n"
168  "ld r20,Z+\n"
169  "ld r21,Z+\n"
170  "ld r22,Z+\n"
171  "ld r23,Z+\n"
172 
173  "add r8,r16\n" // x += y
174  "adc r9,r17\n"
175  "adc r10,r18\n"
176  "adc r11,r19\n"
177  "adc r12,r20\n"
178  "adc r13,r21\n"
179  "adc r14,r22\n"
180  "adc r15,r23\n"
181 
182  "eor r8,%4\n" // x ^= i
183 
184  // X = X - li_in + li_out
185  "ldi r24,8\n" // li_in = li_in + 1
186  "add %2,r24\n"
187  "sub %A1,%2\n" // return X to its initial value
188  "sbc %B1,__zero_reg__\n"
189  "ldi r25,0x1f\n"
190  "and %2,r25\n" // li_in = li_in % 4
191  "add %A1,%3\n" // X = &(l[li_out])
192  "adc %B1,__zero_reg__\n"
193 
194  "st X+,r8\n" // l[li_out] = x
195  "st X+,r9\n"
196  "st X+,r10\n"
197  "st X+,r11\n"
198  "st X+,r12\n"
199  "st X+,r13\n"
200  "st X+,r14\n"
201  "st X+,r15\n"
202 
203  "add %3,r24\n" // li_out = li_out + 1
204  "sub %A1,%3\n" // return X to its initial value
205  "sbc %B1,__zero_reg__\n"
206  "and %3,r25\n" // li_out = li_out % 4
207 
208  // k[i + 1] = leftRotate3_64(k[i]) ^ l[li_out];
209  "lsl r16\n" // y = leftRotate1_64(y)
210  "rol r17\n"
211  "rol r18\n"
212  "rol r19\n"
213  "rol r20\n"
214  "rol r21\n"
215  "rol r22\n"
216  "rol r23\n"
217  "adc r16,__zero_reg__\n"
218 
219  "lsl r16\n" // y = leftRotate1_64(y)
220  "rol r17\n"
221  "rol r18\n"
222  "rol r19\n"
223  "rol r20\n"
224  "rol r21\n"
225  "rol r22\n"
226  "rol r23\n"
227  "adc r16,__zero_reg__\n"
228 
229  "lsl r16\n" // y = leftRotate1_64(y)
230  "rol r17\n"
231  "rol r18\n"
232  "rol r19\n"
233  "rol r20\n"
234  "rol r21\n"
235  "rol r22\n"
236  "rol r23\n"
237  "adc r16,__zero_reg__\n"
238 
239  "eor r16,r8\n" // y ^= x
240  "eor r17,r9\n"
241  "eor r18,r10\n"
242  "eor r19,r11\n"
243  "eor r20,r12\n"
244  "eor r21,r13\n"
245  "eor r22,r14\n"
246  "eor r23,r15\n"
247 
248  "st Z,r16\n" // k[i + 1] = y
249  "std Z+1,r17\n"
250  "std Z+2,r18\n"
251  "std Z+3,r19\n"
252  "std Z+4,r20\n"
253  "std Z+5,r21\n"
254  "std Z+6,r22\n"
255  "std Z+7,r23\n"
256 
257  // Loop
258  "inc %4\n" // ++i
259  "dec %5\n" // --rounds
260  "breq 2f\n"
261  "rjmp 1b\n"
262  "2:\n"
263 
264  : : "z"(k), "x"(l),
265  "r"((uint8_t)0), // initial value of li_in
266  "r"((uint8_t)((m - 1) * 8)), // initial value of li_out
267  "r"(0), // initial value of i
268  "r"(rounds - 1)
269  : "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
270  "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
271  "r24", "r25"
272  );
273 #else
274  uint64_t l[4];
275  uint8_t m;
276  if (len == 32) {
277  m = 4;
278  unpack64(l[2], key);
279  unpack64(l[1], key + 8);
280  unpack64(l[0], key + 16);
281  unpack64(k[0], key + 24);
282  } else if (len == 24) {
283  m = 3;
284  unpack64(l[1], key);
285  unpack64(l[0], key + 8);
286  unpack64(k[0], key + 16);
287  } else if (len == 16) {
288  m = 2;
289  unpack64(l[0], key);
290  unpack64(k[0], key + 8);
291  } else {
292  return false;
293  }
294  rounds = 30 + m;
295  uint8_t li_in = 0;
296  uint8_t li_out = m - 1;
297  for (uint8_t i = 0; i < (rounds - 1); ++i) {
298  l[li_out] = (k[i] + rightRotate8_64(l[li_in])) ^ i;
299  k[i + 1] = leftRotate3_64(k[i]) ^ l[li_out];
300  if ((++li_in) >= m)
301  li_in = 0;
302  if ((++li_out) >= m)
303  li_out = 0;
304  }
305 #endif
306  clean(l);
307  return true;
308 }
309 
310 void Speck::encryptBlock(uint8_t *output, const uint8_t *input)
311 {
312 #if USE_AVR_INLINE_ASM
313  uint32_t xlow, xhigh, ylow, yhigh;
314 
315  // Unpack the input into the x and y variables, converting
316  // from big-endian into little-endian in the process.
317  __asm__ __volatile__ (
318  "ld %D1,Z\n"
319  "ldd %C1,Z+1\n"
320  "ldd %B1,Z+2\n"
321  "ldd %A1,Z+3\n"
322  "ldd %D0,Z+4\n"
323  "ldd %C0,Z+5\n"
324  "ldd %B0,Z+6\n"
325  "ldd %A0,Z+7\n"
326  "ldd %D3,Z+8\n"
327  "ldd %C3,Z+9\n"
328  "ldd %B3,Z+10\n"
329  "ldd %A3,Z+11\n"
330  "ldd %D2,Z+12\n"
331  "ldd %C2,Z+13\n"
332  "ldd %B2,Z+14\n"
333  "ldd %A2,Z+15\n"
334  : "=r"(xlow), "=r"(xhigh), "=r"(ylow), "=r"(yhigh)
335  : "z"(input)
336  );
337 
338  // Perform all encryption rounds. Z points to the key schedule.
339  __asm__ __volatile__ (
340  "1:\n"
341  // x = (rightRotate8_64(x) + y) ^ *s++;
342  "mov __tmp_reg__,%A0\n" // x = rightRotate8_64(x)
343  "mov %A0,%B0\n"
344  "mov %B0,%C0\n"
345  "mov %C0,%D0\n"
346  "mov %D0,%A1\n"
347  "mov %A1,%B1\n"
348  "mov %B1,%C1\n"
349  "mov %C1,%D1\n"
350  "mov %D1,__tmp_reg__\n"
351 
352  "add %A0,%A2\n" // x += y
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  "ld __tmp_reg__,Z+\n" // x ^= *s++
362  "eor %A0,__tmp_reg__\n"
363  "ld __tmp_reg__,Z+\n"
364  "eor %B0,__tmp_reg__\n"
365  "ld __tmp_reg__,Z+\n"
366  "eor %C0,__tmp_reg__\n"
367  "ld __tmp_reg__,Z+\n"
368  "eor %D0,__tmp_reg__\n"
369  "ld __tmp_reg__,Z+\n"
370  "eor %A1,__tmp_reg__\n"
371  "ld __tmp_reg__,Z+\n"
372  "eor %B1,__tmp_reg__\n"
373  "ld __tmp_reg__,Z+\n"
374  "eor %C1,__tmp_reg__\n"
375  "ld __tmp_reg__,Z+\n"
376  "eor %D1,__tmp_reg__\n"
377 
378  // y = leftRotate3_64(y) ^ x;
379  "lsl %A2\n" // y = leftRotate1_64(y)
380  "rol %B2\n"
381  "rol %C2\n"
382  "rol %D2\n"
383  "rol %A3\n"
384  "rol %B3\n"
385  "rol %C3\n"
386  "rol %D3\n"
387  "adc %A2,__zero_reg__\n"
388 
389  "lsl %A2\n" // y = leftRotate1_64(y)
390  "rol %B2\n"
391  "rol %C2\n"
392  "rol %D2\n"
393  "rol %A3\n"
394  "rol %B3\n"
395  "rol %C3\n"
396  "rol %D3\n"
397 
398  "adc %A2,__zero_reg__\n"
399  "lsl %A2\n" // y = leftRotate1_64(y)
400  "rol %B2\n"
401  "rol %C2\n"
402  "rol %D2\n"
403  "rol %A3\n"
404  "rol %B3\n"
405  "rol %C3\n"
406  "rol %D3\n"
407  "adc %A2,__zero_reg__\n"
408 
409  "eor %A2,%A0\n" // y ^= x
410  "eor %B2,%B0\n"
411  "eor %C2,%C0\n"
412  "eor %D2,%D0\n"
413  "eor %A3,%A1\n"
414  "eor %B3,%B1\n"
415  "eor %C3,%C1\n"
416  "eor %D3,%D1\n"
417 
418  // Loop
419  "dec %5\n" // --round
420  "breq 2f\n"
421  "rjmp 1b\n"
422  "2:\n"
423  : "+r"(xlow), "+r"(xhigh), "+r"(ylow), "+r"(yhigh)
424  : "z"(k), "r"(rounds)
425  );
426 
427  // Pack the results into the output and convert back to big-endian.
428  __asm__ __volatile__ (
429  "st Z,%D1\n"
430  "std Z+1,%C1\n"
431  "std Z+2,%B1\n"
432  "std Z+3,%A1\n"
433  "std Z+4,%D0\n"
434  "std Z+5,%C0\n"
435  "std Z+6,%B0\n"
436  "std Z+7,%A0\n"
437  "std Z+8,%D3\n"
438  "std Z+9,%C3\n"
439  "std Z+10,%B3\n"
440  "std Z+11,%A3\n"
441  "std Z+12,%D2\n"
442  "std Z+13,%C2\n"
443  "std Z+14,%B2\n"
444  "std Z+15,%A2\n"
445  : : "r"(xlow), "r"(xhigh), "r"(ylow), "r"(yhigh), "z"(output)
446  );
447 #else
448  uint64_t x, y;
449  const uint64_t *s = k;
450  unpack64(x, input);
451  unpack64(y, input + 8);
452  for (uint8_t round = rounds; round > 0; --round, ++s) {
453  x = (rightRotate8_64(x) + y) ^ s[0];
454  y = leftRotate3_64(y) ^ x;
455  }
456  pack64(output, x);
457  pack64(output + 8, y);
458 #endif
459 }
460 
461 void Speck::decryptBlock(uint8_t *output, const uint8_t *input)
462 {
463 #if USE_AVR_INLINE_ASM
464  uint32_t xlow, xhigh, ylow, yhigh;
465 
466  // Unpack the input into the x and y variables, converting
467  // from big-endian into little-endian in the process.
468  __asm__ __volatile__ (
469  "ld %D1,Z\n"
470  "ldd %C1,Z+1\n"
471  "ldd %B1,Z+2\n"
472  "ldd %A1,Z+3\n"
473  "ldd %D0,Z+4\n"
474  "ldd %C0,Z+5\n"
475  "ldd %B0,Z+6\n"
476  "ldd %A0,Z+7\n"
477  "ldd %D3,Z+8\n"
478  "ldd %C3,Z+9\n"
479  "ldd %B3,Z+10\n"
480  "ldd %A3,Z+11\n"
481  "ldd %D2,Z+12\n"
482  "ldd %C2,Z+13\n"
483  "ldd %B2,Z+14\n"
484  "ldd %A2,Z+15\n"
485  : "=r"(xlow), "=r"(xhigh), "=r"(ylow), "=r"(yhigh)
486  : "z"(input)
487  );
488 
489  // Perform all decryption rounds. Z points to the end of key schedule.
490  __asm__ __volatile__ (
491  "1:\n"
492  // y = rightRotate3_64(x ^ y);
493  "eor %A2,%A0\n" // y ^= x
494  "eor %B2,%B0\n"
495  "eor %C2,%C0\n"
496  "eor %D2,%D0\n"
497  "eor %A3,%A1\n"
498  "eor %B3,%B1\n"
499  "eor %C3,%C1\n"
500  "eor %D3,%D1\n"
501 
502  "bst %A2,0\n" // y = rightRotate1_64(y)
503  "ror %D3\n"
504  "ror %C3\n"
505  "ror %B3\n"
506  "ror %A3\n"
507  "ror %D2\n"
508  "ror %C2\n"
509  "ror %B2\n"
510  "ror %A2\n"
511  "bld %D3,7\n"
512 
513  "bst %A2,0\n" // y = rightRotate1_64(y)
514  "ror %D3\n"
515  "ror %C3\n"
516  "ror %B3\n"
517  "ror %A3\n"
518  "ror %D2\n"
519  "ror %C2\n"
520  "ror %B2\n"
521  "ror %A2\n"
522  "bld %D3,7\n"
523 
524  "bst %A2,0\n" // y = rightRotate1_64(y)
525  "ror %D3\n"
526  "ror %C3\n"
527  "ror %B3\n"
528  "ror %A3\n"
529  "ror %D2\n"
530  "ror %C2\n"
531  "ror %B2\n"
532  "ror %A2\n"
533  "bld %D3,7\n"
534 
535  // x = leftRotate8_64((x ^ *s--) - y);
536  "ld __tmp_reg__,-Z\n" // x ^= *s--
537  "eor %D1,__tmp_reg__\n"
538  "ld __tmp_reg__,-Z\n"
539  "eor %C1,__tmp_reg__\n"
540  "ld __tmp_reg__,-Z\n"
541  "eor %B1,__tmp_reg__\n"
542  "ld __tmp_reg__,-Z\n"
543  "eor %A1,__tmp_reg__\n"
544  "ld __tmp_reg__,-Z\n"
545  "eor %D0,__tmp_reg__\n"
546  "ld __tmp_reg__,-Z\n"
547  "eor %C0,__tmp_reg__\n"
548  "ld __tmp_reg__,-Z\n"
549  "eor %B0,__tmp_reg__\n"
550  "ld __tmp_reg__,-Z\n"
551  "eor %A0,__tmp_reg__\n"
552 
553  "sub %A0,%A2\n" // x -= y
554  "sbc %B0,%B2\n"
555  "sbc %C0,%C2\n"
556  "sbc %D0,%D2\n"
557  "sbc %A1,%A3\n"
558  "sbc %B1,%B3\n"
559  "sbc %C1,%C3\n"
560  "sbc %D1,%D3\n"
561 
562  "mov __tmp_reg__,%D1\n" // x = lefRotate8_64(x)
563  "mov %D1,%C1\n"
564  "mov %C1,%B1\n"
565  "mov %B1,%A1\n"
566  "mov %A1,%D0\n"
567  "mov %D0,%C0\n"
568  "mov %C0,%B0\n"
569  "mov %B0,%A0\n"
570  "mov %A0,__tmp_reg__\n"
571 
572  // Loop
573  "dec %5\n" // --round
574  "breq 2f\n"
575  "rjmp 1b\n"
576  "2:\n"
577  : "+r"(xlow), "+r"(xhigh), "+r"(ylow), "+r"(yhigh)
578  : "z"(k + rounds), "r"(rounds)
579  );
580 
581  // Pack the results into the output and convert back to big-endian.
582  __asm__ __volatile__ (
583  "st Z,%D1\n"
584  "std Z+1,%C1\n"
585  "std Z+2,%B1\n"
586  "std Z+3,%A1\n"
587  "std Z+4,%D0\n"
588  "std Z+5,%C0\n"
589  "std Z+6,%B0\n"
590  "std Z+7,%A0\n"
591  "std Z+8,%D3\n"
592  "std Z+9,%C3\n"
593  "std Z+10,%B3\n"
594  "std Z+11,%A3\n"
595  "std Z+12,%D2\n"
596  "std Z+13,%C2\n"
597  "std Z+14,%B2\n"
598  "std Z+15,%A2\n"
599  : : "r"(xlow), "r"(xhigh), "r"(ylow), "r"(yhigh), "z"(output)
600  );
601 #else
602  uint64_t x, y;
603  const uint64_t *s = k + rounds - 1;
604  unpack64(x, input);
605  unpack64(y, input + 8);
606  for (uint8_t round = rounds; round > 0; --round, --s) {
607  y = rightRotate3_64(x ^ y);
608  x = leftRotate8_64((x ^ s[0]) - y);
609  }
610  pack64(output, x);
611  pack64(output + 8, y);
612 #endif
613 }
614 
616 {
617  clean(k);
618 }
size_t keySize() const
Default size of the key for this block cipher, in bytes.
Definition: Speck.cpp:81
size_t blockSize() const
Size of a single block processed by this cipher, in bytes.
Definition: Speck.cpp:76
Speck()
Constructs a Speck block cipher with no initial key.
Definition: Speck.cpp:66
void encryptBlock(uint8_t *output, const uint8_t *input)
Encrypts a single block using this cipher.
Definition: Speck.cpp:310
void decryptBlock(uint8_t *output, const uint8_t *input)
Decrypts a single block using this cipher.
Definition: Speck.cpp:461
bool setKey(const uint8_t *key, size_t len)
Sets the key to use for future encryption and decryption operations.
Definition: Speck.cpp:99
void clear()
Clears all security-sensitive state from this block cipher.
Definition: Speck.cpp:615