2016-04-18 19:30:19 +01:00
|
|
|
/* Copyright (c) 2016, Google Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <openssl/aes.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
extern uint16_t newhope_omegas_montgomery[];
|
|
|
|
extern uint16_t newhope_omegas_inv_montgomery[];
|
|
|
|
|
|
|
|
extern uint16_t newhope_psis_bitrev_montgomery[];
|
|
|
|
extern uint16_t newhope_psis_inv_montgomery[];
|
|
|
|
|
2016-05-28 00:49:29 +01:00
|
|
|
void NEWHOPE_POLY_frombytes(NEWHOPE_POLY* r, const uint8_t* a) {
|
2016-04-18 19:30:19 +01:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < PARAM_N / 4; i++) {
|
|
|
|
r->coeffs[4 * i + 0] =
|
|
|
|
a[7 * i + 0] | (((uint16_t)a[7 * i + 1] & 0x3f) << 8);
|
|
|
|
r->coeffs[4 * i + 1] = (a[7 * i + 1] >> 6) |
|
|
|
|
(((uint16_t)a[7 * i + 2]) << 2) |
|
|
|
|
(((uint16_t)a[7 * i + 3] & 0x0f) << 10);
|
|
|
|
r->coeffs[4 * i + 2] = (a[7 * i + 3] >> 4) |
|
|
|
|
(((uint16_t)a[7 * i + 4]) << 4) |
|
|
|
|
(((uint16_t)a[7 * i + 5] & 0x03) << 12);
|
|
|
|
r->coeffs[4 * i + 3] =
|
|
|
|
(a[7 * i + 5] >> 2) | (((uint16_t)a[7 * i + 6]) << 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_tobytes(uint8_t* r, const NEWHOPE_POLY* p) {
|
|
|
|
int i;
|
|
|
|
uint16_t t0, t1, t2, t3, m;
|
|
|
|
int16_t c;
|
|
|
|
for (i = 0; i < PARAM_N / 4; i++) {
|
|
|
|
t0 = newhope_barrett_reduce(
|
|
|
|
p->coeffs[4 * i + 0]); /* Make sure that coefficients
|
|
|
|
have only 14 bits */
|
|
|
|
t1 = newhope_barrett_reduce(p->coeffs[4 * i + 1]);
|
|
|
|
t2 = newhope_barrett_reduce(p->coeffs[4 * i + 2]);
|
|
|
|
t3 = newhope_barrett_reduce(p->coeffs[4 * i + 3]);
|
|
|
|
|
|
|
|
m = t0 - PARAM_Q;
|
|
|
|
c = m;
|
|
|
|
c >>= 15;
|
|
|
|
t0 = m ^ ((t0 ^ m) & c); /* Make sure that coefficients are in [0,q] */
|
|
|
|
|
|
|
|
m = t1 - PARAM_Q;
|
|
|
|
c = m;
|
|
|
|
c >>= 15;
|
|
|
|
t1 = m ^ ((t1 ^ m) & c); /* <Make sure that coefficients are in [0,q] */
|
|
|
|
|
|
|
|
m = t2 - PARAM_Q;
|
|
|
|
c = m;
|
|
|
|
c >>= 15;
|
|
|
|
t2 = m ^ ((t2 ^ m) & c); /* <Make sure that coefficients are in [0,q] */
|
|
|
|
|
|
|
|
m = t3 - PARAM_Q;
|
|
|
|
c = m;
|
|
|
|
c >>= 15;
|
|
|
|
t3 = m ^ ((t3 ^ m) & c); /* Make sure that coefficients are in [0,q] */
|
|
|
|
|
|
|
|
r[7 * i + 0] = t0 & 0xff;
|
|
|
|
r[7 * i + 1] = (t0 >> 8) | (t1 << 6);
|
|
|
|
r[7 * i + 2] = (t1 >> 2);
|
|
|
|
r[7 * i + 3] = (t1 >> 10) | (t2 << 4);
|
|
|
|
r[7 * i + 4] = (t2 >> 4);
|
|
|
|
r[7 * i + 5] = (t2 >> 12) | (t3 << 2);
|
|
|
|
r[7 * i + 6] = (t3 >> 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_uniform(NEWHOPE_POLY* a, const uint8_t* seed) {
|
|
|
|
/* The reference implementation uses SHAKE-128 here; this implementation uses
|
|
|
|
* AES-CTR. Use half the seed for the initialization vector and half for the
|
|
|
|
* key. */
|
|
|
|
#if SEED_LENGTH != 2 * AES_BLOCK_SIZE
|
|
|
|
#error "2 * seed length != AES_BLOCK_SIZE"
|
|
|
|
#endif
|
|
|
|
uint8_t ivec[AES_BLOCK_SIZE];
|
|
|
|
memcpy(ivec, &seed[SEED_LENGTH / 2], SEED_LENGTH / 2);
|
|
|
|
AES_KEY key;
|
|
|
|
AES_set_encrypt_key(seed, 8 * SEED_LENGTH / 2, &key);
|
|
|
|
|
|
|
|
/* AES state. */
|
|
|
|
uint8_t ecount[AES_BLOCK_SIZE];
|
|
|
|
memset(ecount, 0, AES_BLOCK_SIZE);
|
|
|
|
|
|
|
|
/* Encrypt a block of zeros just to get the random bytes. With luck, 2688
|
|
|
|
* bytes is enough. */
|
2016-04-27 00:03:47 +01:00
|
|
|
uint8_t buf[AES_BLOCK_SIZE * 168];
|
2016-04-18 19:30:19 +01:00
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
|
|
|
|
unsigned int block_num = 0;
|
|
|
|
AES_ctr128_encrypt(buf, buf, sizeof(buf), &key, ivec, ecount, &block_num);
|
|
|
|
|
|
|
|
size_t pos = 0, coeff_num = 0;
|
|
|
|
while (coeff_num < PARAM_N) {
|
|
|
|
/* Specialized for q = 12889 */
|
|
|
|
uint16_t val = (buf[pos] | ((uint16_t)buf[pos + 1] << 8)) & 0x3fff;
|
|
|
|
if (val < PARAM_Q) {
|
|
|
|
a->coeffs[coeff_num++] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += 2;
|
|
|
|
if (pos > sizeof(buf) - 2) {
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
AES_ctr128_encrypt(buf, buf, sizeof(buf), &key, ivec, ecount, &block_num);
|
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_getnoise(NEWHOPE_POLY* r) {
|
|
|
|
#if PARAM_K != 16
|
|
|
|
#error "poly_getnoise in poly.c only supports k=16"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t tp[PARAM_N];
|
|
|
|
|
|
|
|
/* The reference implementation calls ChaCha20 here. */
|
|
|
|
RAND_bytes((uint8_t *) tp, sizeof(tp));
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < PARAM_N; i++) {
|
|
|
|
const uint32_t t = tp[i];
|
|
|
|
|
|
|
|
size_t j;
|
|
|
|
uint32_t d = 0;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
d += (t >> j) & 0x01010101;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t a = ((d >> 8) & 0xff) + (d & 0xff);
|
|
|
|
const uint32_t b = (d >> 24) + ((d >> 16) & 0xff);
|
|
|
|
r->coeffs[i] = a + PARAM_Q - b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_pointwise(NEWHOPE_POLY* r, const NEWHOPE_POLY* a,
|
|
|
|
const NEWHOPE_POLY* b) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < PARAM_N; i++) {
|
|
|
|
uint16_t t = newhope_montgomery_reduce(3186 * b->coeffs[i]);
|
|
|
|
/* t is now in Montgomery domain */
|
|
|
|
r->coeffs[i] = newhope_montgomery_reduce(a->coeffs[i] * t);
|
|
|
|
/* r->coeffs[i] is back in normal domain */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_add(NEWHOPE_POLY* r, const NEWHOPE_POLY* a,
|
|
|
|
const NEWHOPE_POLY* b) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < PARAM_N; i++) {
|
|
|
|
r->coeffs[i] = newhope_barrett_reduce(a->coeffs[i] + b->coeffs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_ntt(NEWHOPE_POLY* r) {
|
|
|
|
newhope_mul_coefficients(r->coeffs, newhope_psis_bitrev_montgomery);
|
|
|
|
newhope_ntt((uint16_t *) r->coeffs, newhope_omegas_montgomery);
|
|
|
|
}
|
|
|
|
|
|
|
|
void newhope_poly_invntt(NEWHOPE_POLY* r) {
|
|
|
|
newhope_bitrev_vector(r->coeffs);
|
|
|
|
newhope_ntt((uint16_t *) r->coeffs, newhope_omegas_inv_montgomery);
|
|
|
|
newhope_mul_coefficients(r->coeffs, newhope_psis_inv_montgomery);
|
|
|
|
}
|