diff --git a/src/kex_lwe_frodo/kex_lwe_frodo.c b/src/kex_lwe_frodo/kex_lwe_frodo.c index 72aa499b9..db4f401d1 100644 --- a/src/kex_lwe_frodo/kex_lwe_frodo.c +++ b/src/kex_lwe_frodo/kex_lwe_frodo.c @@ -48,14 +48,15 @@ OQS_KEX *OQS_KEX_lwe_frodo_new(OQS_RAND *rand, const uint8_t *seed, const size_t k->rand = rand; k->ctx = NULL; - k->alice_0 = &OQS_KEX_lwe_frodo_alice_0; - k->bob = &OQS_KEX_lwe_frodo_bob; - k->alice_1 = &OQS_KEX_lwe_frodo_alice_1; 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; @@ -119,250 +120,11 @@ err: } -int 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)); - 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); - 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; - } - - /* compute B = AS + E */ - ret = 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); - - *alice_msg_len = params->pub_len; - - ret = 1; - goto cleanup; - -err: - ret = 0; - free(*alice_msg); - *alice_msg = NULL; - free(*alice_priv); - *alice_priv = NULL; - -cleanup: - free(e); - free(b); - return ret; - -} - -int 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) { - - int ret; - - 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) { - 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); - if (*bob_msg == NULL) { - goto err; - } - bob_rec = *bob_msg + params->pub_len; - *key = malloc(params->key_bits >> 3); - 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; - } - - /* compute B' = S'A + E' */ - ret = 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); - - /* generate E'' */ - ret = oqs_kex_lwe_frodo_sample_n(eprimeprime, params->nbar * params->nbar, params, k->rand); - if (ret != 1) { - goto err; - } - - /* unpack B */ - oqs_kex_lwe_frodo_unpack(b, params->n * params->nbar, alice_msg, alice_msg_len, params->log2_q); - - /* compute V = S'B + E'' */ - oqs_kex_lwe_frodo_mul_add_sb_plus_e(v, b, bob_priv, eprimeprime, params); - - /* compute C = _{2^B} */ - oqs_kex_lwe_frodo_crossround2(bob_rec, v, params); - - /* compute K = round(V)_{2^B} */ - oqs_kex_lwe_frodo_round2(*key, v, params); - - *bob_msg_len = params->pub_len + params->rec_hint_len; - *key_len = params->key_bits >> 3; - - ret = 1; - goto cleanup; - -err: - ret = 0; - free(*bob_msg); - *bob_msg = NULL; - if (*key != NULL) { - memset(*key, 0, params->key_bits >> 3); - } - free(*key); - *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); - - return ret; - -} - -int 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 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) { - 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); - 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); - - /* compute W = B'S */ - oqs_kex_lwe_frodo_mul_bs(w, bprime, (uint16_t *) alice_priv, params); - - /* compute K = rec(B'S, C) */ - const uint8_t *bob_rec = bob_msg + params->pub_len; - oqs_kex_lwe_frodo_reconcile(*key, w, bob_rec, params); - - *key_len = params->key_bits >> 3; - - ret = 1; - goto cleanup; - -err: - ret = 0; - memset(key, 0, params->key_bits >> 3); - free(*key); - *key = NULL; - -cleanup: - free(w); - free(bprime); - return ret; - -} +// pre-process code to obtain "recommended" functions +#define MACRIFY(NAME) NAME ## _recommended +#include "kex_lwe_frodo_macrify.c" +// undefine macros to avoid any confusion later +#undef MACRIFY void OQS_KEX_lwe_frodo_alice_priv_free(UNUSED OQS_KEX *k, void *alice_priv) { if (alice_priv) { diff --git a/src/kex_lwe_frodo/kex_lwe_frodo.h b/src/kex_lwe_frodo/kex_lwe_frodo.h index ec3f93518..3bfe77860 100644 --- a/src/kex_lwe_frodo/kex_lwe_frodo.h +++ b/src/kex_lwe_frodo/kex_lwe_frodo.h @@ -14,9 +14,9 @@ OQS_KEX *OQS_KEX_lwe_frodo_new(OQS_RAND *rand, const uint8_t *seed, const size_t seed_len, const char *named_parameters); -int OQS_KEX_lwe_frodo_alice_0(OQS_KEX *k, void **alice_priv, uint8_t **alice_msg, size_t *alice_msg_len); -int 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); -int 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 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); +int OQS_KEX_lwe_frodo_alice_1_recommended(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); void OQS_KEX_lwe_frodo_alice_priv_free(OQS_KEX *k, void *alice_priv); void OQS_KEX_lwe_frodo_free(OQS_KEX *k); diff --git a/src/kex_lwe_frodo/kex_lwe_frodo_macrify.c b/src/kex_lwe_frodo/kex_lwe_frodo_macrify.c new file mode 100644 index 000000000..09092e391 --- /dev/null +++ b/src/kex_lwe_frodo/kex_lwe_frodo_macrify.c @@ -0,0 +1,244 @@ +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)); + 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); + 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; + } + + /* 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); + + *alice_msg_len = params->pub_len; + + ret = 1; + goto cleanup; + +err: + ret = 0; + free(*alice_msg); + *alice_msg = NULL; + free(*alice_priv); + *alice_priv = NULL; + +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) { + + int ret; + + 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) { + 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); + if (*bob_msg == NULL) { + goto err; + } + bob_rec = *bob_msg + params->pub_len; + *key = malloc(params->key_bits >> 3); + 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; + } + + /* 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); + + /* generate E'' */ + ret = oqs_kex_lwe_frodo_sample_n(eprimeprime, params->nbar * params->nbar, params, k->rand); + if (ret != 1) { + goto err; + } + + /* unpack B */ + oqs_kex_lwe_frodo_unpack(b, params->n * params->nbar, alice_msg, alice_msg_len, params->log2_q); + + /* compute V = S'B + E'' */ + MACRIFY(oqs_kex_lwe_frodo_mul_add_sb_plus_e)(v, b, bob_priv, eprimeprime); + + /* compute C = _{2^B} */ + MACRIFY(oqs_kex_lwe_frodo_crossround2)(bob_rec, v); + + /* 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; + + ret = 1; + goto cleanup; + +err: + ret = 0; + free(*bob_msg); + *bob_msg = NULL; + if (*key != NULL) { + memset(*key, 0, params->key_bits >> 3); + } + free(*key); + *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); + + 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 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) { + 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); + 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); + + /* 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; + MACRIFY(oqs_kex_lwe_frodo_reconcile)(*key, w, bob_rec); + + *key_len = params->key_bits >> 3; + + ret = 1; + goto cleanup; + +err: + ret = 0; + memset(key, 0, params->key_bits >> 3); + free(*key); + *key = NULL; + +cleanup: + free(w); + free(bprime); + return ret; + +} diff --git a/src/kex_lwe_frodo/local.h b/src/kex_lwe_frodo/local.h index e6f28066e..8db4c611c 100644 --- a/src/kex_lwe_frodo/local.h +++ b/src/kex_lwe_frodo/local.h @@ -23,9 +23,9 @@ struct oqs_kex_lwe_frodo_params { size_t cdf_table_len; }; -void oqs_kex_lwe_frodo_crossround2(unsigned char *out, const uint16_t *in, struct oqs_kex_lwe_frodo_params *params); -void oqs_kex_lwe_frodo_round2(unsigned char *out, uint16_t *in, struct oqs_kex_lwe_frodo_params *params); -void oqs_kex_lwe_frodo_reconcile(unsigned char *out, uint16_t *w, const unsigned char *hint, struct oqs_kex_lwe_frodo_params *params); +void oqs_kex_lwe_frodo_crossround2_recommended(unsigned char *out, const uint16_t *in); +void oqs_kex_lwe_frodo_round2_recommended(unsigned char *out, uint16_t *in); +void oqs_kex_lwe_frodo_reconcile_recommended(unsigned char *out, uint16_t *w, const unsigned char *hint); void oqs_kex_lwe_frodo_key_round(uint16_t *vec, const size_t length, const int b); void oqs_kex_lwe_frodo_key_round_hints(uint16_t *vec, const size_t length, const int b, const unsigned char *hint); @@ -34,9 +34,9 @@ void oqs_kex_lwe_frodo_unpack(uint16_t *out, const size_t outlen, const unsigned int 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(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(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(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params); -void oqs_kex_lwe_frodo_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s, struct oqs_kex_lwe_frodo_params *params); +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_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); #endif /* _OQS_KEX_RLWE_BCNS15_LOCAL_H_ */ diff --git a/src/kex_lwe_frodo/lwe.c b/src/kex_lwe_frodo/lwe.c index 0578a3c39..956da33d1 100644 --- a/src/kex_lwe_frodo/lwe.c +++ b/src/kex_lwe_frodo/lwe.c @@ -10,239 +10,6 @@ #define min(x, y) (((x) < (y)) ? (x) : (y)) -// [.]_2 -void oqs_kex_lwe_frodo_round2(unsigned char *out, uint16_t *in, struct oqs_kex_lwe_frodo_params *params) { - oqs_kex_lwe_frodo_key_round(in, params->nbar * params->nbar, params->log2_q - params->extracted_bits); - int i; - for (i = 0; i < params->nbar * params->nbar; i++) { - in[i] >>= params->log2_q - params->extracted_bits; // drop bits that were zeroed out - } - - // out should have enough space for the key - oqs_kex_lwe_frodo_pack(out, params->key_bits / 8, in, params->nbar * params->nbar, params->extracted_bits); -} - -void oqs_kex_lwe_frodo_crossround2(unsigned char *out, const uint16_t *in, struct oqs_kex_lwe_frodo_params *params) { - int i; - // out should have enough space for N_BAR * N_BAR bits - memset((unsigned char *)out, 0, params->rec_hint_len); - - uint16_t whole = 1 << (params->log2_q - params->extracted_bits); - uint16_t half = whole >> 1; - uint16_t mask = whole - 1; - - for (i = 0; i < params->nbar * params->nbar; i++) { - uint16_t remainder = in[i] & mask; - out[i / 8] += (remainder >= half) << (i % 8); - } -} - -void oqs_kex_lwe_frodo_reconcile(unsigned char *out, uint16_t *w, const unsigned char *hint, struct oqs_kex_lwe_frodo_params *params) { - oqs_kex_lwe_frodo_key_round_hints(w, params->nbar * params->nbar, params->log2_q - params->extracted_bits, hint); - int i; - for (i = 0; i < params->nbar * params->nbar; i++) { - w[i] >>= params->log2_q - 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 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; - } - - // 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++) { - s_transpose[k * params->n + j] = s[j * params->nbar + k]; - } - } - - assert(params->seed_len == 16); - void *aes_key_schedule = NULL; - OQS_AES128_load_schedule(params->seed, &aes_key_schedule, 1); - - for (i = 0; i < params->n; i++) { - // go through A's rows - memset(a_row, 0, a_rowlen); - for (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); - - for (k = 0; k < params->nbar; k++) { - uint16_t sum = 0; - for (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; - } - } - - 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 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; - } - - 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) { - // 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++) { - // 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); - - // 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++) { - 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++) { - uint16_t sum = 0; - for (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; - } - } - - 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 -void oqs_kex_lwe_frodo_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s, struct oqs_kex_lwe_frodo_params *params) { - // 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]; - } - out[i * params->nbar + j] %= params->q; // not really necessary since LWE_Q is a power of 2. - } - } -} - -// multiply by s on the left -void oqs_kex_lwe_frodo_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e, struct oqs_kex_lwe_frodo_params *params) { - // b (N x N_BAR) - // 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]; - } - out[k * params->nbar + i] %= params->q; // not really necessary since LWE_Q is a power of 2. - } - } -} - // round all elements of a vector to the nearest multiple of 2^b void oqs_kex_lwe_frodo_key_round(uint16_t *vec, const size_t length, const int b) { size_t i; @@ -374,3 +141,28 @@ 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) +// 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 +#undef MACRIFY diff --git a/src/kex_lwe_frodo/lwe_macrify.c b/src/kex_lwe_frodo/lwe_macrify.c new file mode 100644 index 000000000..207262ff9 --- /dev/null +++ b/src/kex_lwe_frodo/lwe_macrify.c @@ -0,0 +1,233 @@ +// [.]_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++) { + in[i] >>= PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS; // drop bits that were zeroed out + } + + // out should have enough space for the key + oqs_kex_lwe_frodo_pack(out, PARAMS_KEY_BITS / 8, in, PARAMS_NBAR * PARAMS_NBAR, PARAMS_EXTRACTED_BITS); +} + +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); + + uint16_t whole = 1 << (PARAMS_LOG2Q - PARAMS_EXTRACTED_BITS); + uint16_t half = whole >> 1; + uint16_t mask = whole - 1; + + for (i = 0; i < PARAMS_NBAR * PARAMS_NBAR; i++) { + uint16_t remainder = in[i] & mask; + out[i / 8] += (remainder >= half) << (i % 8); + } +} + +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++) { + 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) { + // 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; + } + + // 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++) { + s_transpose[k * PARAMS_N + j] = s[j * PARAMS_NBAR + k]; + } + } + + assert(params->seed_len == 16); + void *aes_key_schedule = NULL; + OQS_AES128_load_schedule(params->seed, &aes_key_schedule, 1); + + for (i = 0; i < PARAMS_N; i++) { + // go through A's rows + memset(a_row, 0, a_rowlen); + for (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); + + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (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; + } + } + + 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) { + // 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; + } + + 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) { + // 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++) { + // 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); + + // 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++) { + 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++) { + uint16_t sum = 0; + for (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; + } + } + + 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 +void MACRIFY(oqs_kex_lwe_frodo_mul_bs)(uint16_t *out, const uint16_t *b, const uint16_t *s) { + // 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]; + } + out[i * PARAMS_NBAR + j] %= PARAMS_Q; // not really necessary since LWE_Q is a power of 2. + } + } +} + +// multiply by s on the left +void MACRIFY(oqs_kex_lwe_frodo_mul_add_sb_plus_e)(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { + // b (N x N_BAR) + // 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]; + } + out[k * PARAMS_NBAR + i] %= PARAMS_Q; // not really necessary since LWE_Q is a power of 2. + } + } +} +