/* 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. * * [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 "cleanup.h" #include "decode_internal.h" #include "gf2x.h" #include "utilities.h" // Decoding (bit-flipping) parameter #if defined(BG_DECODER) # if(LEVEL == 1) # define MAX_IT 3 # elif(LEVEL == 3) # define MAX_IT 4 # else # error "Level can only be 1/3" # endif #elif defined(BGF_DECODER) # if(LEVEL == 1) # define MAX_IT 5 # elif(LEVEL == 3) # define MAX_IT 5 # else # error "Level can only be 1/3" # endif #endif ret_t compute_syndrome(OUT syndrome_t *syndrome, IN const pad_r_t *c0, IN const pad_r_t *h0, IN const decode_ctx *ctx) { DEFER_CLEANUP(pad_r_t pad_s, pad_r_cleanup); gf2x_mod_mul(&pad_s, c0, h0); bike_memcpy((uint8_t *)syndrome->qw, pad_s.val.raw, R_BYTES); ctx->dup(syndrome); return SUCCESS; } _INLINE_ ret_t recompute_syndrome(OUT syndrome_t *syndrome, IN const pad_r_t *c0, IN const pad_r_t *h0, IN const pad_r_t *pk, IN const e_t *e, IN const decode_ctx *ctx) { DEFER_CLEANUP(pad_r_t tmp_c0, pad_r_cleanup); DEFER_CLEANUP(pad_r_t e0 = {0}, pad_r_cleanup); DEFER_CLEANUP(pad_r_t e1 = {0}, pad_r_cleanup); e0.val = e->val[0]; e1.val = e->val[1]; // tmp_c0 = pk * e1 + c0 + e0 gf2x_mod_mul(&tmp_c0, &e1, pk); gf2x_mod_add(&tmp_c0, &tmp_c0, c0); gf2x_mod_add(&tmp_c0, &tmp_c0, &e0); // Recompute the syndrome using the updated ciphertext POSIX_GUARD(compute_syndrome(syndrome, &tmp_c0, h0, ctx)); 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 p. 16, Section 5.2 uint32_t thr = THRESHOLD_COEFF0 + (THRESHOLD_COEFF1 * syndrome_weight); const uint32_t mask = secure_l32_mask(thr, THRESHOLD_MIN); thr = (u32_barrier(mask) & thr) | (u32_barrier(~mask) & THRESHOLD_MIN); DMSG(" Threshold: %d\n", thr); return thr; } // Calculate the Unsatisfied Parity Checks (UPCs) and update the errors // vector (e) accordingly. In addition, update the black and gray errors vector // with the relevant values. _INLINE_ void find_err1(OUT e_t *e, OUT e_t *black_e, OUT e_t *gray_e, IN const syndrome_t * syndrome, IN const compressed_idx_d_ar_t wlist, IN const uint8_t threshold, IN const decode_ctx *ctx) { // 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 bike_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++) { ctx->rotate_right(&rotated_syndrome, syndrome, wlist[i].val[j]); ctx->bit_sliced_adder(&upc, &rotated_syndrome, LOG2_MSB(j + 1)); } // 2) Subtract the threshold from the UPC counters ctx->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_BYTES; 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_BYTES - 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++) { bike_memset((uint8_t *)rotated_syndrome.qw, 0xff, R_BYTES); ctx->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_BYTES; 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 e_t *e, IN e_t * pos_e, IN const syndrome_t * syndrome, IN const compressed_idx_d_ar_t wlist, IN const uint8_t threshold, IN const decode_ctx *ctx) { 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 bike_memset(&upc, 0, sizeof(upc)); // 1) Right-rotate the syndrome, for every index of a set bit in the secret // key. Then slice-add it to the UPC array. for(size_t j = 0; j < DV; j++) { ctx->rotate_right(&rotated_syndrome, syndrome, wlist[i].val[j]); ctx->bit_sliced_adder(&upc, &rotated_syndrome, LOG2_MSB(j + 1)); } // 2) Subtract the threshold from the UPC counters ctx->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_BYTES; 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 are not included in the multiplication, and in the hash function. e->val[i].raw[R_BYTES - 1] &= LAST_R_BYTE_MASK; } } ret_t decode(OUT e_t *e, IN const ct_t *ct, IN const sk_t *sk) { // Initialize the decode methods struct decode_ctx ctx; decode_ctx_init(&ctx); DEFER_CLEANUP(e_t black_e = {0}, e_cleanup); DEFER_CLEANUP(e_t gray_e = {0}, e_cleanup); DEFER_CLEANUP(pad_r_t c0 = {0}, pad_r_cleanup); DEFER_CLEANUP(pad_r_t h0 = {0}, pad_r_cleanup); pad_r_t pk = {0}; // Pad ciphertext (c0), secret key (h0), and public key (h) c0.val = ct->c0; h0.val = sk->bin[0]; pk.val = sk->pk; DEFER_CLEANUP(syndrome_t s = {0}, syndrome_cleanup); DMSG(" Computing s.\n"); POSIX_GUARD(compute_syndrome(&s, &c0, &h0, &ctx)); ctx.dup(&s); // Reset (init) the error because it is xored in the find_err functions. bike_memset(e, 0, sizeof(*e)); 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, &ctx); POSIX_GUARD(recompute_syndrome(&s, &c0, &h0, &pk, e, &ctx)); #if defined(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, &ctx); POSIX_GUARD(recompute_syndrome(&s, &c0, &h0, &pk, e, &ctx)); 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, &ctx); POSIX_GUARD(recompute_syndrome(&s, &c0, &h0, &pk, e, &ctx)); } if(r_bits_vector_weight((r_t *)s.qw) > 0) { BIKE_ERROR(E_DECODING_FAILURE); } return SUCCESS; }