Skinny-C
 All Data Structures Files Functions Variables Groups Pages
skinny64-ctr-vec128.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 "skinny64-cipher.h"
24 #include "skinny64-ctr-internal.h"
25 #include "skinny-internal.h"
26 #include <stdlib.h>
27 
28 #if SKINNY_VEC128_MATH
29 
30 /* This implementation encrypts eight blocks at a time */
31 #define SKINNY64_CTR_BLOCK_SIZE (SKINNY64_BLOCK_SIZE * 8)
32 
34 typedef struct
35 {
38 
40  SkinnyVector8x16_t counter[4];
41 
43  unsigned char ecounter[SKINNY64_CTR_BLOCK_SIZE];
44 
46  unsigned offset;
47 
49  void *base_ptr;
50 
51 } Skinny64CTRVec128Ctx_t;
52 
53 static int skinny64_ctr_vec128_init(Skinny64CTR_t *ctr)
54 {
55  Skinny64CTRVec128Ctx_t *ctx;
56  void *base_ptr;
57  if ((ctx = skinny_calloc(sizeof(Skinny64CTRVec128Ctx_t), &base_ptr)) == NULL)
58  return 0;
59  ctx->base_ptr = base_ptr;
60  ctx->offset = SKINNY64_CTR_BLOCK_SIZE;
61  ctr->ctx = ctx;
62  return 1;
63 }
64 
65 static void skinny64_ctr_vec128_cleanup(Skinny64CTR_t *ctr)
66 {
67  if (ctr->ctx) {
68  Skinny64CTRVec128Ctx_t *ctx = ctr->ctx;
69  void *base_ptr = ctx->base_ptr;
70  skinny_cleanse(ctx, sizeof(Skinny64CTRVec128Ctx_t));
71  free(base_ptr);
72  ctr->ctx = 0;
73  }
74 }
75 
76 static int skinny64_ctr_vec128_set_key(Skinny64CTR_t *ctr, const void *key, unsigned size)
77 {
78  Skinny64CTRVec128Ctx_t *ctx;
79 
80  /* Validate the parameters */
81  if (!key)
82  return 0;
83  ctx = ctr->ctx;
84  if (!ctx)
85  return 0;
86 
87  /* Populate the underlying key schedule */
88  if (!skinny64_set_key(&(ctx->kt.ks), key, size))
89  return 0;
90 
91  /* Reset the keystream */
92  ctx->offset = SKINNY64_CTR_BLOCK_SIZE;
93  return 1;
94 }
95 
96 static int skinny64_ctr_vec128_set_tweaked_key
97  (Skinny64CTR_t *ctr, const void *key, unsigned key_size)
98 {
99  Skinny64CTRVec128Ctx_t *ctx;
100 
101  /* Validate the parameters */
102  if (!key)
103  return 0;
104  ctx = ctr->ctx;
105  if (!ctx)
106  return 0;
107 
108  /* Populate the underlying key schedule */
109  if (!skinny64_set_tweaked_key(&(ctx->kt), key, key_size))
110  return 0;
111 
112  /* Reset the keystream */
113  ctx->offset = SKINNY64_CTR_BLOCK_SIZE;
114  return 1;
115 }
116 
117 static int skinny64_ctr_vec128_set_tweak
118  (Skinny64CTR_t *ctr, const void *tweak, unsigned tweak_size)
119 {
120  Skinny64CTRVec128Ctx_t *ctx;
121 
122  /* Validate the parameters */
123  ctx = ctr->ctx;
124  if (!ctx)
125  return 0;
126 
127  /* Populate the underlying tweak */
128  if (!skinny64_set_tweak(&(ctx->kt), tweak, tweak_size))
129  return 0;
130 
131  /* Reset the keystream */
132  ctx->offset = SKINNY64_CTR_BLOCK_SIZE;
133  return 1;
134 }
135 
136 /* Increment a specific column in an array of row vectors */
137 STATIC_INLINE void skinny64_ctr_increment
138  (SkinnyVector8x16_t *counter, unsigned column, unsigned inc)
139 {
140  uint8_t *ctr = ((uint8_t *)counter) + column * 2;
141  uint8_t *ptr;
142  unsigned index;
143  for (index = 8; index > 0; ) {
144  --index;
145  ptr = ctr + (index & 0x06) * 8;
146 #if SKINNY_LITTLE_ENDIAN
147  ptr += index & 0x01;
148 #else
149  ptr += 1 - (index & 0x01);
150 #endif
151  inc += ptr[0];
152  ptr[0] = (uint8_t)inc;
153  inc >>= 8;
154  }
155 }
156 
157 static int skinny64_ctr_vec128_set_counter
158  (Skinny64CTR_t *ctr, const void *counter, unsigned size)
159 {
160  Skinny64CTRVec128Ctx_t *ctx;
161  unsigned char block[SKINNY64_BLOCK_SIZE];
162 
163  /* Validate the parameters */
164  if (size > SKINNY64_BLOCK_SIZE)
165  return 0;
166  ctx = ctr->ctx;
167  if (!ctx)
168  return 0;
169 
170  /* Set the counter and reset the keystream to a block boundary */
171  if (counter) {
172  memset(block, 0, SKINNY64_BLOCK_SIZE - size);
173  memcpy(block + SKINNY64_BLOCK_SIZE - size, counter, size);
174  } else {
175  memset(block, 0, SKINNY64_BLOCK_SIZE);
176  }
177  ctx->offset = SKINNY64_CTR_BLOCK_SIZE;
178 
179  /* Load the counter block and convert into row vectors */
180  ctx->counter[0] = skinny_to_vec8x16(READ_WORD16(block, 0));
181  ctx->counter[1] = skinny_to_vec8x16(READ_WORD16(block, 2));
182  ctx->counter[2] = skinny_to_vec8x16(READ_WORD16(block, 4));
183  ctx->counter[3] = skinny_to_vec8x16(READ_WORD16(block, 6));
184 
185  /* Increment the second through seventh columns of each row vector */
186  skinny64_ctr_increment(ctx->counter, 1, 1);
187  skinny64_ctr_increment(ctx->counter, 2, 2);
188  skinny64_ctr_increment(ctx->counter, 3, 3);
189  skinny64_ctr_increment(ctx->counter, 4, 4);
190  skinny64_ctr_increment(ctx->counter, 5, 5);
191  skinny64_ctr_increment(ctx->counter, 6, 6);
192  skinny64_ctr_increment(ctx->counter, 7, 7);
193 
194  /* Clean up and exit */
195  skinny_cleanse(block, sizeof(block));
196  return 1;
197 }
198 
199 STATIC_INLINE SkinnyVector8x16_t skinny64_rotate_right
200  (SkinnyVector8x16_t x, unsigned count)
201 {
202  return (x >> count) | (x << (16 - count));
203 }
204 
205 STATIC_INLINE SkinnyVector8x16_t skinny64_sbox(SkinnyVector8x16_t x)
206 {
207  SkinnyVector8x16_t bit0 = ~x;
208  SkinnyVector8x16_t bit1 = bit0 >> 1;
209  SkinnyVector8x16_t bit2 = bit0 >> 2;
210  SkinnyVector8x16_t bit3 = bit0 >> 3;
211  bit0 ^= bit3 & bit2;
212  bit3 ^= bit1 & bit2;
213  bit2 ^= bit1 & bit0;
214  bit1 ^= bit0 & bit3;
215  x = ((bit0 << 3) & 0x8888U) |
216  ( bit1 & 0x1111U) |
217  ((bit2 << 1) & 0x2222U) |
218  ((bit3 << 2) & 0x4444U);
219  return ~x;
220 }
221 
222 static void skinny64_ecb_encrypt_eight
223  (void *output, const SkinnyVector8x16_t *input, const Skinny64Key_t *ks)
224 {
225  SkinnyVector8x16_t row0;
226  SkinnyVector8x16_t row1;
227  SkinnyVector8x16_t row2;
228  SkinnyVector8x16_t row3;
229  const Skinny64HalfCells_t *schedule;
230  unsigned index;
231  SkinnyVector8x16_t temp;
232 
233  /* Read the rows of all eight counter blocks into memory */
234  row0 = input[0];
235  row1 = input[1];
236  row2 = input[2];
237  row3 = input[3];
238 
239  /* Perform all encryption rounds */
240  schedule = ks->schedule;
241  for (index = ks->rounds; index > 0; --index, ++schedule) {
242  /* Apply the S-box to all bytes in the state */
243  row0 = skinny64_sbox(row0);
244  row1 = skinny64_sbox(row1);
245  row2 = skinny64_sbox(row2);
246  row3 = skinny64_sbox(row3);
247 
248  /* Apply the subkey for this round */
249  row0 ^= schedule->row[0];
250  row1 ^= schedule->row[1];
251  row2 ^= 0x20;
252 
253  /* Shift the rows */
254  row1 = skinny64_rotate_right(row1, 4);
255  row2 = skinny64_rotate_right(row2, 8);
256  row3 = skinny64_rotate_right(row3, 12);
257 
258  /* Mix the columns */
259  row1 ^= row2;
260  row2 ^= row0;
261  temp = row3 ^ row2;
262  row3 = row2;
263  row2 = row1;
264  row1 = row0;
265  row0 = temp;
266  }
267 
268  /* Write the rows of all eight blocks back to memory.
269  Note: In this case, direct WRITE_WORD16() calls seem to give
270  better performance than rearranging the vectors and performing
271  an unaligned vector write */
272 #if 0 /* SKINNY_LITTLE_ENDIAN && SKINNY_UNALIGNED */
273  *((SkinnyVector8x16U_t *)output) =
274  (SkinnyVector8x16_t){row0[0], row1[0], row2[0], row3[0],
275  row0[1], row1[1], row2[1], row3[1]};
276  *((SkinnyVector8x16U_t *)(output + 16)) =
277  (SkinnyVector8x16_t){row0[2], row1[2], row2[2], row3[2],
278  row0[3], row1[3], row2[3], row3[3]};
279  *((SkinnyVector8x16U_t *)(output + 32)) =
280  (SkinnyVector8x16_t){row0[4], row1[4], row2[4], row3[4],
281  row0[5], row1[5], row2[5], row3[5]};
282  *((SkinnyVector8x16U_t *)(output + 48)) =
283  (SkinnyVector8x16_t){row0[6], row1[6], row2[6], row3[6],
284  row0[7], row1[7], row2[7], row3[7]};
285 #else
286  WRITE_WORD16(output, 0, row0[0]);
287  WRITE_WORD16(output, 2, row1[0]);
288  WRITE_WORD16(output, 4, row2[0]);
289  WRITE_WORD16(output, 6, row3[0]);
290  WRITE_WORD16(output, 8, row0[1]);
291  WRITE_WORD16(output, 10, row1[1]);
292  WRITE_WORD16(output, 12, row2[1]);
293  WRITE_WORD16(output, 14, row3[1]);
294  WRITE_WORD16(output, 16, row0[2]);
295  WRITE_WORD16(output, 18, row1[2]);
296  WRITE_WORD16(output, 20, row2[2]);
297  WRITE_WORD16(output, 22, row3[2]);
298  WRITE_WORD16(output, 24, row0[3]);
299  WRITE_WORD16(output, 26, row1[3]);
300  WRITE_WORD16(output, 28, row2[3]);
301  WRITE_WORD16(output, 30, row3[3]);
302  WRITE_WORD16(output, 32, row0[4]);
303  WRITE_WORD16(output, 34, row1[4]);
304  WRITE_WORD16(output, 36, row2[4]);
305  WRITE_WORD16(output, 38, row3[4]);
306  WRITE_WORD16(output, 40, row0[5]);
307  WRITE_WORD16(output, 42, row1[5]);
308  WRITE_WORD16(output, 44, row2[5]);
309  WRITE_WORD16(output, 46, row3[5]);
310  WRITE_WORD16(output, 48, row0[6]);
311  WRITE_WORD16(output, 50, row1[6]);
312  WRITE_WORD16(output, 52, row2[6]);
313  WRITE_WORD16(output, 54, row3[6]);
314  WRITE_WORD16(output, 56, row0[7]);
315  WRITE_WORD16(output, 58, row1[7]);
316  WRITE_WORD16(output, 60, row2[7]);
317  WRITE_WORD16(output, 62, row3[7]);
318 #endif
319 }
320 
321 static int skinny64_ctr_vec128_encrypt
322  (void *output, const void *input, size_t size, Skinny64CTR_t *ctr)
323 {
324  Skinny64CTRVec128Ctx_t *ctx;
325  uint8_t *out = (uint8_t *)output;
326  const uint8_t *in = (const uint8_t *)input;
327 
328  /* Validate the parameters */
329  if (!output || !input)
330  return 0;
331  ctx = ctr->ctx;
332  if (!ctx)
333  return 0;
334 
335  /* Encrypt the input in CTR mode to create the output */
336  while (size > 0) {
337  if (ctx->offset >= SKINNY64_CTR_BLOCK_SIZE) {
338  /* We need a new keystream block */
339  skinny64_ecb_encrypt_eight
340  (ctx->ecounter, ctx->counter, &(ctx->kt.ks));
341  skinny64_ctr_increment(ctx->counter, 0, 8);
342  skinny64_ctr_increment(ctx->counter, 1, 8);
343  skinny64_ctr_increment(ctx->counter, 2, 8);
344  skinny64_ctr_increment(ctx->counter, 3, 8);
345  skinny64_ctr_increment(ctx->counter, 4, 8);
346  skinny64_ctr_increment(ctx->counter, 5, 8);
347  skinny64_ctr_increment(ctx->counter, 6, 8);
348  skinny64_ctr_increment(ctx->counter, 7, 8);
349 
350  /* XOR an entire keystream block in one go if possible */
351  if (size >= SKINNY64_CTR_BLOCK_SIZE) {
352  skinny64_xor(out, in, ctx->ecounter);
353  skinny64_xor(out + SKINNY64_BLOCK_SIZE,
354  in + SKINNY64_BLOCK_SIZE,
355  ctx->ecounter + SKINNY64_BLOCK_SIZE);
356  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 2,
357  in + SKINNY64_BLOCK_SIZE * 2,
358  ctx->ecounter + SKINNY64_BLOCK_SIZE * 2);
359  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 3,
360  in + SKINNY64_BLOCK_SIZE * 3,
361  ctx->ecounter + SKINNY64_BLOCK_SIZE * 3);
362  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 4,
363  in + SKINNY64_BLOCK_SIZE * 4,
364  ctx->ecounter + SKINNY64_BLOCK_SIZE * 4);
365  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 5,
366  in + SKINNY64_BLOCK_SIZE * 5,
367  ctx->ecounter + SKINNY64_BLOCK_SIZE * 5);
368  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 6,
369  in + SKINNY64_BLOCK_SIZE * 6,
370  ctx->ecounter + SKINNY64_BLOCK_SIZE * 6);
371  skinny64_xor(out + SKINNY64_BLOCK_SIZE * 7,
372  in + SKINNY64_BLOCK_SIZE * 7,
373  ctx->ecounter + SKINNY64_BLOCK_SIZE * 7);
374  out += SKINNY64_CTR_BLOCK_SIZE;
375  in += SKINNY64_CTR_BLOCK_SIZE;
376  size -= SKINNY64_CTR_BLOCK_SIZE;
377  } else {
378  /* Last partial block in the request */
379  skinny_xor(out, in, ctx->ecounter, size);
380  ctx->offset = size;
381  break;
382  }
383  } else {
384  /* Left-over keystream data from the last request */
385  size_t temp = SKINNY64_CTR_BLOCK_SIZE - ctx->offset;
386  if (temp > size)
387  temp = size;
388  skinny_xor(out, in, ctx->ecounter + ctx->offset, temp);
389  ctx->offset += temp;
390  out += temp;
391  in += temp;
392  size -= temp;
393  }
394  }
395  return 1;
396 }
397 
399 Skinny64CTRVtable_t const _skinny64_ctr_vec128 = {
400  skinny64_ctr_vec128_init,
401  skinny64_ctr_vec128_cleanup,
402  skinny64_ctr_vec128_set_key,
403  skinny64_ctr_vec128_set_tweaked_key,
404  skinny64_ctr_vec128_set_tweak,
405  skinny64_ctr_vec128_set_counter,
406  skinny64_ctr_vec128_encrypt
407 };
408 
409 #else /* !SKINNY_VEC128_MATH */
410 
411 /* Stubbed out */
412 Skinny64CTRVtable_t const _skinny64_ctr_vec128;
413 
414 #endif /* !SKINNY_VEC128_MATH */
int skinny64_set_tweak(Skinny64TweakedKey_t *ks, const void *tweak, unsigned tweak_size)
Changes the tweak value for a previously-initialized key schedule.
int skinny64_set_key(Skinny64Key_t *ks, const void *key, unsigned size)
Sets the key schedule for a Skinny64 block cipher.
int skinny64_set_tweaked_key(Skinny64TweakedKey_t *ks, const void *key, unsigned key_size)
Sets the key schedule for a Skinny64 block cipher, and prepare for tweaked encryption.
Key schedule for Skinny64 block ciphers when a tweak is in use.
Skinny64HalfCells_t schedule[SKINNY64_MAX_ROUNDS]
Key schedule for Skinny64 block ciphers.
Union that describes a 32-bit 2x4 array of cells.
#define SKINNY64_BLOCK_SIZE
Size of a block for Skinny64 block ciphers.
State information for Skinny-64 in CTR mode.