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