2020-12-07 13:32:56 +01:00

507 lines
11 KiB
C

/*
* MIT License
*
* Copyright (C) Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "frodo_utils.h"
/**
* See header
*/
void frodo_pack(u_char *out, size_t outlen, uint16_t *in, size_t inlen,
u_char lsb)
{
size_t i = 0; /* whole bytes already filled in */
size_t j = 0; /* whole uint16_t already copied */
uint16_t w = 0; /* the leftover, not yet copied */
u_char bits = 0; /* the number of lsb in w */
memset(out, 0x00, outlen);
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0))))
{
u_char b = 0; /* bits in out[i] already filled in */
/**
* in: | | |********|********|
* ^
* j
* w : | ****|
* ^
* bits
* out:|**|**|**|**|**|**|**|**|* |
* ^^
* ib
*/
while (b < 8)
{
int nbits = min(8 - b, bits);
uint16_t mask = (1 << nbits) - 1;
u_char t = (w >> (bits - nbits)) & mask; /* the bits to copy from w to out */
out[i] = out[i] + (t << (8 - b - nbits));
b += nbits;
bits -= nbits;
/* not strictly necessary; mostly for debugging */
w &= ~(mask << bits);
if (bits == 0)
{
if (j < inlen)
{
w = in[j];
bits = lsb;
j++;
}
else
{
break; /* the input vector is exhausted */
}
}
}
if (b == 8)
{
i++; /* out[i] is filled in */
}
}
}
/**
* See header
*/
void frodo_unpack(uint16_t *out, size_t outlen, u_char *in, size_t inlen,
u_char lsb)
{
size_t i = 0; /* whole uint16_t already filled in */
size_t j = 0; /* whole bytes already copied */
u_char w = 0; /* the leftover, not yet copied */
u_char bits = 0; /* the number of lsb bits of w */
memset(out, 0, outlen * sizeof(uint16_t));
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0))))
{
u_char b = 0; /* bits in out[i] already filled in */
/**
* in: | | | | | | |**|**|...
* ^
* j
* w : | *|
* ^
* bits
* out:| *****| *****| *** | |...
* ^ ^
* i b
*/
while (b < lsb)
{
int nbits = min(lsb - b, bits);
uint16_t mask = (1 << nbits) - 1;
u_char t = (w >> (bits - nbits)) & mask; /* the bits to copy from w to out */
out[i] = out[i] + (t << (lsb - b - nbits));
b += nbits;
bits -= nbits;
/* not strictly necessary; mostly for debugging */
w &= ~(mask << bits);
if (bits == 0)
{
if (j < inlen)
{
w = in[j];
bits = 8;
j++;
}
else
{
break; /* the input vector is exhausted */
}
}
}
if (b == lsb)
{
i++; /* out[i] is filled in */
}
}
}
/**
* See header
*/
void frodo_sample_n(const frodo_params_t *params, uint16_t *s, size_t n)
{
int i, j;
for (i = 0; i < n; i++)
{
uint8_t sample = 0;
uint16_t prnd = s[i] >> 1; /* Drop the least significant bit */
uint8_t sign = s[i] & 0x1; /* Pick the least significant bit */
/* No need to compare with the last value */
for (j = 0; j < params->cdf_table_len - 1; j++)
{
/**
* Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise.
* Uses the fact that CDF_TABLE[j] and s fit in 15 bits
*/
sample += (uint16_t)(params->cdf_table[j] - prnd) >> 15;
}
/* Assuming that sign is either 0 or 1, flips sample iff sign = 1 */
s[i] = ((-sign) ^ sample) + sign;
}
}
/**
* Generate square matrix A by using SHAKE128
*/
static bool generate_matrix_by_shake(const frodo_params_t *params, uint16_t *A,
uint8_t *seed_A)
{
const uint32_t seed_A_len = params->seed_A_len;
const uint32_t n = params->n;
uint8_t seed_A_separated[2 + seed_A_len];
uint16_t *seed_A_origin = (uint16_t*)&seed_A_separated;
uint16_t i;
bool success = FALSE;
xof_t *xof;
memcpy(&seed_A_separated[2], seed_A, seed_A_len);
/* Instantiate a SHAKE-128 eXtended Output Function */
xof = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128);
if (!xof)
{
DBG1(DBG_LIB, "could not instantiate %N", ext_out_function_names,
XOF_SHAKE_128);
return FALSE;
}
for (i = 0; i < n; i++)
{
seed_A_origin[0] = i;
if (!xof->set_seed(xof, chunk_create(seed_A_separated, 2 + seed_A_len)))
{
goto err;
}
if (!xof->get_bytes(xof, 2*n, (uint8_t*)(A + i*n)))
{
goto err;
}
}
success = TRUE;
err:
xof->destroy(xof);
return success;
}
/**
* Generate square matrix A by using AES128
*/
static bool generate_matrix_by_aes(const frodo_params_t *params, uint16_t *A,
uint8_t *seed_A)
{
const uint32_t n = params->n;
const uint32_t A_len = n * n * sizeof(uint16_t);
crypter_t *crypter;
chunk_t A_chunk;
bool success = FALSE;
uint32_t block_len, step, k;
uint16_t i, j;
memset((uint8_t*)A, 0x00, A_len);
crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_ECB, 16);
if (!crypter)
{
DBG1(DBG_LIB, "could not instantiate AES_ECB-128");
return FALSE;
}
block_len = crypter->get_block_size(crypter);
step = block_len / sizeof(uint16_t);
if (!crypter->set_key(crypter, chunk_create(seed_A, params->seed_A_len)))
{
goto err;
}
/* ECB encryption */
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j += step)
{
k = i*n + j;
A[k] = i;
A[k + 1] = j;
}
}
A_chunk = chunk_create((uint8_t*)A, A_len);
if (!crypter->encrypt(crypter, A_chunk, chunk_empty, NULL))
{
goto err;
}
success = TRUE;
err:
crypter->destroy(crypter);
return success;
}
/**
* See header
*/
bool frodo_mul_add_as_plus_e(const frodo_params_t *params, uint16_t *out,
uint16_t *s, uint16_t *e, uint8_t *seed_A,
bool use_aes)
{
const uint32_t n = params->n;
const uint32_t nb = params->nb;
const uint32_t n_x_n = n * n;
int16_t A[n_x_n];
int i, j, k;
if (use_aes ? !generate_matrix_by_aes (params, A, seed_A) :
!generate_matrix_by_shake(params, A, seed_A))
{
return FALSE;
}
memcpy(out, e, nb * n * sizeof(uint16_t));
for (i = 0; i < n; i++)
{
/* Matrix multiplication-addition A*s + e */
for (k = 0; k < nb; k++)
{
uint16_t sum = 0;
for (j = 0; j < n; j++)
{
sum += A[i*n + j] * s[k*n + j];
}
/* Adding e. No need to reduce modulo 2^15,
* extra bits are taken care of during packing later on.
*/
out[i*nb + k] += sum;}
}
return TRUE;
}
/**
* See header
*/
bool frodo_mul_add_sa_plus_e(const frodo_params_t *params, uint16_t *out,
uint16_t *s, uint16_t *e, uint8_t *seed_A,
bool use_aes)
{
const uint32_t n = params->n;
const uint32_t nb = params->nb;
const uint32_t n_x_n = n * n;
int16_t A[n_x_n];
int i, j, k;
if (use_aes ? !generate_matrix_by_aes (params, A, seed_A) :
!generate_matrix_by_shake(params, A, seed_A))
{
return FALSE;
}
memcpy(out, e, nb * n * sizeof(uint16_t));
/* Matrix multiplication-addition A*s + e*/
for (i = 0; i < n; i++)
{
for (k = 0; k < nb; k++)
{
uint16_t sum = 0;
for (j = 0; j < n; j++)
{
sum += A[j*n + i] * s[k*n + j];
}
/* Adding e. No need to reduce modulo 2^15,
* extra bits are taken care of during packing later on.
*/
out[k*n + i] += sum;
}
}
return TRUE;
}
/**
* See header
*/
void frodo_mul_add_sb_plus_e(const frodo_params_t *params, uint16_t *out,
uint16_t *b, uint16_t *s, uint16_t *e)
{
const uint32_t n = params->n;
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
int i, j, k;
for (k = 0; k < nb; k++)
{
for (i = 0; i < nb; i++)
{
out[k*nb + i] = e[k*nb + i];
for (j = 0; j < n; j++)
{
out[k*nb + i] += s[k*n + j] * b[j*nb + i];
}
out[k*nb + i] = (uint32_t)(out[k*nb + i]) & ((1 << log_q) - 1);
}
}
}
/**
* See header
*/
void frodo_mul_bs(const frodo_params_t *params, uint16_t *out,
uint16_t *b, uint16_t *s)
{
const uint32_t n = params->n;
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
int i, j, k;
for (i = 0; i < nb; i++)
{
for (j = 0; j < nb; j++)
{
out[i*nb + j] = 0;
for (k = 0; k < n; k++)
{
out[i*nb + j] += b[i*n + k] * s[j*n + k];
}
out[i*nb + j] = (uint32_t)(out[i*nb + j]) & ((1 << log_q) - 1);
}
}
}
/**
* See header
*/
void frodo_add(const frodo_params_t *params, uint16_t *out,
uint16_t *a, uint16_t *b)
{
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
u_int i;
for (i = 0; i < (nb * nb); i++)
{
out[i] = (a[i] + b[i]) & ((1 << log_q) - 1);
}
}
void frodo_sub(const frodo_params_t *params, uint16_t *out,
uint16_t *a, uint16_t *b)
{
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
u_int i;
for (i = 0; i < (nb * nb); i++)
{
out[i] = (a[i] - b[i]) & ((1 << log_q) - 1);
}
}
/**
* See header
*/
void frodo_key_encode(const frodo_params_t *params, uint16_t *out, uint16_t *in)
{
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
const uint32_t extr_bits = params->extr_bits;
u_int i, j, npieces_word = 8;
u_int nwords = (nb * nb)/8;
uint64_t temp, mask = ((uint64_t)1 << extr_bits) - 1;
uint16_t* pos = out;
for (i = 0; i < nwords; i++)
{
temp = 0;
for(j = 0; j < extr_bits; j++)
{
temp |= ((uint64_t)((uint8_t*)in)[i*extr_bits + j]) << (8*j);
}
for (j = 0; j < npieces_word; j++)
{
*pos = (uint16_t)((temp & mask) << (log_q - extr_bits));
temp >>= extr_bits;
pos++;
}
}
}
/**
* See header
*/
void frodo_key_decode(const frodo_params_t *params, uint16_t *out, uint16_t *in)
{
const uint32_t nb = params->nb;
const uint32_t log_q = params->log_q;
const uint32_t extr_bits = params->extr_bits;
u_int i, j, index = 0, npieces_word = 8;
u_int nwords = (nb * nb) / 8;
uint16_t temp;
u_int maskex = ((uint16_t)1 << extr_bits) - 1;
u_int maskq = ((uint16_t)1 << log_q) - 1;
uint8_t *pos = (uint8_t*)out;
uint64_t templong;
for (i = 0; i < nwords; i++)
{
templong = 0;
for (j = 0; j < npieces_word; j++)
{
/* temp = floor(in*2^{-11}+0.5) */
temp = ((in[index] & maskq) +
(1 << (log_q - extr_bits - 1))) >> (log_q - extr_bits);
templong |= ((uint64_t)(temp & maskex)) << (extr_bits * j);
index++;
}
for(j = 0; j < extr_bits; j++)
{
pos[i*extr_bits + j] = (templong >> (8*j)) & 0xFF;
}
}
}