1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0"
*
* Written by Nir Drucker, Shay Gueron, and Dusan Kostic,
* AWS Cryptographic Algorithms Group.
* (ndrucker@amazon.com, gueron@amazon.com, dkostic@amazon.com)
*
* [1] The optimizations are based on the description developed in the paper:
* Drucker, Nir, and Shay Gueron. 2019. “A Toolbox for Software Optimization
* of QC-MDPC Code-Based Cryptosystems.” Journal of Cryptographic Engineering,
* January, 1–17. https://doi.org/10.1007/s13389-018-00200-4.
*
* [2] The decoder algorithm is the Black-Gray decoder in
* the early submission of CAKE (due to N. Sandrier and R Misoczki).
*
* [3] The analysis for the constant time implementation is given in
* Drucker, Nir, Shay Gueron, and Dusan Kostic. 2019.
* “On Constant-Time QC-MDPC Decoding with Negligible Failure Rate.”
* Cryptology EPrint Archive, 2019. https://eprint.iacr.org/2019/1289.
*
* [4] it was adapted to BGF in:
* Drucker, Nir, Shay Gueron, and Dusan Kostic. 2019.
* “QC-MDPC decoders with several shades of gray.”
* Cryptology EPrint Archive, 2019. To be published.
*
* [5] Chou, T.: QcBits: Constant-Time Small-Key Code-Based Cryptography.
* In: Gier-lichs, B., Poschmann, A.Y. (eds.) Cryptographic Hardware
* and Embedded Systems– CHES 2016. pp. 280–300. Springer Berlin Heidelberg,
* Berlin, Heidelberg (2016)
*
* [6] The rotate512_small funciton is a derivative of the code described in:
* Guimarães, Antonio, Diego F Aranha, and Edson Borin. 2019.
* “Optimized Implementation of QC-MDPC Code-Based Cryptography.”
* Concurrency and Computation: Practice and Experience 31 (18):
* e5089. https://doi.org/10.1002/cpe.5089.
*/
#include "decode.h"
#include "gf2x.h"
#include "utilities.h"
#include <string.h>
// Decoding (bit-flipping) parameter
#ifdef BG_DECODER
# if(LEVEL == 1)
# define MAX_IT 3
# elif(LEVEL == 3)
# define MAX_IT 4
# elif(LEVEL == 5)
# define MAX_IT 7
# else
# error "Level can only be 1/3/5"
# endif
#elif defined(BGF_DECODER)
# if(LEVEL == 1)
# define MAX_IT 5
# elif(LEVEL == 3)
# define MAX_IT 6
# elif(LEVEL == 5)
# define MAX_IT 7
# else
# error "Level can only be 1/3/5"
# endif
#endif
// Duplicates the first R_BITS of the syndrome three times
// |------------------------------------------|
// | Third copy | Second copy | first R_BITS |
// |------------------------------------------|
// This is required by the rotate functions.
_INLINE_ void
dup(IN OUT syndrome_t *s)
{
s->qw[R_QW - 1] =
(s->qw[0] << LAST_R_QW_LEAD) | (s->qw[R_QW - 1] & LAST_R_QW_MASK);
for(size_t i = 0; i < (2 * R_QW) - 1; i++)
{
s->qw[R_QW + i] =
(s->qw[i] >> LAST_R_QW_TRAIL) | (s->qw[i + 1] << LAST_R_QW_LEAD);
}
}
ret_t
compute_syndrome(OUT syndrome_t *syndrome, IN const ct_t *ct, IN const sk_t *sk)
{
// gf2x_mod_mul requires the values to be 64bit padded and extra (dbl) space
// for the results
DEFER_CLEANUP(dbl_pad_syndrome_t pad_s, dbl_pad_syndrome_cleanup);
DEFER_CLEANUP(pad_sk_t pad_sk = {0}, pad_sk_cleanup);
pad_sk[0].val = sk->bin[0];
pad_sk[1].val = sk->bin[1];
DEFER_CLEANUP(pad_ct_t pad_ct = {0}, pad_ct_cleanup);
pad_ct[0].val = ct->val[0];
pad_ct[1].val = ct->val[1];
// Compute s = c0*h0 + c1*h1:
GUARD(gf2x_mod_mul((uint64_t *)&pad_s[0], (uint64_t *)&pad_ct[0],
(uint64_t *)&pad_sk[0]));
GUARD(gf2x_mod_mul((uint64_t *)&pad_s[1], (uint64_t *)&pad_ct[1],
(uint64_t *)&pad_sk[1]));
GUARD(gf2x_add(pad_s[0].val.raw, pad_s[0].val.raw, pad_s[1].val.raw, R_SIZE));
memcpy((uint8_t *)syndrome->qw, pad_s[0].val.raw, R_SIZE);
dup(syndrome);
return SUCCESS;
}
_INLINE_ ret_t
recompute_syndrome(OUT syndrome_t *syndrome,
IN const ct_t *ct,
IN const sk_t *sk,
IN const split_e_t *splitted_e)
{
ct_t tmp_ct = *ct;
// Adapt the ciphertext
GUARD(gf2x_add(tmp_ct.val[0].raw, tmp_ct.val[0].raw, splitted_e->val[0].raw,
R_SIZE));
GUARD(gf2x_add(tmp_ct.val[1].raw, tmp_ct.val[1].raw, splitted_e->val[1].raw,
R_SIZE));
// Recompute the syndrome
GUARD(compute_syndrome(syndrome, &tmp_ct, sk));
return SUCCESS;
}
_INLINE_ uint8_t
get_threshold(IN const syndrome_t *s)
{
bike_static_assert(sizeof(*s) >= sizeof(r_t), syndrome_is_large_enough);
const uint32_t syndrome_weight = r_bits_vector_weight((const r_t *)s->qw);
// The equations below are defined in BIKE's specification:
// https://bikesuite.org/files/round2/spec/BIKE-Spec-Round2.2019.03.30.pdf
// Page 20 Section 2.4.2
const uint8_t threshold =
THRESHOLD_COEFF0 + (THRESHOLD_COEFF1 * syndrome_weight);
DMSG(" Thresold: %d\n", threshold);
return threshold;
}
// Use half-adder as described in [5].
_INLINE_ void
bit_sliced_adder(OUT upc_t *upc,
IN OUT syndrome_t *rotated_syndrome,
IN const size_t num_of_slices)
{
// From cache-memory perspective this loop should be the outside loop
for(size_t j = 0; j < num_of_slices; j++)
{
for(size_t i = 0; i < R_QW; i++)
{
const uint64_t carry = (upc->slice[j].u.qw[i] & rotated_syndrome->qw[i]);
upc->slice[j].u.qw[i] ^= rotated_syndrome->qw[i];
rotated_syndrome->qw[i] = carry;
}
}
}
_INLINE_ void
bit_slice_full_subtract(OUT upc_t *upc, IN uint8_t val)
{
// Borrow
uint64_t br[R_QW] = {0};
for(size_t j = 0; j < SLICES; j++)
{
const uint64_t lsb_mask = 0 - (val & 0x1);
val >>= 1;
// Perform a - b with c as the input/output carry
// br = 0 0 0 0 1 1 1 1
// a = 0 0 1 1 0 0 1 1
// b = 0 1 0 1 0 1 0 1
// -------------------
// o = 0 1 1 0 0 1 1 1
// c = 0 1 0 0 1 1 0 1
//
// o = a^b^c
// _ __ _ _ _ _ _
// br = abc + abc + abc + abc = abc + ((a+b))c
for(size_t i = 0; i < R_QW; i++)
{
const uint64_t a = upc->slice[j].u.qw[i];
const uint64_t b = lsb_mask;
const uint64_t tmp = ((~a) & b & (~br[i])) | ((((~a) | b) & br[i]));
upc->slice[j].u.qw[i] = a ^ b ^ br[i];
br[i] = tmp;
}
}
}
// Calculate the Unsatisfied Parity Checks (UPCs) and update the errors
// vector (e) accordingy. In addition, update the black and gray errors vector
// with the relevant values.
_INLINE_ void
find_err1(OUT split_e_t *e,
OUT split_e_t *black_e,
OUT split_e_t *gray_e,
IN const syndrome_t * syndrome,
IN const compressed_idx_dv_ar_t wlist,
IN const uint8_t threshold)
{
// This function uses the bit-slice-adder methodology of [5]:
DEFER_CLEANUP(syndrome_t rotated_syndrome = {0}, syndrome_cleanup);
DEFER_CLEANUP(upc_t upc, upc_cleanup);
for(uint32_t i = 0; i < N0; i++)
{
// UPC must start from zero at every iteration
memset(&upc, 0, sizeof(upc));
// 1) Right-rotate the syndrome for every secret key set bit index
// Then slice-add it to the UPC array.
for(size_t j = 0; j < DV; j++)
{
rotate_right(&rotated_syndrome, syndrome, wlist[i].val[j]);
bit_sliced_adder(&upc, &rotated_syndrome, LOG2_MSB(j + 1));
}
// 2) Subtract the threshold from the UPC counters
bit_slice_full_subtract(&upc, threshold);
// 3) Update the errors and the black errors vectors.
// The last slice of the UPC array holds the MSB of the accumulated values
// minus the threshold. Every zero bit indicates a potential error bit.
// The errors values are stored in the black array and xored with the
// errors Of the previous iteration.
const r_t *last_slice = &(upc.slice[SLICES - 1].u.r.val);
for(size_t j = 0; j < R_SIZE; j++)
{
const uint8_t sum_msb = (~last_slice->raw[j]);
black_e->val[i].raw[j] = sum_msb;
e->val[i].raw[j] ^= sum_msb;
}
// Ensure that the padding bits (upper bits of the last byte) are zero so
// they will not be included in the multiplication and in the hash function.
e->val[i].raw[R_SIZE - 1] &= LAST_R_BYTE_MASK;
// 4) Calculate the gray error array by adding "DELTA" to the UPC array.
// For that we reuse the rotated_syndrome variable setting it to all "1".
for(size_t l = 0; l < DELTA; l++)
{
memset((uint8_t *)rotated_syndrome.qw, 0xff, R_SIZE);
bit_sliced_adder(&upc, &rotated_syndrome, SLICES);
}
// 5) Update the gray list with the relevant bits that are not
// set in the black list.
for(size_t j = 0; j < R_SIZE; j++)
{
const uint8_t sum_msb = (~last_slice->raw[j]);
gray_e->val[i].raw[j] = (~(black_e->val[i].raw[j])) & sum_msb;
}
}
}
// Recalculate the UPCs and update the errors vector (e) according to it
// and to the black/gray vectors.
_INLINE_ void
find_err2(OUT split_e_t *e,
IN split_e_t *pos_e,
IN const syndrome_t * syndrome,
IN const compressed_idx_dv_ar_t wlist,
IN const uint8_t threshold)
{
DEFER_CLEANUP(syndrome_t rotated_syndrome = {0}, syndrome_cleanup);
DEFER_CLEANUP(upc_t upc, upc_cleanup);
for(uint32_t i = 0; i < N0; i++)
{
// UPC must start from zero at every iteration
memset(&upc, 0, sizeof(upc));
// 1) Right-rotate the syndrome for every secret key set bit index
// Then slice-add it to the UPC array.
for(size_t j = 0; j < DV; j++)
{
rotate_right(&rotated_syndrome, syndrome, wlist[i].val[j]);
bit_sliced_adder(&upc, &rotated_syndrome, LOG2_MSB(j + 1));
}
// 2) Subtract the threshold from the UPC counters
bit_slice_full_subtract(&upc, threshold);
// 3) Update the errors vector.
// The last slice of the UPC array holds the MSB of the accumulated values
// minus the threshold. Every zero bit indicates a potential error bit.
const r_t *last_slice = &(upc.slice[SLICES - 1].u.r.val);
for(size_t j = 0; j < R_SIZE; j++)
{
const uint8_t sum_msb = (~last_slice->raw[j]);
e->val[i].raw[j] ^= (pos_e->val[i].raw[j] & sum_msb);
}
// Ensure that the padding bits (upper bits of the last byte) are zero so
// they will not be included in the multiplication and in the hash function.
e->val[i].raw[R_SIZE - 1] &= LAST_R_BYTE_MASK;
}
}
ret_t
decode(OUT split_e_t *e,
IN const syndrome_t *original_s,
IN const ct_t *ct,
IN const sk_t *sk)
{
split_e_t black_e = {0};
split_e_t gray_e = {0};
syndrome_t s;
// Reset (init) the error because it is xored in the find_err funcitons.
memset(e, 0, sizeof(*e));
s = *original_s;
dup(&s);
for(uint32_t iter = 0; iter < MAX_IT; iter++)
{
const uint8_t threshold = get_threshold(&s);
DMSG(" Iteration: %d\n", iter);
DMSG(" Weight of e: %lu\n",
r_bits_vector_weight(&e->val[0]) + r_bits_vector_weight(&e->val[1]));
DMSG(" Weight of syndrome: %lu\n", r_bits_vector_weight((r_t *)s.qw));
find_err1(e, &black_e, &gray_e, &s, sk->wlist, threshold);
GUARD(recompute_syndrome(&s, ct, sk, e));
#ifdef BGF_DECODER
if(iter >= 1)
{
continue;
}
#endif
DMSG(" Weight of e: %lu\n",
r_bits_vector_weight(&e->val[0]) + r_bits_vector_weight(&e->val[1]));
DMSG(" Weight of syndrome: %lu\n", r_bits_vector_weight((r_t *)s.qw));
find_err2(e, &black_e, &s, sk->wlist, ((DV + 1) / 2) + 1);
GUARD(recompute_syndrome(&s, ct, sk, e));
DMSG(" Weight of e: %lu\n",
r_bits_vector_weight(&e->val[0]) + r_bits_vector_weight(&e->val[1]));
DMSG(" Weight of syndrome: %lu\n", r_bits_vector_weight((r_t *)s.qw));
find_err2(e, &gray_e, &s, sk->wlist, ((DV + 1) / 2) + 1);
GUARD(recompute_syndrome(&s, ct, sk, e));
}
if(r_bits_vector_weight((r_t *)s.qw) > 0)
{
BIKE_ERROR(E_DECODING_FAILURE);
}
return SUCCESS;
}
|