247 lines
8.7 KiB
C
247 lines
8.7 KiB
C
#include "parameters.h"
|
|
#include "reed_muller.h"
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
/**
|
|
* @file reed_muller.c
|
|
* Constant time implementation of Reed-Muller code RM(1,7)
|
|
*/
|
|
|
|
// setting this will help the compiler with auto vectorization
|
|
#undef ALIGNVECTORS
|
|
|
|
|
|
|
|
// number of repeated code words
|
|
#define MULTIPLICITY CEIL_DIVIDE(PARAM_N2, 128)
|
|
|
|
// codeword is 128 bits, seen multiple ways
|
|
typedef union {
|
|
uint8_t u8[16];
|
|
uint32_t u32[4];
|
|
} codeword
|
|
;
|
|
|
|
// Expanded codeword has a short for every bit, for internal calculations
|
|
typedef int16_t expandedCodeword[128]
|
|
;
|
|
|
|
// copy bit 0 into all bits of a 32 bit value
|
|
#define BIT0MASK(x) (int32_t)(-((x) & 1))
|
|
|
|
|
|
static void encode(codeword *word, int32_t message);
|
|
static void hadamard(expandedCodeword *src, expandedCodeword *dst);
|
|
static void expand_and_sum(expandedCodeword *dest, codeword src[]);
|
|
static int32_t find_peaks(expandedCodeword *transform);
|
|
|
|
|
|
|
|
/**
|
|
* @brief Encode a single byte into a single codeword using RM(1,7)
|
|
*
|
|
* Encoding matrix of this code:
|
|
* bit pattern (note that bits are numbered big endian)
|
|
* 0 aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa
|
|
* 1 cccccccc cccccccc cccccccc cccccccc
|
|
* 2 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0
|
|
* 3 ff00ff00 ff00ff00 ff00ff00 ff00ff00
|
|
* 4 ffff0000 ffff0000 ffff0000 ffff0000
|
|
* 5 ffffffff 00000000 ffffffff 00000000
|
|
* 6 ffffffff ffffffff 00000000 00000000
|
|
* 7 ffffffff ffffffff ffffffff ffffffff
|
|
*
|
|
* @param[out] word An RM(1,7) codeword
|
|
* @param[in] message A message
|
|
*/
|
|
static void encode(codeword *word, int32_t message) {
|
|
// the four parts of the word are identical
|
|
// except for encoding bits 5 and 6
|
|
int32_t first_word;
|
|
// bit 7 flips all the bits, do that first to save work
|
|
first_word = BIT0MASK(message >> 7);
|
|
// bits 0, 1, 2, 3, 4 are the same for all four longs
|
|
// (Warning: in the bit matrix above, low bits are at the left!)
|
|
first_word ^= BIT0MASK(message >> 0) & 0xaaaaaaaa;
|
|
first_word ^= BIT0MASK(message >> 1) & 0xcccccccc;
|
|
first_word ^= BIT0MASK(message >> 2) & 0xf0f0f0f0;
|
|
first_word ^= BIT0MASK(message >> 3) & 0xff00ff00;
|
|
first_word ^= BIT0MASK(message >> 4) & 0xffff0000;
|
|
// we can store this in the first quarter
|
|
word->u32[0] = first_word;
|
|
// bit 5 flips entries 1 and 3; bit 6 flips 2 and 3
|
|
first_word ^= BIT0MASK(message >> 5);
|
|
word->u32[1] = first_word;
|
|
first_word ^= BIT0MASK(message >> 6);
|
|
word->u32[3] = first_word;
|
|
first_word ^= BIT0MASK(message >> 5);
|
|
word->u32[2] = first_word;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Hadamard transform
|
|
*
|
|
* Perform hadamard transform of src and store result in dst
|
|
* src is overwritten: it is also used as intermediate buffer
|
|
* Method is best explained if we use H(3) instead of H(7):
|
|
*
|
|
* The routine multiplies by the matrix H(3):
|
|
* [1 1 1 1 1 1 1 1]
|
|
* [1 -1 1 -1 1 -1 1 -1]
|
|
* [1 1 -1 -1 1 1 -1 -1]
|
|
* [a b c d e f g h] * [1 -1 -1 1 1 -1 -1 1] = result of routine
|
|
* [1 1 1 1 -1 -1 -1 -1]
|
|
* [1 -1 1 -1 -1 1 -1 1]
|
|
* [1 1 -1 -1 -1 -1 1 1]
|
|
* [1 -1 -1 1 -1 1 1 -1]
|
|
* You can do this in three passes, where each pass does this:
|
|
* set lower half of buffer to pairwise sums,
|
|
* and upper half to differences
|
|
* index 0 1 2 3 4 5 6 7
|
|
* input: a, b, c, d, e, f, g, h
|
|
* pass 1: a+b, c+d, e+f, g+h, a-b, c-d, e-f, g-h
|
|
* pass 2: a+b+c+d, e+f+g+h, a-b+c-d, e-f+g-h, a+b-c-d, e+f-g-h, a-b-c+d, e-f-g+h
|
|
* pass 3: a+b+c+d+e+f+g+h a+b-c-d+e+f-g-h a+b+c+d-e-f-g-h a+b-c-d-e+-f+g+h
|
|
* a-b+c-d+e-f+g-h a-b-c+d+e-f-g+h a-b+c-d-e+f-g+h a-b-c+d-e+f+g-h
|
|
* This order of computation is chosen because it vectorises well.
|
|
* Likewise, this routine multiplies by H(7) in seven passes.
|
|
*
|
|
* @param[out] src Structure that contain the expanded codeword
|
|
* @param[out] dst Structure that contain the expanded codeword
|
|
*/
|
|
static void hadamard(expandedCodeword *src, expandedCodeword *dst) {
|
|
// the passes move data:
|
|
// src -> dst -> src -> dst -> src -> dst -> src -> dst
|
|
// using p1 and p2 alternately
|
|
expandedCodeword *p1 = src;
|
|
expandedCodeword *p2 = dst;
|
|
for (int32_t pass = 0 ; pass < 7 ; pass++) {
|
|
for (int32_t i = 0 ; i < 64 ; i++) {
|
|
(*p2)[i] = (*p1)[2 * i] + (*p1)[2 * i + 1];
|
|
(*p2)[i + 64] = (*p1)[2 * i] - (*p1)[2 * i + 1];
|
|
}
|
|
// swap p1, p2 for next round
|
|
expandedCodeword *p3 = p1;
|
|
p1 = p2;
|
|
p2 = p3;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Add multiple codewords into expanded codeword
|
|
*
|
|
* Accesses memory in order
|
|
* Note: this does not write the codewords as -1 or +1 as the green machine does
|
|
* instead, just 0 and 1 is used.
|
|
* The resulting hadamard transform has:
|
|
* all values are halved
|
|
* the first entry is 64 too high
|
|
*
|
|
* @param[out] dest Structure that contain the expanded codeword
|
|
* @param[in] src Structure that contain the codeword
|
|
*/
|
|
static void expand_and_sum(expandedCodeword *dest, codeword src[]) {
|
|
// start with the first copy
|
|
for (int32_t part = 0 ; part < 4 ; part++) {
|
|
for (int32_t bit = 0 ; bit < 32 ; bit++) {
|
|
(*dest)[part * 32 + bit] = src[0].u32[part] >> bit & 1;
|
|
}
|
|
}
|
|
// sum the rest of the copies
|
|
for (int32_t copy = 1 ; copy < MULTIPLICITY ; copy++) {
|
|
for (int32_t part = 0 ; part < 4 ; part++) {
|
|
for (int32_t bit = 0 ; bit < 32 ; bit++) {
|
|
(*dest)[part * 32 + bit] += src[copy].u32[part] >> bit & 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Finding the location of the highest value
|
|
*
|
|
* This is the final step of the green machine: find the location of the highest value,
|
|
* and add 128 if the peak is positive
|
|
* if there are two identical peaks, the peak with smallest value
|
|
* in the lowest 7 bits it taken
|
|
* @param[in] transform Structure that contain the expanded codeword
|
|
*/
|
|
static int32_t find_peaks(expandedCodeword *transform) {
|
|
int32_t peak_abs_value = 0;
|
|
int32_t peak_value = 0;
|
|
int32_t peak_pos = 0;
|
|
for (int32_t i = 0 ; i < 128 ; i++) {
|
|
// get absolute value
|
|
int32_t t = (*transform)[i];
|
|
int32_t pos_mask = -(t > 0);
|
|
int32_t absolute = (pos_mask & t) | (~pos_mask & -t);
|
|
// all compilers nowadays compile with a conditional move
|
|
peak_value = absolute > peak_abs_value ? t : peak_value;
|
|
peak_pos = absolute > peak_abs_value ? i : peak_pos;
|
|
peak_abs_value = absolute > peak_abs_value ? absolute : peak_abs_value;
|
|
}
|
|
// set bit 7
|
|
peak_pos |= 128 * (peak_value > 0);
|
|
return peak_pos;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Encodes the received word
|
|
*
|
|
* The message consists of N1 bytes each byte is encoded into PARAM_N2 bits,
|
|
* or MULTIPLICITY repeats of 128 bits
|
|
*
|
|
* @param[out] cdw Array of size VEC_N1N2_SIZE_64 receiving the encoded message
|
|
* @param[in] msg Array of size VEC_N1_SIZE_64 storing the message
|
|
*/
|
|
void PQCLEAN_HQCRMRS192_CLEAN_reed_muller_encode(uint64_t *cdw, const uint64_t *msg) {
|
|
uint8_t *message_array = (uint8_t *) msg;
|
|
codeword *codeArray = (codeword *) cdw;
|
|
for (size_t i = 0 ; i < VEC_N1_SIZE_BYTES ; i++) {
|
|
// fill entries i * MULTIPLICITY to (i+1) * MULTIPLICITY
|
|
int32_t pos = i * MULTIPLICITY;
|
|
// encode first word
|
|
encode(&codeArray[pos], message_array[i]);
|
|
// copy to other identical codewords
|
|
for (size_t copy = 1 ; copy < MULTIPLICITY ; copy++) {
|
|
memcpy(&codeArray[pos + copy], &codeArray[pos], sizeof(codeword));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Decodes the received word
|
|
*
|
|
* Decoding uses fast hadamard transform, for a more complete picture on Reed-Muller decoding, see MacWilliams, Florence Jessie, and Neil James Alexander Sloane.
|
|
* The theory of error-correcting codes codes @cite macwilliams1977theory
|
|
*
|
|
* @param[out] msg Array of size VEC_N1_SIZE_64 receiving the decoded message
|
|
* @param[in] cdw Array of size VEC_N1N2_SIZE_64 storing the received word
|
|
*/
|
|
void PQCLEAN_HQCRMRS192_CLEAN_reed_muller_decode(uint64_t *msg, const uint64_t *cdw) {
|
|
uint8_t *message_array = (uint8_t *) msg;
|
|
codeword *codeArray = (codeword *) cdw;
|
|
expandedCodeword expanded;
|
|
for (size_t i = 0 ; i < VEC_N1_SIZE_BYTES ; i++) {
|
|
// collect the codewords
|
|
expand_and_sum(&expanded, &codeArray[i * MULTIPLICITY]);
|
|
// apply hadamard transform
|
|
expandedCodeword transform;
|
|
hadamard(&expanded, &transform);
|
|
// fix the first entry to get the half Hadamard transform
|
|
transform[0] -= 64 * MULTIPLICITY;
|
|
// finish the decoding
|
|
message_array[i] = find_peaks(&transform);
|
|
}
|
|
}
|