Clean up and optimize Frodo implementation (#60)

This commit is contained in:
Alex Parent 2016-12-07 19:32:25 -05:00 committed by Douglas Stebila
parent 36ae6bf418
commit c5382941ae
10 changed files with 251 additions and 431 deletions

View File

@ -47,7 +47,7 @@ endif
all: links lib tests
objs/%.o: src/%.c
objs/%.o: src/%.c | links
@mkdir -p $(@D)
$(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@
@ -95,7 +95,7 @@ $(KEX_RLWE_MSRLN16_OBJS): $(KEX_RLWE_MSRLN16_HEADERS)
# KEX_LWE_FRODO
KEX_LWE_FRODO_OBJS := $(addprefix objs/kex_lwe_frodo/, lwe.o kex_lwe_frodo.o lwe_noise.o)
KEX_LWE_FRODO_HEADERS := $(addprefix src/kex_lwe_frodo/, kex_lwe_frodo.h local.h)
KEX_LWE_FRODO_HEADERS := $(addprefix src/kex_lwe_frodo/, kex_lwe_frodo.h local.h kex_lwe_frodo_macrify.c lwe_macrify.c)
$(KEX_LWE_FRODO_OBJS): $(KEX_LWE_FRODO_HEADERS)
# KEX_SIDH_CLN16

View File

@ -18,7 +18,7 @@ OQS_KEX *OQS_KEX_new(OQS_RAND *rand, enum OQS_KEX_alg_name alg_name, const uint8
case OQS_KEX_alg_rlwe_newhope:
return OQS_KEX_rlwe_newhope_new(rand);
case OQS_KEX_alg_lwe_frodo:
return OQS_KEX_lwe_frodo_new(rand, seed, seed_len, named_parameters);
return OQS_KEX_lwe_frodo_new_recommended(rand, seed, seed_len, named_parameters);
case OQS_KEX_alg_sidh_cln16:
return OQS_KEX_sidh_cln16_new(rand);
default:

View File

@ -13,6 +13,7 @@
#include <oqs/kex.h>
#include <oqs/rand.h>
#include <oqs/common.h>
#include "kex_lwe_frodo.h"
#include "local.h"
@ -21,115 +22,16 @@
#include <stdio.h>
OQS_KEX *OQS_KEX_lwe_frodo_new(OQS_RAND *rand, const uint8_t *seed, const size_t seed_len, const char *named_parameters) {
OQS_KEX *k;
struct oqs_kex_lwe_frodo_params *params;
if ((seed_len == 0) || (seed == NULL)) {
return NULL;
}
k = malloc(sizeof(OQS_KEX));
if (k == NULL) {
goto err;
}
k->named_parameters = NULL;
k->method_name = NULL;
k->params = malloc(sizeof(struct oqs_kex_lwe_frodo_params));
if (NULL == k->params) {
goto err;
}
params = (struct oqs_kex_lwe_frodo_params *) k->params;
params->cdf_table = NULL;
params->seed = NULL;
params->param_name = NULL;
k->rand = rand;
k->ctx = NULL;
k->alice_priv_free = &OQS_KEX_lwe_frodo_alice_priv_free;
k->free = &OQS_KEX_lwe_frodo_free;
if (strcmp(named_parameters, "recommended") == 0) {
k->alice_0 = &OQS_KEX_lwe_frodo_alice_0_recommended;
k->bob = &OQS_KEX_lwe_frodo_bob_recommended;
k->alice_1 = &OQS_KEX_lwe_frodo_alice_1_recommended;
k->method_name = strdup("LWE Frodo recommended");
if (NULL == k->method_name) {
goto err;
}
k->estimated_classical_security = 144;
k->estimated_quantum_security = 130;
k->named_parameters = strdup(named_parameters);
if (k->named_parameters == NULL) {
goto err;
}
params->seed = malloc(seed_len);
if (NULL == params->seed) {
goto err;
}
memcpy(params->seed, seed, seed_len);
params->seed_len = seed_len;
params->param_name = strdup("recommended");
if (NULL == params->param_name) {
goto err;
}
params->log2_q = 15;
params->q = 1 << params->log2_q;
params->n = 752;
params->extracted_bits = 4;
params->nbar = 8;
params->key_bits = 256;
params->rec_hint_len = LWE_DIV_ROUNDUP(params->nbar * params->nbar, 8);
params->pub_len = LWE_DIV_ROUNDUP(params->n * params->nbar * params->log2_q, 8);
params->stripe_step = 8;
params->sampler_num = 12;
params->cdf_table_len = 6;
params->cdf_table = malloc(params->cdf_table_len * sizeof(uint16_t));
if (NULL == params->cdf_table) {
goto err;
}
uint16_t cdf_table_tmp[6] = {602, 1521, 1927, 2031, 2046, 2047};
memcpy(params->cdf_table, cdf_table_tmp, sizeof(cdf_table_tmp));
} else {
goto err;
}
return k;
err:
if (k) {
if (k->params) {
free(params->cdf_table);
free(params->seed);
free(params->param_name);
free(k->params);
}
free(k->named_parameters);
free(k->method_name);
free(k);
}
return NULL;
}
// pre-process code to obtain "recommended" functions
#include "recommended.h"
#define MACRIFY(NAME) NAME ## _recommended
#include "kex_lwe_frodo_macrify.c"
// undefine macros to avoid any confusion later
#include "recommended.h"
#undef MACRIFY
void OQS_KEX_lwe_frodo_alice_priv_free(UNUSED OQS_KEX *k, void *alice_priv) {
if (alice_priv) {
free(alice_priv);
}
free(alice_priv);
}
void OQS_KEX_lwe_frodo_free(OQS_KEX *k) {

View File

@ -12,7 +12,7 @@
#include <oqs/kex.h>
#include <oqs/rand.h>
OQS_KEX *OQS_KEX_lwe_frodo_new(OQS_RAND *rand, const uint8_t *seed, const size_t seed_len, const char *named_parameters);
OQS_KEX *OQS_KEX_lwe_frodo_new_recommended(OQS_RAND *rand, const uint8_t *seed, const size_t seed_len, const char *named_parameters);
int OQS_KEX_lwe_frodo_alice_0_recommended(OQS_KEX *k, void **alice_priv, uint8_t **alice_msg, size_t *alice_msg_len);
int OQS_KEX_lwe_frodo_bob_recommended(OQS_KEX *k, const uint8_t *alice_msg, const size_t alice_msg_len, uint8_t **bob_msg, size_t *bob_msg_len, uint8_t **key, size_t *key_len);

View File

@ -1,65 +1,133 @@
OQS_KEX *MACRIFY(OQS_KEX_lwe_frodo_new)(OQS_RAND *rand, const uint8_t *seed, const size_t seed_len, const char *named_parameters) {
OQS_KEX *k;
struct oqs_kex_lwe_frodo_params *params;
if ((seed_len == 0) || (seed == NULL)) {
return NULL;
}
k = malloc(sizeof(OQS_KEX));
if (k == NULL) {
goto err;
}
k->named_parameters = NULL;
k->method_name = NULL;
k->params = malloc(sizeof(struct oqs_kex_lwe_frodo_params));
if (NULL == k->params) {
goto err;
}
params = (struct oqs_kex_lwe_frodo_params *) k->params;
params->cdf_table = NULL;
params->seed = NULL;
params->param_name = NULL;
k->rand = rand;
k->ctx = NULL;
k->alice_priv_free = &OQS_KEX_lwe_frodo_alice_priv_free;
k->free = &OQS_KEX_lwe_frodo_free;
if (strcmp(named_parameters, "recommended") == 0) {
k->alice_0 = &OQS_KEX_lwe_frodo_alice_0_recommended;
k->bob = &OQS_KEX_lwe_frodo_bob_recommended;
k->alice_1 = &OQS_KEX_lwe_frodo_alice_1_recommended;
k->method_name = strdup("LWE Frodo recommended");
if (NULL == k->method_name) {
goto err;
}
k->estimated_classical_security = 144;
k->estimated_quantum_security = 130;
k->named_parameters = strdup(named_parameters);
if (k->named_parameters == NULL) {
goto err;
}
params->seed = malloc(seed_len);
if (NULL == params->seed) {
goto err;
}
memcpy(params->seed, seed, seed_len);
params->seed_len = seed_len;
params->param_name = strdup("recommended");
if (NULL == params->param_name) {
goto err;
}
params->log2_q = PARAMS_LOG2Q;
params->q = PARAMS_Q;
params->n = PARAMS_N;
params->extracted_bits = PARAMS_EXTRACTED_BITS;
params->nbar = PARAMS_NBAR;
params->key_bits = PARAMS_KEY_BITS;
params->rec_hint_len = PARAMS_REC_HINT_LENGTH;
params->pub_len = PARAMS_REC_PUB_LENGTH;
params->stripe_step = PARAMS_STRIPE_STEP;
params->sampler_num = 12;
params->cdf_table_len = 6;
params->cdf_table = malloc(params->cdf_table_len * sizeof(uint16_t));
if (NULL == params->cdf_table) {
goto err;
}
uint16_t cdf_table_tmp[6] = {602, 1521, 1927, 2031, 2046, 2047};
memcpy(params->cdf_table, cdf_table_tmp, sizeof(cdf_table_tmp));
} else {
goto err;
}
return k;
err:
OQS_KEX_lwe_frodo_free(k);
return NULL;
}
int MACRIFY(OQS_KEX_lwe_frodo_alice_0)(OQS_KEX *k, void **alice_priv, uint8_t **alice_msg, size_t *alice_msg_len) {
int ret;
struct oqs_kex_lwe_frodo_params *params = (struct oqs_kex_lwe_frodo_params *) k->params;
uint16_t *b = NULL, *e = NULL;
*alice_priv = NULL;
*alice_msg = NULL;
/* allocate private key, error, and outgoing message */
*alice_priv = malloc(params->n * params->nbar * sizeof(uint16_t));
*alice_priv = malloc(PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
if (*alice_priv == NULL) {
goto err;
}
b = (uint16_t *) malloc(params->n * params->nbar * sizeof(uint16_t));
if (b == NULL) {
goto err;
}
e = (uint16_t *) malloc(params->n * params->nbar * sizeof(uint16_t));
if (e == NULL) {
goto err;
}
*alice_msg = malloc(params->pub_len);
uint16_t b[PARAMS_N * PARAMS_NBAR];
uint16_t e[PARAMS_N * PARAMS_NBAR];
*alice_msg = malloc(PARAMS_REC_PUB_LENGTH);
*alice_msg_len = PARAMS_REC_PUB_LENGTH;
if (*alice_msg == NULL) {
goto err;
}
/* generate S and E */
ret = oqs_kex_lwe_frodo_sample_n(*alice_priv, params->n * params->nbar, params, k->rand);
if (ret != 1) {
goto err;
}
ret = oqs_kex_lwe_frodo_sample_n(e, params->n * params->nbar, params, k->rand);
if (ret != 1) {
goto err;
}
oqs_kex_lwe_frodo_sample_n(*alice_priv, PARAMS_N * PARAMS_NBAR, params, k->rand);
oqs_kex_lwe_frodo_sample_n(e, PARAMS_N * PARAMS_NBAR, params, k->rand);
/* compute B = AS + E */
ret = MACRIFY(oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly)(b, *alice_priv, e, params);
if (ret != 1) {
goto err;
}
oqs_kex_lwe_frodo_pack(*alice_msg, params->pub_len, b, params->n * params->nbar, params->log2_q);
MACRIFY(oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly)(b, *alice_priv, e, params);
oqs_kex_lwe_frodo_pack(*alice_msg, PARAMS_REC_PUB_LENGTH, b, PARAMS_N * PARAMS_NBAR, PARAMS_LOG2Q);
*alice_msg_len = params->pub_len;
ret = 1;
goto cleanup;
err:
ret = 0;
OQS_MEM_cleanse(e, sizeof(e));
free(*alice_msg);
*alice_msg = NULL;
free(*alice_priv);
*alice_priv = NULL;
ret = 0;
cleanup:
free(e);
free(b);
return ret;
}
int MACRIFY(OQS_KEX_lwe_frodo_bob)(OQS_KEX *k, const uint8_t *alice_msg, const size_t alice_msg_len, uint8_t **bob_msg, size_t *bob_msg_len, uint8_t **key, size_t *key_len) {
@ -68,78 +136,46 @@ int MACRIFY(OQS_KEX_lwe_frodo_bob)(OQS_KEX *k, const uint8_t *alice_msg, const s
struct oqs_kex_lwe_frodo_params *params = (struct oqs_kex_lwe_frodo_params *) k->params;
uint16_t *bob_priv = NULL;
uint8_t *bob_rec = NULL;
uint16_t *b = NULL, *bprime = NULL, *eprime = NULL, *eprimeprime = NULL;
uint16_t *v = NULL;
*bob_msg = NULL;
*key = NULL;
/* check length of other party's public key */
if (alice_msg_len != params->pub_len) {
if (alice_msg_len != PARAMS_REC_PUB_LENGTH) {
goto err;
}
/* allocate private key, errors, outgoing message, and key */
bob_priv = malloc(params->n * params->nbar * sizeof(uint16_t));
if (bob_priv == NULL) {
goto err;
}
bprime = (uint16_t *) malloc(params->n * params->nbar * sizeof(uint16_t));
if (bprime == NULL) {
goto err;
}
eprime = (uint16_t *) malloc(params->n * params->nbar * sizeof(uint16_t));
if (eprime == NULL) {
goto err;
}
eprimeprime = (uint16_t *) malloc(params->nbar * params->nbar * sizeof(uint16_t));
if (eprimeprime == NULL) {
goto err;
}
b = (uint16_t *) malloc(params->n * params->nbar * sizeof(uint16_t));
if (b == NULL) {
goto err;
}
v = (uint16_t *) malloc(params->nbar * params->nbar * sizeof(uint16_t));
if (v == NULL) {
goto err;
}
*bob_msg = malloc(params->pub_len + params->rec_hint_len);
uint16_t bob_priv[PARAMS_N * PARAMS_NBAR];
uint16_t bprime[PARAMS_N * PARAMS_NBAR];
uint16_t eprime[PARAMS_N * PARAMS_NBAR];
uint16_t eprimeprime[PARAMS_N * PARAMS_NBAR];
uint16_t b[PARAMS_N * PARAMS_NBAR];
uint16_t v[PARAMS_N * PARAMS_NBAR];
*bob_msg = malloc(PARAMS_REC_PUB_LENGTH + PARAMS_REC_HINT_LENGTH);
if (*bob_msg == NULL) {
goto err;
}
bob_rec = *bob_msg + params->pub_len;
*key = malloc(params->key_bits >> 3);
bob_rec = *bob_msg + PARAMS_REC_PUB_LENGTH;
*key = malloc(PARAMS_KEY_BYTES);
if (*key == NULL) {
goto err;
}
/* generate S' and E' */
ret = oqs_kex_lwe_frodo_sample_n(bob_priv, params->n * params->nbar, params, k->rand);
if (ret != 1) {
goto err;
}
ret = oqs_kex_lwe_frodo_sample_n(eprime, params->n * params->nbar, params, k->rand);
if (ret != 1) {
goto err;
}
oqs_kex_lwe_frodo_sample_n(bob_priv, PARAMS_N * PARAMS_NBAR, params, k->rand);
oqs_kex_lwe_frodo_sample_n(eprime, PARAMS_N * PARAMS_NBAR, params, k->rand);
/* compute B' = S'A + E' */
ret = MACRIFY(oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly)(bprime, bob_priv, eprime, params);
if (ret != 1) {
goto err;
}
oqs_kex_lwe_frodo_pack(*bob_msg, params->pub_len, bprime, params->n * params->nbar, params->log2_q);
MACRIFY(oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly)(bprime, bob_priv, eprime, params);
oqs_kex_lwe_frodo_pack(*bob_msg, PARAMS_REC_PUB_LENGTH, bprime, PARAMS_N * PARAMS_NBAR, PARAMS_LOG2Q);
/* generate E'' */
ret = oqs_kex_lwe_frodo_sample_n(eprimeprime, params->nbar * params->nbar, params, k->rand);
if (ret != 1) {
goto err;
}
oqs_kex_lwe_frodo_sample_n(eprimeprime, PARAMS_NBAR * PARAMS_NBAR, params, k->rand);
/* unpack B */
oqs_kex_lwe_frodo_unpack(b, params->n * params->nbar, alice_msg, alice_msg_len, params->log2_q);
oqs_kex_lwe_frodo_unpack(b, PARAMS_N * PARAMS_NBAR, alice_msg, alice_msg_len, PARAMS_LOG2Q);
/* compute V = S'B + E'' */
MACRIFY(oqs_kex_lwe_frodo_mul_add_sb_plus_e)(v, b, bob_priv, eprimeprime);
@ -150,8 +186,8 @@ int MACRIFY(OQS_KEX_lwe_frodo_bob)(OQS_KEX *k, const uint8_t *alice_msg, const s
/* compute K = round(V)_{2^B} */
MACRIFY(oqs_kex_lwe_frodo_round2)(*key, v);
*bob_msg_len = params->pub_len + params->rec_hint_len;
*key_len = params->key_bits >> 3;
*bob_msg_len = PARAMS_REC_PUB_LENGTH + PARAMS_REC_HINT_LENGTH;
*key_len = PARAMS_KEY_BYTES;
ret = 1;
goto cleanup;
@ -160,85 +196,58 @@ err:
ret = 0;
free(*bob_msg);
*bob_msg = NULL;
if (*key != NULL) {
memset(*key, 0, params->key_bits >> 3);
}
free(*key);
OQS_MEM_secure_free(*key, PARAMS_KEY_BYTES);
*key = NULL;
cleanup:
free(bob_priv);
if (eprime != NULL) {
memset(eprime, 0, params->n * params->nbar * sizeof(uint16_t));
}
free(bprime);
free(eprime);
if (eprimeprime != NULL) {
memset(eprimeprime, 0, params->nbar * params->nbar * sizeof(uint16_t));
}
free(eprimeprime);
free(b);
if (v != NULL) {
memset(v, 0, params->nbar * params->nbar * sizeof(uint16_t));
}
free(v);
OQS_MEM_cleanse(eprime, sizeof(eprime));
OQS_MEM_cleanse(eprimeprime, sizeof(eprimeprime));
OQS_MEM_cleanse(v, sizeof(v));
return ret;
}
int MACRIFY(OQS_KEX_lwe_frodo_alice_1)(OQS_KEX *k, const void *alice_priv, const uint8_t *bob_msg, const size_t bob_msg_len, uint8_t **key, size_t *key_len) {
int MACRIFY(OQS_KEX_lwe_frodo_alice_1)(UNUSED OQS_KEX *k, const void *alice_priv, const uint8_t *bob_msg, const size_t bob_msg_len, uint8_t **key, size_t *key_len) {
int ret;
struct oqs_kex_lwe_frodo_params *params = (struct oqs_kex_lwe_frodo_params *) k->params;
uint16_t *bprime = NULL, *w = NULL;
*key = NULL;
/* check length of other party's public key */
if (bob_msg_len != params->pub_len + params->rec_hint_len) {
if (bob_msg_len != PARAMS_REC_PUB_LENGTH + PARAMS_REC_HINT_LENGTH) {
goto err;
}
/* allocate working values and session key */
bprime = malloc(params->n * params->nbar * sizeof(uint16_t));
if (bprime == NULL) {
goto err;
}
w = malloc(params->nbar * params->nbar * sizeof(uint16_t));
if (w == NULL) {
goto err;
}
*key = malloc(params->key_bits >> 3);
uint16_t bprime[PARAMS_N * PARAMS_NBAR];
uint16_t w[PARAMS_N * PARAMS_NBAR];
*key = malloc(PARAMS_KEY_BYTES);
if (*key == NULL) {
goto err;
}
/* unpack B' */
oqs_kex_lwe_frodo_unpack(bprime, params->n * params->nbar, bob_msg, params->pub_len, params->log2_q);
oqs_kex_lwe_frodo_unpack(bprime, PARAMS_N * PARAMS_NBAR, bob_msg, PARAMS_REC_PUB_LENGTH, PARAMS_LOG2Q);
/* compute W = B'S */
MACRIFY(oqs_kex_lwe_frodo_mul_bs)(w, bprime, (uint16_t *) alice_priv);
/* compute K = rec(B'S, C) */
const uint8_t *bob_rec = bob_msg + params->pub_len;
const uint8_t *bob_rec = bob_msg + PARAMS_REC_PUB_LENGTH;
MACRIFY(oqs_kex_lwe_frodo_reconcile)(*key, w, bob_rec);
*key_len = params->key_bits >> 3;
*key_len = PARAMS_KEY_BYTES;
ret = 1;
goto cleanup;
err:
ret = 0;
memset(key, 0, params->key_bits >> 3);
free(*key);
OQS_MEM_secure_free(*key, PARAMS_KEY_BYTES);
*key = NULL;
cleanup:
free(w);
free(bprime);
return ret;
}

View File

@ -32,10 +32,10 @@ void oqs_kex_lwe_frodo_key_round_hints(uint16_t *vec, const size_t length, const
void oqs_kex_lwe_frodo_pack(unsigned char *out, const size_t outlen, const uint16_t *in, const size_t inlen, const unsigned char lsb);
void oqs_kex_lwe_frodo_unpack(uint16_t *out, const size_t outlen, const unsigned char *in, const size_t inlen, const unsigned char lsb);
int oqs_kex_lwe_frodo_sample_n(uint16_t *s, const size_t n, struct oqs_kex_lwe_frodo_params *params, OQS_RAND *rand);
void oqs_kex_lwe_frodo_sample_n(uint16_t *s, const size_t n, struct oqs_kex_lwe_frodo_params *params, OQS_RAND *rand);
int oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly_recommended(uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params);
int oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly_recommended(uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params);
void oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly_recommended(uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params);
void oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly_recommended(uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params);
void oqs_kex_lwe_frodo_mul_add_sb_plus_e_recommended(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e);
void oqs_kex_lwe_frodo_mul_bs_recommended(uint16_t *out, const uint16_t *b, const uint16_t *s);

View File

@ -71,7 +71,7 @@ void oqs_kex_lwe_frodo_pack(unsigned char *out, const size_t outlen, const uint1
int nbits = min(8 - b, bits);
uint16_t mask = (1 << nbits) - 1;
unsigned char t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out
out[i] = out[i] + (t << (8 - b - nbits));
out[i] += t << (8 - b - nbits);
b += nbits;
bits -= nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
@ -119,7 +119,7 @@ void oqs_kex_lwe_frodo_unpack(uint16_t *out, const size_t outlen, const unsigned
int nbits = min(lsb - b, bits);
uint16_t mask = (1 << nbits) - 1;
unsigned char t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out
out[i] = out[i] + (t << (lsb - b - nbits));
out[i] += t << (lsb - b - nbits);
b += nbits;
bits -= nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
@ -140,27 +140,11 @@ void oqs_kex_lwe_frodo_unpack(uint16_t *out, const size_t outlen, const unsigned
}
}
#define LWE_DIV_ROUNDUP(x, y) (((x) + (y)-1) / y)
// define parameters for "recommended" parameter set
#define PARAMS_N 752
#define PARAMS_NBAR 8
#define PARAMS_LOG2Q 15
#define PARAMS_Q (1 << PARAMS_LOG2Q)
#define PARAMS_EXTRACTED_BITS 4
#define PARAMS_KEY_BITS 256
#define PARAMS_STRIPE_STEP 8
#define PARAMS_REC_HINT_LENGTH LWE_DIV_ROUNDUP(PARAMS_NBAR * PARAMS_NBAR, 8)
#include "recommended.h"
// pre-process code to obtain "recommended" functions
#define MACRIFY(NAME) NAME ## _recommended
#include "lwe_macrify.c"
// undefine macros to avoid any confusion later
#undef PARAMS_N
#undef PARAMS_NBAR
#undef PARAMS_LOG2Q
#undef PARAMS_Q
#undef PARAMS_EXTRACTED_BITS
#undef PARAMS_KEY_BITS
#undef PARAMS_STRIPE_STEP
#undef PARAMS_REC_HINT_LENGTH
#include "recommended.h"
#undef MACRIFY

View File

@ -1,8 +1,7 @@
// [.]_2
void MACRIFY(oqs_kex_lwe_frodo_round2)(unsigned char *out, uint16_t *in) {
oqs_kex_lwe_frodo_key_round(in, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS);
int i;
for (i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
for (int i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
in[i] >>= PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS; // drop bits that were zeroed out
}
@ -11,7 +10,6 @@ void MACRIFY(oqs_kex_lwe_frodo_round2)(unsigned char *out, uint16_t *in) {
}
void MACRIFY(oqs_kex_lwe_frodo_crossround2)(unsigned char *out, const uint16_t *in) {
int i;
// out should have enough space for N_BAR * N_BAR bits
memset((unsigned char *)out, 0, PARAMS_REC_HINT_LENGTH);
@ -19,7 +17,7 @@ void MACRIFY(oqs_kex_lwe_frodo_crossround2)(unsigned char *out, const uint16_t *
uint16_t half = whole >> 1;
uint16_t mask = whole - 1;
for (i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
for (int i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
uint16_t remainder = in[i] & mask;
out[i / 8] += (remainder >= half) << (i % 8);
}
@ -27,44 +25,24 @@ void MACRIFY(oqs_kex_lwe_frodo_crossround2)(unsigned char *out, const uint16_t *
void MACRIFY(oqs_kex_lwe_frodo_reconcile)(unsigned char *out, uint16_t *w, const unsigned char *hint) {
oqs_kex_lwe_frodo_key_round_hints(w, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS, hint);
int i;
for (i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
for (int i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) {
w[i] >>= PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS; // drop bits that were zeroed out
}
oqs_kex_lwe_frodo_pack(out, PARAMS_KEY_BITS / 8, w, PARAMS_NBAR * PARAMS_NBAR, PARAMS_EXTRACTED_BITS);
}
// Generate-and-multiply: generate A row-wise, multiply by s on the right.
int MACRIFY(oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly)(uint16_t *out, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params) {
void MACRIFY(oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly)(uint16_t *out, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params) {
// A (N x N)
// s,e (N x N_BAR)
// out = A * s + e (N x N_BAR)
int i, j, k;
int ret = 0;
uint16_t *a_row = NULL;
uint16_t *s_transpose = NULL;
for (i = 0; i < PARAMS_N; i++) {
for (j = 0; j < PARAMS_NBAR; j++) {
out[i * PARAMS_NBAR + j] = e[i * PARAMS_NBAR + j];
}
}
size_t a_rowlen = PARAMS_N * sizeof(int16_t);
a_row = (uint16_t *) malloc(a_rowlen);
if (a_row == NULL) {
goto err;
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
// transpose s to store it in the column-major order
s_transpose = (uint16_t *) malloc(PARAMS_NBAR * PARAMS_N * sizeof(int16_t));
if (s_transpose == NULL) {
goto err;
}
for (j = 0; j < PARAMS_N; j++) {
for (k = 0; k < PARAMS_NBAR; k++) {
uint16_t s_transpose[PARAMS_NBAR * PARAMS_N];
for (int j = 0; j < PARAMS_N; j++) {
for (int k = 0; k < PARAMS_NBAR; k++) {
s_transpose[k * PARAMS_N + j] = s[j * PARAMS_NBAR + k];
}
}
@ -73,127 +51,78 @@ int MACRIFY(oqs_kex_lwe_frodo_mul_add_as_plus_e_on_the_fly)(uint16_t *out, const
void *aes_key_schedule = NULL;
OQS_AES128_load_schedule(params->seed, &aes_key_schedule, 1);
for (i = 0; i < PARAMS_N; i++) {
for (int i = 0; i < PARAMS_N; i++) {
uint16_t a_row[PARAMS_N] = {0};
// go through A's rows
memset(a_row, 0, a_rowlen);
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
for (int j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
// Loading values in the little-endian order!
a_row[j] = i;
a_row[j + 1] = j;
}
OQS_AES128_ECB_enc_sch((uint8_t *) a_row, a_rowlen, aes_key_schedule, (uint8_t *) a_row);
OQS_AES128_ECB_enc_sch((uint8_t *) a_row, sizeof(a_row), aes_key_schedule, (uint8_t *) a_row);
for (k = 0; k < PARAMS_NBAR; k++) {
for (int k = 0; k < PARAMS_NBAR; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
for (int j = 0; j < PARAMS_N; j++) {
// matrix-vector multiplication happens here
sum += a_row[j] * s_transpose[k * PARAMS_N + j];
}
out[i * PARAMS_NBAR + k] += sum;
out[i * PARAMS_NBAR + k] %= PARAMS_Q;
//Equivalent to %= PARAMS_Q if PARAMS_Q is a power of 2
out[i * PARAMS_NBAR + k] &= PARAMS_Q - 1;
}
}
OQS_AES128_free_schedule(aes_key_schedule);
ret = 1;
goto cleanup;
err:
memset(out, 0, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
cleanup:
if (a_row != NULL) {
memset(a_row, 0, a_rowlen);
free(a_row);
}
if (s_transpose != NULL) {
memset(s_transpose, 0, PARAMS_NBAR * PARAMS_N * sizeof(int16_t));
free(s_transpose);
}
return ret;
}
// Generate-and-multiply: generate A column-wise, multiply by s' on the left.
int MACRIFY(oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly)(uint16_t *out, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params) {
void MACRIFY(oqs_kex_lwe_frodo_mul_add_sa_plus_e_on_the_fly)(uint16_t *out, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params) {
// a (N x N)
// s',e' (N_BAR x N)
// out = s'a + e' (N_BAR x N)
int i, j, k, kk;
int ret = 0;
uint16_t *a_cols = NULL;
uint16_t *a_cols_t = NULL;
for (i = 0; i < PARAMS_NBAR; i++) {
for (j = 0; j < PARAMS_N; j++) {
out[i * PARAMS_N + j] = e[i * PARAMS_N + j];
}
}
size_t a_colslen = PARAMS_N * PARAMS_STRIPE_STEP * sizeof(int16_t);
// a_cols stores 8 columns of A at a time.
a_cols = (uint16_t *) malloc(a_colslen);
a_cols_t = (uint16_t *) malloc(a_colslen); // a_cols transposed (stored in the column-major order).
if ((a_cols == NULL) || (a_cols_t == NULL)) {
goto err;
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
assert(params->seed_len == 16);
void *aes_key_schedule = NULL;
OQS_AES128_load_schedule(params->seed, &aes_key_schedule, 1);
for (kk = 0; kk < PARAMS_N; kk += PARAMS_STRIPE_STEP) {
for (int kk = 0; kk < PARAMS_N; kk += PARAMS_STRIPE_STEP) {
// Go through A's columns, 8 (== PARAMS_STRIPE_STEP) columns at a time.
memset(a_cols, 0, a_colslen);
for (i = 0; i < PARAMS_N; i++) {
// a_cols stores 8 columns of A at a time.
uint16_t a_cols[PARAMS_N * PARAMS_STRIPE_STEP] = {0};
for (int i = 0; i < PARAMS_N; i++) {
// Loading values in the little-endian order!
a_cols[i * PARAMS_STRIPE_STEP] = i;
a_cols[i * PARAMS_STRIPE_STEP + 1] = kk;
}
OQS_AES128_ECB_enc_sch((uint8_t *) a_cols, a_colslen, aes_key_schedule, (uint8_t *) a_cols);
OQS_AES128_ECB_enc_sch((uint8_t *) a_cols, sizeof(a_cols), aes_key_schedule, (uint8_t *) a_cols);
// transpose a_cols to have access to it in the column-major order.
for (i = 0; i < PARAMS_N; i++)
for (k = 0; k < PARAMS_STRIPE_STEP; k++) {
uint16_t a_cols_t[PARAMS_N * PARAMS_STRIPE_STEP];
for (int i = 0; i < PARAMS_N; i++) {
for (int k = 0; k < PARAMS_STRIPE_STEP; k++) {
a_cols_t[k * PARAMS_N + i] = a_cols[i * PARAMS_STRIPE_STEP + k];
}
}
for (i = 0; i < PARAMS_NBAR; i++)
for (k = 0; k < PARAMS_STRIPE_STEP; k++) {
for (int i = 0; i < PARAMS_NBAR; i++) {
for (int k = 0; k < PARAMS_STRIPE_STEP; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
for (int j = 0; j < PARAMS_N; j++) {
sum += s[i * PARAMS_N + j] * a_cols_t[k * PARAMS_N + j];
}
out[i * PARAMS_N + kk + k] += sum;
out[i * PARAMS_N + kk + k] %= PARAMS_Q;
out[i * PARAMS_N + kk + k] &= PARAMS_Q - 1; //Works as long as PARAMS_Q is a power of 2
}
}
}
OQS_AES128_free_schedule(aes_key_schedule);
ret = 1;
goto cleanup;
err:
memset(out, 0, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
cleanup:
if (a_cols != NULL) {
memset(a_cols, 0, a_colslen);
free(a_cols);
}
if (a_cols_t != NULL) {
memset(a_cols_t, 0, a_colslen);
free(a_cols_t);
}
return ret;
}
// multiply by s on the right
@ -201,14 +130,13 @@ void MACRIFY(oqs_kex_lwe_frodo_mul_bs)(uint16_t *out, const uint16_t *b, const u
// b (N_BAR x N)
// s (N x N_BAR)
// out = bs
int i, j, k;
for (i = 0; i < PARAMS_NBAR; i++) {
for (j = 0; j < PARAMS_NBAR; j++) {
out[i * PARAMS_NBAR + j] = 0;
for (k = 0; k < PARAMS_N; k++) {
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[k * PARAMS_NBAR + j];
for (int i = 0; i < PARAMS_NBAR; i++) {
for (int j = 0; j < PARAMS_NBAR; j++) {
uint16_t sum = 0;
for (int k = 0; k < PARAMS_N; k++) {
sum += b[i * PARAMS_N + k] * s[k * PARAMS_NBAR + j];
}
out[i * PARAMS_NBAR + j] %= PARAMS_Q; // not really necessary since LWE_Q is a power of 2.
out[i * PARAMS_NBAR + j] = sum & (PARAMS_Q - 1);
}
}
}
@ -219,15 +147,15 @@ void MACRIFY(oqs_kex_lwe_frodo_mul_add_sb_plus_e)(uint16_t *out, const uint16_t
// s (N_BAR x N)
// e (N_BAR x N_BAR)
// out = sb + e
int i, j, k;
for (k = 0; k < PARAMS_NBAR; k++) {
for (i = 0; i < PARAMS_NBAR; i++) {
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i];
for (j = 0; j < PARAMS_N; j++) {
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i];
memcpy(out, e, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
for (int k = 0; k < PARAMS_NBAR; k++) {
for (int i = 0; i < PARAMS_NBAR; i++) {
uint16_t sum = 0;
for (int j = 0; j < PARAMS_N; j++) {
sum += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i];
}
out[k * PARAMS_NBAR + i] %= PARAMS_Q; // not really necessary since LWE_Q is a power of 2.
out[k * PARAMS_NBAR + i] += sum;
out[k * PARAMS_NBAR + i] &= PARAMS_Q - 1; // not really necessary since LWE_Q is a power of 2.
}
}
}

View File

@ -5,33 +5,26 @@
#include <string.h>
#include <oqs/rand.h>
#include <oqs/common.h>
#include "local.h"
static int lwe_sample_n_inverse_8(uint16_t *s, const size_t n, const uint8_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
static void lwe_sample_n_inverse_8(uint16_t *s, const size_t n, const uint8_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
/* Fills vector s with n samples from the noise distribution which requires
* 8 bits to sample. The distribution is specified by its CDF. Super-constant
* timing: the CDF table is ingested for every sample.
*/
size_t rndlen = n;
uint8_t *rndvec = (uint8_t *)malloc(rndlen);
if (rndvec == NULL) {
fprintf(stderr, "malloc failure\n");
return 0;
}
OQS_RAND_n(rand, rndvec, rndlen);
uint8_t rndvec[n];
OQS_RAND_n(rand, rndvec, sizeof(rndvec));
size_t i, j;
for (i = 0; i < n; ++i) {
for (size_t i = 0; i < n; ++i) {
uint8_t sample = 0;
uint8_t rnd = rndvec[i] >> 1; // drop the least significant bit
uint8_t sign = rndvec[i] & 0x1; // pick the least significant bit
// No need to compare with the last value.
for (j = 0; j < cdf_table_len - 1; j++) {
for (size_t j = 0; j < cdf_table_len - 1; j++) {
// Constant time comparison: 1 if cdf_table[j] < rnd, 0 otherwise.
// Critically uses the fact that cdf_table[j] and rnd fit in 7 bits.
sample += (uint8_t)(cdf_table[j] - rnd) >> 7;
@ -39,30 +32,19 @@ static int lwe_sample_n_inverse_8(uint16_t *s, const size_t n, const uint8_t *cd
// Assuming that sign is either 0 or 1, flips sample iff sign = 1
s[i] = ((-sign) ^ sample) + sign;
}
memset(rndvec, 0, rndlen);
free(rndvec);
return 1;
OQS_MEM_cleanse(rndvec, sizeof(rndvec));
}
static int lwe_sample_n_inverse_12(uint16_t *s, const size_t n, const uint16_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
static void lwe_sample_n_inverse_12(uint16_t *s, const size_t n, const uint16_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
/* Fills vector s with n samples from the noise distribution which requires
* 12 bits to sample. The distribution is specified by its CDF. Super-constant
* timing: the CDF table is ingested for every sample.
*/
size_t rndlen = 3 * ((n + 1) / 2); // 12 bits of unif randomness per output element
uint8_t rnd[3 * ((n + 1) / 2)]; // 12 bits of unif randomness per output element
OQS_RAND_n(rand, rnd, sizeof(rnd));
uint8_t *rnd = (uint8_t *)malloc(rndlen);
if (rnd == NULL) {
fprintf(stderr, "malloc failure\n");
return 0;
}
OQS_RAND_n(rand, rnd, rndlen);
size_t i;
for (i = 0; i < n; i += 2) { // two output elements at a time
for (size_t i = 0; i < n; i += 2) { // two output elements at a time
uint8_t *pRnd = (rnd + 3 * i / 2);
uint16_t rnd1 = (((pRnd[0] << 8) + pRnd[1]) & 0xFFE0) >> 5; // first 11 bits (0..10)
@ -71,9 +53,8 @@ static int lwe_sample_n_inverse_12(uint16_t *s, const size_t n, const uint16_t *
uint8_t sample1 = 0;
uint8_t sample2 = 0;
size_t j;
// No need to compare with the last value.
for (j = 0; j < cdf_table_len - 1; j++) {
for (size_t j = 0; j < cdf_table_len - 1; j++) {
// Constant time comparison: 1 if LWE_CDF_TABLE[j] < rnd1, 0 otherwise.
// Critically uses the fact that LWE_CDF_TABLE[j] and rnd1 fit in 15 bits.
sample1 += (uint16_t)(cdf_table[j] - rnd1) >> 15;
@ -90,34 +71,25 @@ static int lwe_sample_n_inverse_12(uint16_t *s, const size_t n, const uint16_t *
s[i + 1] = ((-sign2) ^ sample2) + sign2;
}
}
memset(rnd, 0, rndlen);
free(rnd);
return 1;
OQS_MEM_cleanse(rnd, sizeof(rnd));
}
static int lwe_sample_n_inverse_16(uint16_t *s, const size_t n, const uint16_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
static void lwe_sample_n_inverse_16(uint16_t *s, const size_t n, const uint16_t *cdf_table, const size_t cdf_table_len, OQS_RAND *rand) {
/* Fills vector s with n samples from the noise distribution which requires
* 16 bits to sample. The distribution is specified by its CDF. Super-constant
* timing: the CDF table is ingested for every sample.
*/
size_t rndlen = 2 * n;
uint16_t *rndvec = (uint16_t *)malloc(rndlen);
if (rndvec == NULL) {
return 0;
}
OQS_RAND_n(rand, (uint8_t *) rndvec, rndlen);
uint16_t rndvec[ n ];
OQS_RAND_n(rand, (uint8_t *) rndvec, sizeof(rndvec));
size_t i, j;
for (i = 0; i < n; ++i) {
for (size_t i = 0; i < n; ++i) {
uint8_t sample = 0;
uint16_t rnd = rndvec[i] >> 1; // drop the least significant bit
uint8_t sign = rndvec[i] & 0x1; // pick the least significant bit
// No need to compare with the last value.
for (j = 0; j < cdf_table_len - 1; j++) {
for (size_t j = 0; j < cdf_table_len - 1; j++) {
// Constant time comparison: 1 if LWE_CDF_TABLE[j] < rnd, 0 otherwise.
// Critically uses the fact that LWE_CDF_TABLE[j] and rnd fit in 15 bits.
sample += (uint16_t)(cdf_table[j] - rnd) >> 15;
@ -125,34 +97,30 @@ static int lwe_sample_n_inverse_16(uint16_t *s, const size_t n, const uint16_t *
// Assuming that sign is either 0 or 1, flips sample iff sign = 1
s[i] = ((-sign) ^ sample) + sign;
}
memset(rndvec, 0, rndlen);
free(rndvec);
return 1;
OQS_MEM_cleanse(rndvec, sizeof(rndvec));
}
int oqs_kex_lwe_frodo_sample_n(uint16_t *s, const size_t n, struct oqs_kex_lwe_frodo_params *params, OQS_RAND *rand) {
void oqs_kex_lwe_frodo_sample_n(uint16_t *s, const size_t n, struct oqs_kex_lwe_frodo_params *params, OQS_RAND *rand) {
switch (params->sampler_num) {
case 8: {
// have to copy cdf_table from uint16_t to uint8_t
uint8_t *cdf_table_8 = malloc(params->cdf_table_len * sizeof(uint8_t));
if (NULL == cdf_table_8) {
return 0;
}
uint8_t cdf_table_8[params->cdf_table_len * sizeof(uint8_t)];
for (size_t i = 0; i < params->cdf_table_len; i++) {
cdf_table_8[i] = (uint8_t) params->cdf_table[i];
}
int ret = lwe_sample_n_inverse_8(s, n, cdf_table_8, params->cdf_table_len, rand);
free(cdf_table_8);
return ret;
lwe_sample_n_inverse_8(s, n, cdf_table_8, params->cdf_table_len, rand);
}
break;
case 12:
return lwe_sample_n_inverse_12(s, n, params->cdf_table, params->cdf_table_len, rand);
lwe_sample_n_inverse_12(s, n, params->cdf_table, params->cdf_table_len, rand);
break;
case 16:
return lwe_sample_n_inverse_16(s, n, params->cdf_table, params->cdf_table_len, rand);
lwe_sample_n_inverse_16(s, n, params->cdf_table, params->cdf_table_len, rand);
break;
default:
return 0;
assert(0); //ERROR
break;
}
}

View File

@ -0,0 +1,29 @@
//Recommended parameter set. Include twice to undefine
#ifndef OQS_LWE_FRODO_RECOMMENDED_H
#define OQS_LWE_FRODO_RECOMMENDED_H
#define PARAMS_N 752
#define PARAMS_NBAR 8
#define PARAMS_LOG2Q 15
#define PARAMS_Q (1 << PARAMS_LOG2Q)
#define PARAMS_EXTRACTED_BITS 4
#define PARAMS_KEY_BITS 256
#define PARAMS_KEY_BYTES (PARAMS_KEY_BITS >> 3)
#define PARAMS_STRIPE_STEP 8
#define LWE_DIV_ROUNDUP(x, y) (((x) + (y)-1) / y)
#define PARAMS_REC_HINT_LENGTH LWE_DIV_ROUNDUP(PARAMS_NBAR * PARAMS_NBAR, 8)
#define PARAMS_REC_PUB_LENGTH LWE_DIV_ROUNDUP(PARAMS_N * PARAMS_NBAR * PARAMS_LOG2Q, 8)
#else
#undef OQS_LWE_FRODO_RECOMMENDED_H
#undef PARAMS_N
#undef PARAMS_NBAR
#undef PARAMS_LOG2Q
#undef PARAMS_Q
#undef PARAMS_EXTRACTED_BITS
#undef PARAMS_KEY_BITS
#undef PARAMS_STRIPE_STEP
#undef LWE_DIV_ROUNDUP
#undef PARAMS_REC_HINT_LENGTH
#endif