Skinny-C
 All Data Structures Files Functions Variables Groups Pages
skinny128-cipher.c
1 /*
2  * Copyright (C) 2017 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 "skinny128-cipher.h"
24 #include "skinny-internal.h"
25 
26 #if SKINNY_64BIT
27 
28 STATIC_INLINE uint64_t skinny128_LFSR2(uint64_t x)
29 {
30  return ((x << 1) & 0xFEFEFEFEFEFEFEFEULL) ^
31  (((x >> 7) ^ (x >> 5)) & 0x0101010101010101ULL);
32 }
33 
34 STATIC_INLINE uint64_t skinny128_LFSR3(uint64_t x)
35 {
36  return ((x >> 1) & 0x7F7F7F7F7F7F7F7FULL) ^
37  (((x << 7) ^ (x << 1)) & 0x8080808080808080ULL);
38 }
39 
40 #else
41 
42 STATIC_INLINE uint32_t skinny128_LFSR2(uint32_t x)
43 {
44  return ((x << 1) & 0xFEFEFEFEU) ^ (((x >> 7) ^ (x >> 5)) & 0x01010101U);
45 }
46 
47 STATIC_INLINE uint32_t skinny128_LFSR3(uint32_t x)
48 {
49  return ((x >> 1) & 0x7F7F7F7FU) ^ (((x << 7) ^ (x << 1)) & 0x80808080U);
50 }
51 
52 #endif
53 
54 STATIC_INLINE void skinny128_permute_tk(Skinny128Cells_t *tk)
55 {
56  /* PT = [9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7] */
57 #if SKINNY_64BIT && SKINNY_LITTLE_ENDIAN
58  /* Permutation generated by http://programming.sirrida.de/calcperm.php */
59  uint64_t x = tk->lrow[1];
60  uint64_t y;
61  tk->lrow[1] = tk->lrow[0];
62  y = x & 0xFF0000FF00FF00FFULL;
63  tk->lrow[0] = (y << 16) | (y >> 48) |
64  ((x & 0x00000000FF000000ULL) << 32) |
65  ((x & 0x0000FF0000000000ULL) >> 16) |
66  ((x & 0x00FF00000000FF00ULL) >> 8);
67 #else
68  uint32_t row2 = tk->row[2];
69  uint32_t row3 = tk->row[3];
70  tk->row[2] = tk->row[0];
71  tk->row[3] = tk->row[1];
72  row3 = (row3 << 16) | (row3 >> 16);
73  tk->row[0] = ((row2 >> 8) & 0x000000FFU) |
74  ((row2 << 16) & 0x00FF0000U) |
75  ( row3 & 0xFF00FF00U);
76  tk->row[1] = ((row2 >> 16) & 0x000000FFU) |
77  (row2 & 0xFF000000U) |
78  ((row3 << 8) & 0x0000FF00U) |
79  ( row3 & 0x00FF0000U);
80 #endif
81 }
82 
83 /* Initializes the key schedule with TK1 */
84 static void skinny128_set_tk1
85  (Skinny128Key_t *ks, const void *key, unsigned key_size, int tweaked)
86 {
88  unsigned index;
89  uint16_t word;
90  uint8_t rc = 0;
91 
92  /* Unpack the key and convert from little-endian to host-endian */
93  if (key_size >= SKINNY128_BLOCK_SIZE) {
94  tk.row[0] = READ_WORD32(key, 0);
95  tk.row[1] = READ_WORD32(key, 4);
96  tk.row[2] = READ_WORD32(key, 8);
97  tk.row[3] = READ_WORD32(key, 12);
98  } else {
99  for (index = 0; index < key_size; index += 4) {
100  if ((index + 4) <= key_size) {
101  word = READ_WORD32(key, index);
102  } else {
103  word = READ_BYTE(key, index);
104  if ((index + 1) < key_size)
105  word |= (READ_BYTE(key, index + 1) << 8);
106  if ((index + 2) < key_size)
107  word |= (READ_BYTE(key, index + 2) << 16);
108  }
109  tk.row[index / 4] = word;
110  }
111  }
112 
113  /* Generate the key schedule words for all rounds */
114  for (index = 0; index < ks->rounds; ++index) {
115  /* Determine the subkey to use at this point in the key schedule */
116 #if SKINNY_64BIT
117  ks->schedule[index].lrow = tk.lrow[0];
118 #else
119  ks->schedule[index].row[0] = tk.row[0];
120  ks->schedule[index].row[1] = tk.row[1];
121 #endif
122 
123  /* XOR in the round constants for the first two rows.
124  The round constants for the 3rd and 4th rows are
125  fixed and will be applied during encrypt/decrypt */
126  rc = (rc << 1) ^ ((rc >> 5) & 0x01) ^ ((rc >> 4) & 0x01) ^ 0x01;
127  rc &= 0x3F;
128  ks->schedule[index].row[0] ^= (rc & 0x0F);
129  ks->schedule[index].row[1] ^= (rc >> 4);
130 
131  /* If we have a tweak, then we need to XOR a 1 bit into the
132  second bit of the top cell of the third column as recommended
133  by the SKINNY specification */
134  if (tweaked)
135  ks->schedule[index].row[0] ^= 0x00020000;
136 
137  /* Permute TK1 for the next round */
138  skinny128_permute_tk(&tk);
139  }
140 }
141 
142 /* XOR the key schedule with TK1 */
143 static void skinny128_xor_tk1(Skinny128Key_t *ks, const void *key)
144 {
145  Skinny128Cells_t tk;
146  unsigned index;
147 
148  /* Unpack the key and convert from little-endian to host-endian */
149  tk.row[0] = READ_WORD32(key, 0);
150  tk.row[1] = READ_WORD32(key, 4);
151  tk.row[2] = READ_WORD32(key, 8);
152  tk.row[3] = READ_WORD32(key, 12);
153 
154  /* Generate the key schedule words for all rounds */
155  for (index = 0; index < ks->rounds; ++index) {
156  /* Determine the subkey to use at this point in the key schedule */
157 #if SKINNY_64BIT
158  ks->schedule[index].lrow ^= tk.lrow[0];
159 #else
160  ks->schedule[index].row[0] ^= tk.row[0];
161  ks->schedule[index].row[1] ^= tk.row[1];
162 #endif
163 
164  /* Permute TK1 for the next round */
165  skinny128_permute_tk(&tk);
166  }
167 }
168 
169 /* XOR the key schedule with TK2 */
170 static void skinny128_set_tk2
171  (Skinny128Key_t *ks, const void *key, unsigned key_size)
172 {
173  Skinny128Cells_t tk;
174  unsigned index;
175  uint16_t word;
176 
177  /* Unpack the key and convert from little-endian to host-endian */
178  if (key_size >= SKINNY128_BLOCK_SIZE) {
179  tk.row[0] = READ_WORD32(key, 0);
180  tk.row[1] = READ_WORD32(key, 4);
181  tk.row[2] = READ_WORD32(key, 8);
182  tk.row[3] = READ_WORD32(key, 12);
183  } else {
184  for (index = 0; index < key_size; index += 4) {
185  if ((index + 4) <= key_size) {
186  word = READ_WORD32(key, index);
187  } else {
188  word = READ_BYTE(key, index);
189  if ((index + 1) < key_size)
190  word |= (READ_BYTE(key, index + 1) << 8);
191  if ((index + 2) < key_size)
192  word |= (READ_BYTE(key, index + 2) << 16);
193  }
194  tk.row[index / 4] = word;
195  }
196  }
197 
198  /* Generate the key schedule words for all rounds */
199  for (index = 0; index < ks->rounds; ++index) {
200  /* Determine the subkey to use at this point in the key schedule */
201 #if SKINNY_64BIT
202  ks->schedule[index].lrow ^= tk.lrow[0];
203 #else
204  ks->schedule[index].row[0] ^= tk.row[0];
205  ks->schedule[index].row[1] ^= tk.row[1];
206 #endif
207 
208  /* Permute TK2 for the next round */
209  skinny128_permute_tk(&tk);
210 
211  /* Apply LFSR2 to the first two rows of TK2 */
212 #if SKINNY_64BIT
213  tk.lrow[0] = skinny128_LFSR2(tk.lrow[0]);
214 #else
215  tk.row[0] = skinny128_LFSR2(tk.row[0]);
216  tk.row[1] = skinny128_LFSR2(tk.row[1]);
217 #endif
218  }
219 }
220 
221 /* XOR the key schedule with TK3 */
222 static void skinny128_set_tk3
223  (Skinny128Key_t *ks, const void *key, unsigned key_size)
224 {
225  Skinny128Cells_t tk;
226  unsigned index;
227  uint16_t word;
228 
229  /* Unpack the key and convert from little-endian to host-endian */
230  if (key_size >= SKINNY128_BLOCK_SIZE) {
231  tk.row[0] = READ_WORD32(key, 0);
232  tk.row[1] = READ_WORD32(key, 4);
233  tk.row[2] = READ_WORD32(key, 8);
234  tk.row[3] = READ_WORD32(key, 12);
235  } else {
236  for (index = 0; index < key_size; index += 4) {
237  if ((index + 4) <= key_size) {
238  word = READ_WORD32(key, index);
239  } else {
240  word = READ_BYTE(key, index);
241  if ((index + 1) < key_size)
242  word |= (READ_BYTE(key, index + 1) << 8);
243  if ((index + 2) < key_size)
244  word |= (READ_BYTE(key, index + 2) << 16);
245  }
246  tk.row[index / 4] = word;
247  }
248  }
249 
250  /* Generate the key schedule words for all rounds */
251  for (index = 0; index < ks->rounds; ++index) {
252  /* Determine the subkey to use at this point in the key schedule */
253 #if SKINNY_64BIT
254  ks->schedule[index].lrow ^= tk.lrow[0];
255 #else
256  ks->schedule[index].row[0] ^= tk.row[0];
257  ks->schedule[index].row[1] ^= tk.row[1];
258 #endif
259 
260  /* Permute TK3 for the next round */
261  skinny128_permute_tk(&tk);
262 
263  /* Apply LFSR3 to the first two rows of TK2 */
264 #if SKINNY_64BIT
265  tk.lrow[0] = skinny128_LFSR3(tk.lrow[0]);
266 #else
267  tk.row[0] = skinny128_LFSR3(tk.row[0]);
268  tk.row[1] = skinny128_LFSR3(tk.row[1]);
269 #endif
270  }
271 }
272 
273 static void skinny128_set_key_inner
274  (Skinny128Key_t *ks, const void *key, unsigned key_size, const void *tweak)
275 {
276  if (!tweak) {
277  /* Key only, no tweak */
278  if (key_size == SKINNY128_BLOCK_SIZE) {
279  ks->rounds = 40;
280  skinny128_set_tk1(ks, key, key_size, 0);
281  } else if (key_size <= (2 * SKINNY128_BLOCK_SIZE)) {
282  ks->rounds = 48;
283  skinny128_set_tk1(ks, key, SKINNY128_BLOCK_SIZE, 0);
284  skinny128_set_tk2(ks, key + SKINNY128_BLOCK_SIZE,
285  key_size - SKINNY128_BLOCK_SIZE);
286  } else {
287  ks->rounds = 56;
288  skinny128_set_tk1(ks, key, SKINNY128_BLOCK_SIZE, 0);
289  skinny128_set_tk2(ks, key + SKINNY128_BLOCK_SIZE,
291  skinny128_set_tk3(ks, key + SKINNY128_BLOCK_SIZE * 2,
292  key_size - SKINNY128_BLOCK_SIZE * 2);
293  }
294  } else {
295  /* Key and tweak */
296  if (key_size == SKINNY128_BLOCK_SIZE) {
297  ks->rounds = 48;
298  skinny128_set_tk1(ks, tweak, SKINNY128_BLOCK_SIZE, 1);
299  skinny128_set_tk2(ks, key, key_size);
300  } else {
301  ks->rounds = 56;
302  skinny128_set_tk1(ks, tweak, SKINNY128_BLOCK_SIZE, 1);
303  skinny128_set_tk2(ks, key, SKINNY128_BLOCK_SIZE);
304  skinny128_set_tk3(ks, key + SKINNY128_BLOCK_SIZE,
305  key_size - SKINNY128_BLOCK_SIZE);
306  }
307  }
308 }
309 
310 int skinny128_set_key(Skinny128Key_t *ks, const void *key, unsigned size)
311 {
312  /* Validate the parameters */
313  if (!ks || !key || size < SKINNY128_BLOCK_SIZE ||
314  size > (SKINNY128_BLOCK_SIZE * 3)) {
315  return 0;
316  }
317 
318  /* Set the key directly with no tweak */
319  skinny128_set_key_inner(ks, key, size, 0);
320  return 1;
321 }
322 
324  (Skinny128TweakedKey_t *ks, const void *key, unsigned key_size)
325 {
326  /* Validate the parameters */
327  if (!ks || !key || key_size < SKINNY128_BLOCK_SIZE ||
328  key_size > (SKINNY128_BLOCK_SIZE * 2)) {
329  return 0;
330  }
331 
332  /* Set the initial tweak to all-zeroes */
333  memset(ks->tweak, 0, sizeof(ks->tweak));
334 
335  /* Set the initial key and tweak value */
336  skinny128_set_key_inner(&(ks->ks), key, key_size, ks->tweak);
337  return 1;
338 }
339 
341  (Skinny128TweakedKey_t *ks, const void *tweak, unsigned tweak_size)
342 {
343  uint8_t tk_prev[SKINNY128_BLOCK_SIZE];
344 
345  /* Validate the parameters */
346  if (!ks || tweak_size < 1 || tweak_size > SKINNY128_BLOCK_SIZE) {
347  return 0;
348  }
349 
350  /* Read the new tweak value and swap with the original */
351  memcpy(tk_prev, ks->tweak, sizeof(tk_prev));
352  memcpy(ks->tweak, tweak, tweak_size);
353  memset(ks->tweak + tweak_size, 0, sizeof(ks->tweak) - tweak_size);
354 
355  /* XOR the original tweak out of the key schedule */
356  skinny128_xor_tk1(&(ks->ks), tk_prev);
357 
358  /* XOR the new tweak into the key schedule */
359  skinny128_xor_tk1(&(ks->ks), ks->tweak);
360  return 1;
361 }
362 
363 STATIC_INLINE uint32_t skinny128_rotate_right(uint32_t x, unsigned count)
364 {
365  /* Note: we are rotating the cells right, which actually moves
366  the values up closer to the MSB. That is, we do a left shift
367  on the word to rotate the cells in the word right */
368  return (x << count) | (x >> (32 - count));
369 }
370 
371 #if SKINNY_64BIT
372 
373 STATIC_INLINE uint64_t skinny128_sbox(uint64_t x)
374 {
375  /* See 32-bit version below for a description of what is happening here */
376  uint64_t y;
377  x = ~x;
378  x ^= (((x >> 2) & (x >> 3)) & 0x1111111111111111ULL);
379  y = (((x << 5) & (x << 1)) & 0x2020202020202020ULL);
380  x ^= (((x << 5) & (x << 4)) & 0x4040404040404040ULL) ^ y;
381  y = (((x << 2) & (x << 1)) & 0x8080808080808080ULL);
382  x ^= (((x >> 2) & (x << 1)) & 0x0202020202020202ULL) ^ y;
383  y = (((x >> 5) & (x << 1)) & 0x0404040404040404ULL);
384  x ^= (((x >> 1) & (x >> 2)) & 0x0808080808080808ULL) ^ y;
385  x = ~x;
386  return ((x & 0x0808080808080808ULL) << 1) |
387  ((x & 0x3232323232323232ULL) << 2) |
388  ((x & 0x0101010101010101ULL) << 5) |
389  ((x & 0x8080808080808080ULL) >> 6) |
390  ((x & 0x4040404040404040ULL) >> 4) |
391  ((x & 0x0404040404040404ULL) >> 2);
392 }
393 
394 STATIC_INLINE uint64_t skinny128_inv_sbox(uint64_t x)
395 {
396  /* See 32-bit version below for a description of what is happening here */
397  uint64_t y;
398  x = ~x;
399  y = (((x >> 1) & (x >> 3)) & 0x0101010101010101ULL);
400  x ^= (((x >> 2) & (x >> 3)) & 0x1010101010101010ULL) ^ y;
401  y = (((x >> 6) & (x >> 1)) & 0x0202020202020202ULL);
402  x ^= (((x >> 1) & (x >> 2)) & 0x0808080808080808ULL) ^ y;
403  y = (((x << 2) & (x << 1)) & 0x8080808080808080ULL);
404  x ^= (((x >> 1) & (x << 2)) & 0x0404040404040404ULL) ^ y;
405  y = (((x << 5) & (x << 1)) & 0x2020202020202020ULL);
406  x ^= (((x << 4) & (x << 5)) & 0x4040404040404040ULL) ^ y;
407  x = ~x;
408  return ((x & 0x0101010101010101ULL) << 2) |
409  ((x & 0x0404040404040404ULL) << 4) |
410  ((x & 0x0202020202020202ULL) << 6) |
411  ((x & 0x2020202020202020ULL) >> 5) |
412  ((x & 0xC8C8C8C8C8C8C8C8ULL) >> 2) |
413  ((x & 0x1010101010101010ULL) >> 1);
414 }
415 
416 #else
417 
418 STATIC_INLINE uint32_t skinny128_sbox(uint32_t x)
419 {
420  /* Original version from the specification is equivalent to:
421  *
422  * #define SBOX_MIX(x)
423  * (((~((((x) >> 1) | (x)) >> 2)) & 0x11111111U) ^ (x))
424  * #define SBOX_SWAP(x)
425  * (((x) & 0xF9F9F9F9U) |
426  * (((x) >> 1) & 0x02020202U) |
427  * (((x) << 1) & 0x04040404U))
428  * #define SBOX_PERMUTE(x)
429  * ((((x) & 0x01010101U) << 2) |
430  * (((x) & 0x06060606U) << 5) |
431  * (((x) & 0x20202020U) >> 5) |
432  * (((x) & 0xC8C8C8C8U) >> 2) |
433  * (((x) & 0x10101010U) >> 1))
434  *
435  * x = SBOX_MIX(x);
436  * x = SBOX_PERMUTE(x);
437  * x = SBOX_MIX(x);
438  * x = SBOX_PERMUTE(x);
439  * x = SBOX_MIX(x);
440  * x = SBOX_PERMUTE(x);
441  * x = SBOX_MIX(x);
442  * return SBOX_SWAP(x);
443  *
444  * However, we can mix the bits in their original positions and then
445  * delay the SBOX_PERMUTE and SBOX_SWAP steps to be performed with one
446  * final permuatation. This reduces the number of shift operations.
447  *
448  * We can further reduce the number of NOT operations from 7 to 2
449  * using the technique from https://github.com/kste/skinny_avx to
450  * convert NOR-XOR operations into AND-XOR operations by converting
451  * the S-box into its NOT-inverse.
452  */
453  uint32_t y;
454 
455  /* Mix the bits */
456  x = ~x;
457  x ^= (((x >> 2) & (x >> 3)) & 0x11111111U);
458  y = (((x << 5) & (x << 1)) & 0x20202020U);
459  x ^= (((x << 5) & (x << 4)) & 0x40404040U) ^ y;
460  y = (((x << 2) & (x << 1)) & 0x80808080U);
461  x ^= (((x >> 2) & (x << 1)) & 0x02020202U) ^ y;
462  y = (((x >> 5) & (x << 1)) & 0x04040404U);
463  x ^= (((x >> 1) & (x >> 2)) & 0x08080808U) ^ y;
464  x = ~x;
465 
466  /* Permutation generated by http://programming.sirrida.de/calcperm.php
467  The final permutation for each byte is [2 7 6 1 3 0 4 5] */
468  return ((x & 0x08080808U) << 1) |
469  ((x & 0x32323232U) << 2) |
470  ((x & 0x01010101U) << 5) |
471  ((x & 0x80808080U) >> 6) |
472  ((x & 0x40404040U) >> 4) |
473  ((x & 0x04040404U) >> 2);
474 }
475 
476 STATIC_INLINE uint32_t skinny128_inv_sbox(uint32_t x)
477 {
478  /* Original version from the specification is equivalent to:
479  *
480  * #define SBOX_MIX(x)
481  * (((~((((x) >> 1) | (x)) >> 2)) & 0x11111111U) ^ (x))
482  * #define SBOX_SWAP(x)
483  * (((x) & 0xF9F9F9F9U) |
484  * (((x) >> 1) & 0x02020202U) |
485  * (((x) << 1) & 0x04040404U))
486  * #define SBOX_PERMUTE_INV(x)
487  * ((((x) & 0x08080808U) << 1) |
488  * (((x) & 0x32323232U) << 2) |
489  * (((x) & 0x01010101U) << 5) |
490  * (((x) & 0xC0C0C0C0U) >> 5) |
491  * (((x) & 0x04040404U) >> 2))
492  *
493  * x = SBOX_SWAP(x);
494  * x = SBOX_MIX(x);
495  * x = SBOX_PERMUTE_INV(x);
496  * x = SBOX_MIX(x);
497  * x = SBOX_PERMUTE_INV(x);
498  * x = SBOX_MIX(x);
499  * x = SBOX_PERMUTE_INV(x);
500  * return SBOX_MIX(x);
501  *
502  * However, we can mix the bits in their original positions and then
503  * delay the SBOX_PERMUTE_INV and SBOX_SWAP steps to be performed with one
504  * final permuatation. This reduces the number of shift operations.
505  */
506  uint32_t y;
507 
508  /* Mix the bits */
509  x = ~x;
510  y = (((x >> 1) & (x >> 3)) & 0x01010101U);
511  x ^= (((x >> 2) & (x >> 3)) & 0x10101010U) ^ y;
512  y = (((x >> 6) & (x >> 1)) & 0x02020202U);
513  x ^= (((x >> 1) & (x >> 2)) & 0x08080808U) ^ y;
514  y = (((x << 2) & (x << 1)) & 0x80808080U);
515  x ^= (((x >> 1) & (x << 2)) & 0x04040404U) ^ y;
516  y = (((x << 5) & (x << 1)) & 0x20202020U);
517  x ^= (((x << 4) & (x << 5)) & 0x40404040U) ^ y;
518  x = ~x;
519 
520  /* Permutation generated by http://programming.sirrida.de/calcperm.php
521  The final permutation for each byte is [5 3 0 4 6 7 2 1] */
522  return ((x & 0x01010101U) << 2) |
523  ((x & 0x04040404U) << 4) |
524  ((x & 0x02020202U) << 6) |
525  ((x & 0x20202020U) >> 5) |
526  ((x & 0xC8C8C8C8U) >> 2) |
527  ((x & 0x10101010U) >> 1);
528 }
529 
530 #endif
531 
533  (void *output, const void *input, const Skinny128Key_t *ks)
534 {
535  Skinny128Cells_t state;
536  const Skinny128HalfCells_t *schedule;
537  unsigned index;
538  uint32_t temp;
539 
540  /* Read the input buffer and convert little-endian to host-endian */
541  state.row[0] = READ_WORD32(input, 0);
542  state.row[1] = READ_WORD32(input, 4);
543  state.row[2] = READ_WORD32(input, 8);
544  state.row[3] = READ_WORD32(input, 12);
545 
546  /* Perform all encryption rounds */
547  schedule = ks->schedule;
548  for (index = ks->rounds; index > 0; --index, ++schedule) {
549  /* Apply the S-box to all bytes in the state */
550 #if SKINNY_64BIT
551  state.lrow[0] = skinny128_sbox(state.lrow[0]);
552  state.lrow[1] = skinny128_sbox(state.lrow[1]);
553 #else
554  state.row[0] = skinny128_sbox(state.row[0]);
555  state.row[1] = skinny128_sbox(state.row[1]);
556  state.row[2] = skinny128_sbox(state.row[2]);
557  state.row[3] = skinny128_sbox(state.row[3]);
558 #endif
559 
560  /* Apply the subkey for this round */
561 #if SKINNY_64BIT
562  state.lrow[0] ^= schedule->lrow;
563  state.lrow[1] ^= 0x02;
564 #else
565  state.row[0] ^= schedule->row[0];
566  state.row[1] ^= schedule->row[1];
567  state.row[2] ^= 0x02;
568 #endif
569 
570  /* Shift the rows */
571  state.row[1] = skinny128_rotate_right(state.row[1], 8);
572  state.row[2] = skinny128_rotate_right(state.row[2], 16);
573  state.row[3] = skinny128_rotate_right(state.row[3], 24);
574 
575  /* Mix the columns */
576  state.row[1] ^= state.row[2];
577  state.row[2] ^= state.row[0];
578  temp = state.row[3] ^ state.row[2];
579  state.row[3] = state.row[2];
580  state.row[2] = state.row[1];
581  state.row[1] = state.row[0];
582  state.row[0] = temp;
583  }
584 
585  /* Convert host-endian back into little-endian in the output buffer */
586  WRITE_WORD32(output, 0, state.row[0]);
587  WRITE_WORD32(output, 4, state.row[1]);
588  WRITE_WORD32(output, 8, state.row[2]);
589  WRITE_WORD32(output, 12, state.row[3]);
590 }
591 
593  (void *output, const void *input, const Skinny128Key_t *ks)
594 {
595  Skinny128Cells_t state;
596  const Skinny128HalfCells_t *schedule;
597  unsigned index;
598  uint32_t temp;
599 
600  /* Read the input buffer and convert little-endian to host-endian */
601  state.row[0] = READ_WORD32(input, 0);
602  state.row[1] = READ_WORD32(input, 4);
603  state.row[2] = READ_WORD32(input, 8);
604  state.row[3] = READ_WORD32(input, 12);
605 
606  /* Perform all decryption rounds */
607  schedule = &(ks->schedule[ks->rounds - 1]);
608  for (index = ks->rounds; index > 0; --index, --schedule) {
609  /* Inverse mix of the columns */
610  temp = state.row[3];
611  state.row[3] = state.row[0];
612  state.row[0] = state.row[1];
613  state.row[1] = state.row[2];
614  state.row[3] ^= temp;
615  state.row[2] = temp ^ state.row[0];
616  state.row[1] ^= state.row[2];
617 
618  /* Inverse shift of the rows */
619  state.row[1] = skinny128_rotate_right(state.row[1], 24);
620  state.row[2] = skinny128_rotate_right(state.row[2], 16);
621  state.row[3] = skinny128_rotate_right(state.row[3], 8);
622 
623  /* Apply the subkey for this round */
624 #if SKINNY_64BIT
625  state.lrow[0] ^= schedule->lrow;
626 #else
627  state.row[0] ^= schedule->row[0];
628  state.row[1] ^= schedule->row[1];
629 #endif
630  state.row[2] ^= 0x02;
631 
632  /* Apply the inverse of the S-box to all bytes in the state */
633 #if SKINNY_64BIT
634  state.lrow[0] = skinny128_inv_sbox(state.lrow[0]);
635  state.lrow[1] = skinny128_inv_sbox(state.lrow[1]);
636 #else
637  state.row[0] = skinny128_inv_sbox(state.row[0]);
638  state.row[1] = skinny128_inv_sbox(state.row[1]);
639  state.row[2] = skinny128_inv_sbox(state.row[2]);
640  state.row[3] = skinny128_inv_sbox(state.row[3]);
641 #endif
642  }
643 
644  /* Convert host-endian back into little-endian in the output buffer */
645  WRITE_WORD32(output, 0, state.row[0]);
646  WRITE_WORD32(output, 4, state.row[1]);
647  WRITE_WORD32(output, 8, state.row[2]);
648  WRITE_WORD32(output, 12, state.row[3]);
649 }
Union that describes a 64-bit 2x4 array of cells.
Skinny128HalfCells_t schedule[SKINNY128_MAX_ROUNDS]
Key schedule for Skinny128 block ciphers when a tweak is in use.
int skinny128_set_tweaked_key(Skinny128TweakedKey_t *ks, const void *key, unsigned key_size)
Sets the key schedule for a Skinny128 block cipher, and prepare for tweaked encryption.
void skinny128_ecb_encrypt(void *output, const void *input, const Skinny128Key_t *ks)
Encrypts a single block using the Skinny128 block cipher in ECB mode.
Key schedule for Skinny128 block ciphers.
Union that describes a 128-bit 4x4 array of cells.
void skinny128_ecb_decrypt(void *output, const void *input, const Skinny128Key_t *ks)
Decrypts a single block using the Skinny128 block cipher in ECB mode.
int skinny128_set_key(Skinny128Key_t *ks, const void *key, unsigned size)
Sets the key schedule for a Skinny128 block cipher.
#define SKINNY128_BLOCK_SIZE
Size of a block for Skinny128 block ciphers.
int skinny128_set_tweak(Skinny128TweakedKey_t *ks, const void *tweak, unsigned tweak_size)
Changes the tweak value for a previously-initialized key schedule.
uint8_t tweak[SKINNY128_BLOCK_SIZE]