ARG_ENABL_SET([md5], [enable MD5 software implementation plugin.])
ARG_ENABL_SET([mgf1], [enable the MGF1 software implementation plugin.])
ARG_ENABL_SET([ml], [enable Module-Lattice-based crypto (ML-KEM) plugin.])
+ARG_ENABL_SET([frodo], [enable FrodoKEM Post Quantum Safe plugin.])
ARG_DISBL_SET([nonce], [disable nonce generation plugin.])
ARG_ENABL_SET([oqs], [enable Open Quantum Safe (liboqs) plugin.])
ARG_DISBL_SET([openssl], [disable the OpenSSL crypto plugin.])
ADD_PLUGIN([ccm], [s charon scripts nm cmd])
ADD_PLUGIN([gcm], [s charon scripts nm cmd])
ADD_PLUGIN([ml], [s charon scripts nm cmd])
+ADD_PLUGIN([frodo], [s charon scripts nm cmd])
ADD_PLUGIN([oqs], [s charon scripts nm cmd])
ADD_PLUGIN([drbg], [s charon pki scripts nm cmd])
ADD_PLUGIN([curl], [s charon pki scripts nm cmd])
AM_CONDITIONAL(USE_AF_ALG, test x$af_alg = xtrue)
AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue)
AM_CONDITIONAL(USE_ML, test x$ml = xtrue)
+AM_CONDITIONAL(USE_FRODO, test x$frodo = xtrue)
AM_CONDITIONAL(USE_OQS, test x$oqs = xtrue)
# charon plugins
src/libstrongswan/plugins/af_alg/Makefile
src/libstrongswan/plugins/drbg/Makefile
src/libstrongswan/plugins/ml/Makefile
+ src/libstrongswan/plugins/frodo/Makefile
src/libstrongswan/plugins/oqs/Makefile
src/libstrongswan/plugins/oqs/tests/Makefile
src/libstrongswan/plugins/test_vectors/Makefile
endif
endif
+if USE_FRODO
+ SUBDIRS += plugins/frodo
+if MONOLITHIC
+ libstrongswan_la_LIBADD += plugins/frodo/libstrongswan-frodo.la
+endif
+endif
+
if USE_TEST_VECTORS
SUBDIRS += plugins/test_vectors
if MONOLITHIC
--- /dev/null
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = \
+ $(PLUGIN_CFLAGS)
+
+# these files are also used by the tests, we can't directly refer to them
+# because of the subdirectory, which would cause distclean to fail
+noinst_LTLIBRARIES = libfrodo.la
+libfrodo_la_SOURCES = \
+ frodo_kem.h frodo_kem.c \
+ frodo_params.h frodo_params.c \
+ frodo_utils.h frodo_utils.c
+
+if MONOLITHIC
+noinst_LTLIBRARIES += libstrongswan-frodo.la
+else
+plugin_LTLIBRARIES = libstrongswan-frodo.la
+endif
+
+libstrongswan_frodo_la_SOURCES = \
+ frodo_plugin.h frodo_plugin.c
+
+libstrongswan_frodo_la_LDFLAGS = -module -avoid-version
+
+libstrongswan_frodo_la_LIBADD = libfrodo.la
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (C) Microsoft Corporation
+ *
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * 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_kem.h"
+#include "frodo_params.h"
+#include "frodo_utils.h"
+
+#include <utils/debug.h>
+
+typedef struct private_key_exchange_t private_key_exchange_t;
+
+/**
+ * Private data.
+ */
+struct private_key_exchange_t {
+
+ /**
+ * Public interface.
+ */
+ key_exchange_t public;
+
+ /**
+ * Key exchange method
+ */
+ key_exchange_method_t method;
+
+ /**
+ * If TRUE use AES128 for generating matrix A, otherwise use SHAKE128
+ */
+ bool use_aes;
+
+ /**
+ * Frodo parameters
+ */
+ const frodo_params_t *params;
+
+ /**
+ * Public key generated as initiator
+ */
+ chunk_t public_key;
+
+ /**
+ * Secret key generated as initiator
+ */
+ chunk_t secret_key;
+
+ /**
+ * Ciphertext generated as responder
+ */
+ chunk_t ciphertext;
+
+ /**
+ * Shared secret
+ */
+ chunk_t shared_secret;
+
+ /**
+ * NIST CTR DRBG
+ */
+ drbg_t *drbg;
+
+ /**
+ * SHAKE-128 or SHAKE-256 eXtended Output Function
+ */
+ xof_t *xof;
+};
+
+/**
+ * Create a DRBG instance if not testing.
+ */
+static bool need_drbg(private_key_exchange_t *this)
+{
+ uint32_t strength = 256;
+ rng_t *entropy;
+
+ if (this->drbg)
+ {
+ return TRUE;
+ }
+
+ /* entropy will be owned by drbg */
+ entropy = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+ if (!entropy)
+ {
+ DBG1(DBG_LIB, "could not create entropy source for DRBG");
+ return FALSE;
+ }
+
+ this->drbg = lib->crypto->create_drbg(lib->crypto, DRBG_CTR_AES256,
+ strength, entropy, chunk_empty);
+ if (!this->drbg)
+ {
+ DBG1(DBG_LIB, "could not instantiate %N at %u bit security",
+ drbg_type_names, DRBG_CTR_AES256, strength);
+ entropy->destroy(entropy);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Generator function shared between encaps and decaps.
+ */
+static bool generate(private_key_exchange_t *this, chunk_t public_key,
+ chunk_t G2in, uint8_t *k, uint16_t *Bp, uint16_t *C)
+{
+ const uint32_t n_x_nb = this->params->n * this->params->nb;
+ const uint32_t nb_x_nb = this->params->nb * this->params->nb;
+ const uint32_t log_q = this->params->log_q;
+ const uint32_t seed_A_len = this->params->seed_A_len;
+ const uint32_t ss_len = this->params->ss_len;
+ const uint32_t pk_len = this->params->pk_len;
+
+ chunk_t seedSE = chunk_alloca(1 + ss_len);
+ uint8_t *mu = G2in.ptr + ss_len;
+ uint8_t *pk_seedA = public_key.ptr;
+ uint8_t *pk_b = public_key.ptr + seed_A_len;
+ uint16_t B[n_x_nb], Sp[n_x_nb], Ep[n_x_nb], Epp[nb_x_nb], V[nb_x_nb];
+
+ /* Compute (seedSE || k) = G_2(pkh || mu) */
+ if (!this->xof->set_seed(this->xof, G2in) ||
+ !this->xof->get_bytes(this->xof, ss_len, seedSE.ptr + 1) ||
+ !this->xof->get_bytes(this->xof, ss_len, k))
+ {
+ return FALSE;
+ }
+ *seedSE.ptr = 0x96;
+
+ /* Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly */
+ if (!this->xof->set_seed(this->xof, seedSE) ||
+ !this->xof->get_bytes(this->xof, sizeof(Sp), (uint8_t*)Sp) ||
+ !this->xof->get_bytes(this->xof, sizeof(Ep), (uint8_t*)Ep) ||
+ !this->xof->get_bytes(this->xof, sizeof(Epp), (uint8_t*)Epp))
+ {
+ return FALSE;
+ }
+
+ frodo_sample_n(this->params, Sp, countof(Sp));
+ frodo_sample_n(this->params, Ep, countof(Ep));
+ frodo_mul_add_sa_plus_e(this->params, Bp, Sp, Ep, pk_seedA, this->use_aes);
+
+ /* Generate Epp, and compute V = Sp*B + Epp */
+ frodo_sample_n(this->params, Epp, countof(Epp));
+ frodo_unpack(B, countof(B), pk_b, pk_len - seed_A_len, log_q);
+ frodo_mul_add_sb_plus_e(this->params, V, B, Sp, Epp);
+
+ /* Encode mu, and compute C = V + enc(mu) (mod q) */
+ frodo_key_encode(this->params, C, (uint16_t*)mu);
+ frodo_add(this->params, C, V, C);
+
+ /* Cleanup */
+ memwipe(Sp, sizeof(Sp));
+ memwipe(Ep, sizeof(Ep));
+ memwipe(Epp, sizeof(Epp));
+ memwipe(V, sizeof(V));
+ memwipe(seedSE.ptr, seedSE.len);
+
+ return TRUE;
+}
+
+/**
+ * Generate the shared secret and encrypt it with the given public key.
+ */
+static bool encaps_shared_secret(private_key_exchange_t *this, chunk_t public_key)
+{
+ const uint32_t n_x_nb = this->params->n * this->params->nb;
+ const uint32_t nb_x_nb = this->params->nb * this->params->nb;
+ const uint32_t log_q = this->params->log_q;
+ const uint32_t extr_bits = this->params->extr_bits;
+ const uint32_t ct_c1_len = (n_x_nb * log_q)/8;
+ const uint32_t ct_c2_len = (nb_x_nb * log_q)/8;
+ const uint32_t mu_len = (nb_x_nb * extr_bits)/8;
+ const uint32_t ss_len = this->params->ss_len;
+ const uint32_t ct_len = this->params->ct_len;
+
+ chunk_t G2in = chunk_alloca(ss_len + mu_len);
+ chunk_t Fin = chunk_alloca(ct_len + ss_len);
+ uint8_t *pkh = G2in.ptr;
+ uint8_t *mu = G2in.ptr + ss_len;
+ uint8_t *ct_c1, *ct_c2;
+ uint8_t *Fin_ct = Fin.ptr;
+ uint8_t *Fin_k = Fin.ptr + ct_len;
+ uint8_t k[ss_len];
+ uint16_t Bp[n_x_nb], C[nb_x_nb];
+
+ /* pkh <- G_1(pk) */
+ if (!this->xof->set_seed(this->xof, public_key) ||
+ !this->xof->get_bytes(this->xof, ss_len, pkh))
+ {
+ return FALSE;
+ }
+
+ /* Generate random mu */
+ if (!this->drbg->generate(this->drbg, mu_len, mu))
+ {
+ DBG1(DBG_LIB, "could not generate mu");
+ return FALSE;
+ }
+
+ if (!generate(this, public_key, G2in, k, Bp, C))
+ {
+ return FALSE;
+ }
+
+ if (!this->ciphertext.ptr)
+ {
+ this->ciphertext = chunk_alloc(ct_len);
+ }
+ ct_c1 = this->ciphertext.ptr;
+ ct_c2 = this->ciphertext.ptr + ct_c1_len;
+
+ frodo_pack(ct_c1, ct_c1_len, Bp, countof(Bp), log_q);
+ frodo_pack(ct_c2, ct_c2_len, C, countof(C), log_q);
+
+ /* Compute ss = F(ct||KK) */
+ memcpy(Fin_ct, this->ciphertext.ptr, this->ciphertext.len);
+ memcpy(Fin_k, k, ss_len);
+
+ if (!this->xof->set_seed(this->xof, Fin) ||
+ !this->xof->allocate_bytes(this->xof, ss_len, &this->shared_secret))
+ {
+ return FALSE;
+ }
+
+ /* Cleanup */
+ memwipe(mu, mu_len);
+ memwipe(k, ss_len);
+ memwipe(Fin_k, ss_len);
+
+ return TRUE;
+}
+
+/**
+ * Decapsulate the shared secret using the secret key
+ */
+static bool decaps_shared_secret(private_key_exchange_t *this, chunk_t ciphertext)
+{
+ const uint32_t n_x_nb = this->params->n * this->params->nb;
+ const uint32_t nb_x_nb = this->params->nb * this->params->nb;
+ const uint32_t log_q = this->params->log_q;
+ const uint32_t extr_bits = this->params->extr_bits;
+ const uint32_t ct_c1_len = (n_x_nb * log_q)/8;
+ const uint32_t ct_c2_len = (nb_x_nb * log_q)/8;
+ const uint32_t mu_len = (nb_x_nb * extr_bits)/8;
+ const uint32_t ss_len = this->params->ss_len;
+ const uint32_t ct_len = this->params->ct_len;
+ const uint32_t pk_len = this->params->pk_len;
+
+ chunk_t G2in = chunk_alloca(ss_len + mu_len);
+ chunk_t Fin = chunk_alloca(ct_len + ss_len);
+ uint8_t *pkh = G2in.ptr;
+ uint8_t *muprime = G2in.ptr + ss_len;
+ uint8_t *ct_c1 = ciphertext.ptr;
+ uint8_t *ct_c2 = ciphertext.ptr + ct_c1_len;
+ uint8_t *sk_s = this->secret_key.ptr;
+ uint16_t *sk_S = (uint16_t*)(this->secret_key.ptr + ss_len + pk_len);
+ uint8_t *sk_pkh = this->secret_key.ptr + ss_len + pk_len + 2*n_x_nb;
+ uint8_t *Fin_ct = Fin.ptr;
+ uint8_t *Fin_k = Fin.ptr + ct_len;
+ uint8_t kprime[ss_len];
+ uint16_t Bp[n_x_nb], BBp[n_x_nb];
+ uint16_t W[nb_x_nb], C[nb_x_nb], CC[nb_x_nb];
+
+ /* Compute W = C - Bp*S (mod q), and decode the randomness mu' */
+ frodo_unpack(Bp, countof(Bp), ct_c1, ct_c1_len, log_q);
+ frodo_unpack(C, countof(C), ct_c2, ct_c2_len, log_q);
+ frodo_mul_bs(this->params, W, Bp, sk_S);
+ frodo_sub(this->params, W, C, W);
+ frodo_key_decode(this->params, (uint16_t*)muprime, W);
+ memcpy(pkh, sk_pkh, ss_len);
+
+ /* Generate (seedSE' || k') = G_2(pkh || mu'), generate Sp, Ep and Epp from
+ * seedSE', then compute BBp = Sp*A + Ep and W = Sp*B + Epp and finally
+ * CC = W + enc(mu') (mod q) */
+ if (!generate(this, this->public_key, G2in, kprime, BBp, CC))
+ {
+ return FALSE;
+ }
+
+ /* Prepare input to F */
+ memcpy(Fin_ct, ciphertext.ptr, ciphertext.len);
+
+ /* Reducing BBp modulo q */
+ for (int i = 0; i < countof(BBp); i++)
+ {
+ BBp[i] = BBp[i] & ((1 << log_q) - 1);
+ }
+
+ /* Copy s as fallback */
+ memcpy(Fin_k, sk_s, ss_len);
+ /* Compare (Bp == BBp && C == CC) and copy k' in constant time if the
+ * values are equal */
+ memcpy_cond(Fin_k, kprime, ss_len,
+ (uint8_t)memeq_const(Bp, BBp, sizeof(Bp)) &
+ (uint8_t)memeq_const(C, CC, sizeof(C)));
+
+ /* Do either ss = F(ct || k') or ss = F(ct || s) depending on the above */
+ if (!this->xof->set_seed(this->xof, Fin) ||
+ !this->xof->allocate_bytes(this->xof, ss_len, &this->shared_secret))
+ {
+ return FALSE;
+ }
+
+ /* Cleanup: */
+ memwipe(W, sizeof(W));
+ memwipe(muprime, mu_len);
+ memwipe(kprime, sizeof(kprime));
+ memwipe(Fin_k, ss_len);
+
+ return TRUE;
+}
+
+/**
+ * Generate a key pair.
+ */
+static bool gnerate_keypair(private_key_exchange_t *this)
+{
+ const uint32_t n_x_nb = this->params->n * this->params->nb;
+ const uint32_t log_q = this->params->log_q;
+ const uint32_t seed_A_len = this->params->seed_A_len;
+ const uint32_t ss_len = this->params->ss_len;
+ const uint32_t pk_len = this->params->pk_len;
+
+ uint8_t *pk_seedA, *pk_b, *sk_pos;
+ uint16_t B[n_x_nb], S[n_x_nb], E[n_x_nb];
+ uint8_t randomness[ss_len + ss_len + seed_A_len];
+ uint8_t *randomness_s = &randomness[0];
+ uint8_t *randomness_seedSE = &randomness[ss_len];
+ uint8_t *randomness_z = &randomness[ss_len + ss_len];
+ uint8_t seedSE[1 + ss_len];
+
+ this->public_key = chunk_alloc(this->params->pk_len);
+ this->secret_key = chunk_alloc(this->params->sk_len);
+ pk_seedA = this->public_key.ptr;
+ pk_b = this->public_key.ptr + seed_A_len;
+
+ /* Generate the secret value s and the seeds for S, E and seed_A */
+ if (!this->drbg->generate(this->drbg, sizeof(randomness), randomness))
+ {
+ return FALSE;
+ }
+
+ /* Generate seed_A as part of the public key */
+ if (!this->xof->set_seed(this->xof,
+ chunk_create(randomness_z, seed_A_len)) ||
+ !this->xof->get_bytes(this->xof, seed_A_len, pk_seedA))
+ {
+ return FALSE;
+ }
+
+ /* Generate S and E, and compute B = A*S + E. Generate A on-the-fly */
+ seedSE[0] = 0x5F;
+ memcpy(&seedSE[1], randomness_seedSE, ss_len);
+
+ if (!this->xof->set_seed(this->xof, chunk_create(seedSE, 1 + ss_len)) ||
+ !this->xof->get_bytes(this->xof, sizeof(S), (uint8_t*)S) ||
+ !this->xof->get_bytes(this->xof, sizeof(E), (uint8_t*)E))
+ {
+ return FALSE;
+ }
+
+ frodo_sample_n(this->params, S, countof(S));
+ frodo_sample_n(this->params, E, countof(E));
+
+ if (!frodo_mul_add_as_plus_e(this->params, B, S, E, this->public_key.ptr,
+ this->use_aes))
+ {
+ return FALSE;
+ }
+
+ /* Encode the second part of the public key */
+ frodo_pack(pk_b, pk_len - seed_A_len, B, countof(B), log_q);
+
+ /* Add s, pk and S to the secret key */
+ sk_pos = this->secret_key.ptr;
+ memcpy(sk_pos, randomness_s, ss_len);
+ sk_pos += ss_len;
+ memcpy(sk_pos, this->public_key.ptr, this->public_key.len);
+ sk_pos += pk_len;
+ memcpy(sk_pos, S, sizeof(S));
+ sk_pos += sizeof(S);
+
+ /* Add H(pk) to the secret key */
+ if (!this->xof->set_seed(this->xof, this->public_key) ||
+ !this->xof->get_bytes(this->xof, ss_len, sk_pos))
+ {
+ return FALSE;
+ }
+
+ /* Cleanup */
+ memwipe(S, sizeof(S));
+ memwipe(E, sizeof(E));
+ memwipe(randomness, sizeof(randomness));
+ memwipe(seedSE, sizeof(seedSE));
+
+ return TRUE;
+}
+
+METHOD(key_exchange_t, get_public_key, bool,
+ private_key_exchange_t *this, chunk_t *value)
+{
+ /* responder action */
+ if (this->ciphertext.ptr)
+ {
+ *value = chunk_clone(this->ciphertext);
+ return TRUE;
+ }
+
+ /* initiator action */
+ if (!this->secret_key.ptr)
+ {
+ if (!need_drbg(this) ||
+ !gnerate_keypair(this))
+ {
+ DBG1(DBG_LIB, "failed to generate %N key pair",
+ key_exchange_method_names, this->method);
+ return FALSE;
+ }
+ }
+
+ *value = chunk_clone(this->public_key);
+
+ return TRUE;
+}
+
+METHOD(key_exchange_t, get_shared_secret, bool,
+ private_key_exchange_t *this, chunk_t *secret)
+{
+ *secret = chunk_clone(this->shared_secret);
+ return TRUE;
+}
+
+METHOD(key_exchange_t, set_public_key, bool,
+ private_key_exchange_t *this, chunk_t value)
+{
+ /* initiator action */
+ if (this->secret_key.ptr)
+ {
+ if (value.len != this->params->ct_len)
+ {
+ DBG1(DBG_LIB, "wrong %N ciphertext size of %u bytes, %u bytes expected",
+ key_exchange_method_names, this->method, value.len,
+ this->params->ct_len);
+ return FALSE;
+ }
+ return decaps_shared_secret(this, value);
+ }
+
+ /* responder action */
+ if (value.len != this->params->pk_len)
+ {
+ DBG1(DBG_LIB, "wrong %N public key size of %u bytes, %u bytes expected",
+ key_exchange_method_names, this->method, value.len,
+ this->params->pk_len);
+ return FALSE;
+ }
+ return need_drbg(this) && encaps_shared_secret(this, value);
+}
+
+
+METHOD(key_exchange_t, get_method, key_exchange_method_t,
+ private_key_exchange_t *this)
+{
+ return this->method;
+}
+
+#ifdef TESTABLE_KE
+
+METHOD(key_exchange_t, set_seed, bool,
+ private_key_exchange_t *this, chunk_t value, drbg_t *drbg)
+{
+ DESTROY_IF(this->drbg);
+ this->drbg = drbg->get_ref(drbg);
+ return TRUE;
+}
+
+#endif /* TESTABLE_KE */
+
+METHOD(key_exchange_t, destroy, void,
+ private_key_exchange_t *this)
+{
+ DESTROY_IF(this->drbg);
+ this->xof->destroy(this->xof);
+
+ chunk_clear(&this->secret_key);
+ chunk_clear(&this->shared_secret);
+ chunk_free(&this->public_key);
+ chunk_free(&this->ciphertext);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+key_exchange_t *frodo_kem_create(key_exchange_method_t method)
+{
+ private_key_exchange_t *this;
+ const frodo_params_t *params;
+ frodo_kem_type_t id;
+ bool use_aes;
+ xof_t *xof;
+
+ switch (method)
+ {
+ case KE_FRODO_SHAKE_L1:
+ id = FRODO_KEM_L1;
+ use_aes = FALSE;
+ break;
+ case KE_FRODO_SHAKE_L3:
+ id = FRODO_KEM_L3;
+ use_aes = FALSE;
+ break;
+ case KE_FRODO_SHAKE_L5:
+ id = FRODO_KEM_L5;
+ use_aes = FALSE;
+ break;
+ case KE_FRODO_AES_L1:
+ id = FRODO_KEM_L1;
+ use_aes = TRUE;
+ break;
+ case KE_FRODO_AES_L3:
+ id = FRODO_KEM_L3;
+ use_aes = TRUE;
+ break;
+ case KE_FRODO_AES_L5:
+ id = FRODO_KEM_L5;
+ use_aes = TRUE;
+ break;
+ default:
+ return NULL;
+ }
+ params = frodo_params_get_by_id(id);
+
+ xof = lib->crypto->create_xof(lib->crypto, params->xof_type);
+ if (!xof)
+ {
+ DBG1(DBG_LIB, "could not instantiate %N", ext_out_function_names,
+ params->xof_type);
+ return NULL;
+ }
+
+ INIT(this,
+ .public = {
+ .get_method = _get_method,
+ .get_public_key = _get_public_key,
+ .set_public_key = _set_public_key,
+ .get_shared_secret = _get_shared_secret,
+ .destroy = _destroy,
+ },
+ .method = method,
+ .use_aes = use_aes,
+ .params = params,
+ .xof = xof,
+ );
+
+#ifdef TESTABLE_KE
+ this->public.set_seed = _set_seed;
+#endif
+
+ return &this->public;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * Quantum-safe key encapsulation implementation using ephemeral FrodoKEM.
+ *
+ * @defgroup frodo_kem frodo_kem
+ * @{ @ingroup frodo_p
+ */
+
+#ifndef FRODO_KEM_H_
+#define FRODO_KEM_H_
+
+#include <library.h>
+
+/**
+ * Creates a new key_exchange_t object.
+ *
+ * @param method key exchange method
+ * @return key_exchange_t object, NULL if not supported
+ */
+key_exchange_t *frodo_kem_create(key_exchange_method_t method);
+
+#endif /** FRODO_KEM_H_ @}*/
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (C) Microsoft Corporation
+ *
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * 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_params.h"
+
+const uint16_t cdf_table_1[] = { 4643, 13363, 20579, 25843, 29227,
+ 31145, 32103, 32525, 32689, 32745,
+ 32762, 32766, 32767
+ };
+const uint16_t cdf_table_3[] = { 5638, 15915, 23689, 28571, 31116,
+ 32217, 32613, 32731, 32760, 32766,
+ 32767
+ };
+const uint16_t cdf_table_5[] = { 9142, 23462, 30338, 32361, 32725,
+ 32765, 32767
+ };
+
+/**
+ * FrodoKEM parameter definitions
+ */
+static const frodo_params_t frodo_params[] = {
+
+ {
+ FRODO_KEM_L1, /* Frodo KEM ID */
+ 640, /* Lattice dimension n */
+ 8, /* Dimension n_bar */
+ 15, /* Logarithm of modulus q */
+ 2, /* Extracted bits extr_bits */
+ 16, /* Size of seed_A seed_A_len */
+ 16, /* Size of shared secret ss_len */
+ 9720, /* Size of ciphertext ct_len */
+ 9616, /* Size of public key pk_len */
+ 19888, /* Size of secret key sk_len */
+ countof(cdf_table_1), /* Size of CDF table cdf_table_len */
+ cdf_table_1, /* CDF table */
+ XOF_SHAKE_128, /* SHAKE XOF */
+ },
+
+ {
+ FRODO_KEM_L3, /* Frodo KEM ID */
+ 976, /* Lattice dimension n */
+ 8, /* Dimension n_bar */
+ 16, /* Logarithm of modulus q */
+ 3, /* Extracted bits extr_bits */
+ 16, /* Size of seed_A seed_A_len */
+ 24, /* Size of shared secret ss_len */
+ 15744, /* Size of ciphertext ct_len */
+ 15632, /* Size of public key pk_len */
+ 31296, /* Size of secret key sk_len */
+ countof(cdf_table_3), /* Size of CDF table cdf_table_len */
+ cdf_table_3, /* CDF table */
+ XOF_SHAKE_256, /* SHAKE XOF */
+ },
+
+ {
+ FRODO_KEM_L5, /* Frodo KEM ID */
+ 1344, /* Lattice dimension n */
+ 8, /* Dimension n_bar */
+ 16, /* Logarithm of modulus q */
+ 4, /* Extracted bits extr_bits */
+ 16, /* Size of seed_A seed_A_len */
+ 32, /* Size of shared secret ss_len */
+ 21632, /* Size of ciphertext ct_len */
+ 21520, /* Size of public key pk_len */
+ 43088, /* Size of secret key sk_len */
+ countof(cdf_table_5), /* Size of CDF table cdf_table_len */
+ cdf_table_5, /* CDF table */
+ XOF_SHAKE_256, /* SHAKE XOF */
+ },
+};
+
+/**
+ * See header.
+ */
+const frodo_params_t *frodo_params_get_by_id(frodo_kem_type_t id)
+{
+ int i;
+
+ for (i = 0; i < countof(frodo_params); i++)
+ {
+ if (frodo_params[i].id == id)
+ {
+ return &frodo_params[i];
+ }
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (C) Microsoft Corporation
+ *
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup frodo_params frodo_params
+ * @{ @ingroup frodo_p
+ */
+
+#ifndef FRODO_PARAMS_H_
+#define FRODO_PARAMS_H_
+
+#include <library.h>
+
+typedef struct frodo_params_t frodo_params_t;
+typedef enum frodo_kem_type_t frodo_kem_type_t;
+
+/**
+ * FrodoKEM types with various security strengths
+ */
+enum frodo_kem_type_t {
+ FRODO_KEM_L1,
+ FRODO_KEM_L3,
+ FRODO_KEM_L5,
+};
+
+/**
+ * FrodoKEM parameter definitions
+ */
+struct frodo_params_t {
+
+ /**
+ * Frodo key exchange ID
+ */
+ const frodo_kem_type_t id;
+
+ /**
+ * Lattice dimension
+ */
+ const uint32_t n;
+
+ /**
+ * Dimension n_bar
+ */
+ const uint32_t nb;
+
+ /**
+ * Logarithm of modulus q
+ */
+ const uint32_t log_q;
+
+ /**
+ * Extracted bits
+ */
+ const uint32_t extr_bits;
+
+ /**
+ * Size of seed_A
+ */
+ const uint32_t seed_A_len;
+
+ /**
+ * Size of shared secret
+ */
+ const uint32_t ss_len;
+
+ /**
+ * Size of ciphertext ((n * nb * log_q) / 8 + (nb * nb * log_q) / 8)
+ */
+ const uint32_t ct_len;
+
+ /**
+ * Size of public key (seed_A_len + (n * nb * log_q) / 8)
+ */
+ const uint32_t pk_len;
+
+ /**
+ * Size of secret key (ss_len(s) + pk_len + 2 * n * nb + ss_len(pkhash))
+ */
+ const uint32_t sk_len;
+
+ /**
+ * Size of CDF table
+ */
+ const uint32_t cdf_table_len;
+
+ /**
+ * CDF table
+ */
+ const uint16_t *cdf_table;
+
+ /**
+ * SHAKE extended output function
+ */
+ ext_out_function_t xof_type;
+};
+
+/**
+ * Get Frodo parameters by Frodo key exchange ID
+ *
+ * @param id Frodo KEM ID
+ * @return Frodo parameters
+*/
+const frodo_params_t* frodo_params_get_by_id(frodo_kem_type_t id);
+
+#endif /** FRODO_PARAMS_H_ @}*/
--- /dev/null
+/*
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "frodo_plugin.h"
+#include "frodo_kem.h"
+
+#include <library.h>
+
+typedef struct private_frodo_plugin_t private_frodo_plugin_t;
+
+/**
+ * private data of frodo_plugin
+ */
+struct private_frodo_plugin_t {
+
+ /**
+ * public functions
+ */
+ frodo_plugin_t public;
+};
+
+METHOD(plugin_t, get_name, char*,
+ private_frodo_plugin_t *this)
+{
+ return "frodo";
+}
+
+METHOD(plugin_t, get_features, int,
+ private_frodo_plugin_t *this, plugin_feature_t *features[])
+{
+ static plugin_feature_t f[] = {
+ PLUGIN_REGISTER(KE, frodo_kem_create),
+ PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L1),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L3),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L5),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(KE, KE_FRODO_AES_L1),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
+ PLUGIN_DEPENDS(CRYPTER, ENCR_AES_ECB, 16),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(KE, KE_FRODO_AES_L3),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
+ PLUGIN_DEPENDS(CRYPTER, ENCR_AES_ECB, 16),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(KE, KE_FRODO_AES_L5),
+ PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
+ PLUGIN_DEPENDS(CRYPTER, ENCR_AES_ECB, 16),
+ PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256),
+ PLUGIN_DEPENDS(RNG, RNG_STRONG),
+ };
+ *features = f;
+
+ return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+ private_frodo_plugin_t *this)
+{
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *frodo_plugin_create()
+{
+ private_frodo_plugin_t *this;
+
+ INIT(this,
+ .public = {
+ .plugin = {
+ .get_name = _get_name,
+ .get_features = _get_features,
+ .destroy = _destroy,
+ },
+ },
+ );
+
+ return &this->public.plugin;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup frodo_p frodo
+ * @ingroup plugins
+ *
+ * @defgroup frodo_plugin frodo_plugin
+ * @{ @ingroup frodo_p
+ */
+
+#ifndef FRODO_PLUGIN_H_
+#define FRODO_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct frodo_plugin_t frodo_plugin_t;
+
+/**
+ * Plugin implementing Frodo-based key exchange
+ */
+struct frodo_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+#endif /** FRODO_PLUGIN_H_ @}*/
--- /dev/null
+/*
+ * 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, 0, 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
+ {
+ /* the input vector is exhausted */
+ break;
+ }
+ }
+ }
+ if (b == 8)
+ {
+ /* out[i] is filled in */
+ i++;
+ }
+ }
+}
+
+/**
+ * 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
+ {
+ /* the input vector is exhausted */
+ break;
+ }
+ }
+ }
+ if (b == lsb)
+ {
+ /* out[i] is filled in */
+ i++;
+ }
+ }
+}
+
+/**
+ * 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;
+
+ xof_t *xof;
+ uint8_t seed_A_separated[2 + seed_A_len];
+ uint16_t *seed_A_origin = (uint16_t*)&seed_A_separated;
+ int i;
+ bool success = FALSE;
+
+ 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] = htole16(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 n_x_n = n * n;
+ const uint32_t A_len = n_x_n * sizeof(uint16_t);
+
+ crypter_t *crypter;
+ chunk_t A_chunk;
+ uint32_t block_len, step, k;
+ int i, j;
+ bool success = FALSE;
+
+ memset((uint8_t*)A, 0, A_len);
+
+ crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_ECB, 16);
+ if (!crypter)
+ {
+ DBG1(DBG_LIB, "could not instantiate %N (128 bit)",
+ encryption_algorithm_names, ENCR_AES_ECB);
+ 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;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ for (j = 0; j < n; j += step)
+ {
+ k = i*n + j;
+ A[k] = htole16(i);
+ A[k + 1] = htole16(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;
+ const uint32_t n_x_nb = n * nb;
+
+ 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, n_x_nb * 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 += le16toh(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;
+ const uint32_t n_x_nb = n * nb;
+
+ 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, n_x_nb * 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 += le16toh(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;
+ const uint64_t mask = ((uint64_t)1 << extr_bits) - 1;
+ const u_int nwords = (nb * nb)/8;
+ const u_int npieces_word = 8;
+
+ u_int i, j;
+ uint64_t temp;
+ 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;
+ const u_int nwords = (nb * nb) / 8;
+ const u_int maskex = ((uint16_t)1 << extr_bits) - 1;
+ const u_int maskq = ((uint16_t)1 << log_q) - 1;
+ const u_int npieces_word = 8;
+
+ u_int i, j, index = 0;
+ uint16_t temp;
+ 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup frodo_utils frodo_utls
+ * @{ @ingroup frodo_p
+ */
+
+#ifndef FRODO_UTILS_H_
+#define FRODO_UTILS_H_
+
+#include "frodo_params.h"
+
+#include <library.h>
+
+/**
+ * Pack the input uint16 vector into a char output vector, copying lsb bits
+ * from each input element.
+ *
+ * If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied.
+ *
+ * @param out buffer to put packed vector
+ * @param outlen length of the buffer
+ * @param in input vector
+ * @param inlen size of vector (number of uint16_t)
+ * @param lsb number of bits to copy from each input element
+ */
+void frodo_pack(u_char *out, size_t outlen, uint16_t *in, size_t inlen,
+ u_char lsb);
+
+/**
+ * Unpack the input char vector into a uint16_t output vector, copying lsb bits
+ * for each output element from input.
+ *
+ * outlen must be at least ceil(inlen * 8 / lsb).
+ *
+ * @param out buffer to put unpacked vector
+ * @param outlen size of the buffer (number of uint16_t)
+ * @param in packed input vector
+ * @param inlen size of vector
+ * @param lsb number of bits to copy from each input element
+ */
+void frodo_unpack(uint16_t *out, size_t outlen, u_char *in, size_t inlen,
+ u_char lsb);
+
+/**
+ * Fills vector s with n samples from the noise distribution which requires
+ * 16 bits to sample. The distribution is specified by its CDF.
+ *
+ * @param params parameter set
+ * @param s pseudo-random values to sample (are overwritten by output)
+ * @param n size of s
+ */
+void frodo_sample_n(const frodo_params_t *params, uint16_t *s, size_t n);
+
+/**
+ * Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s
+ * on the right.
+ *
+ * @param params parameter set
+ * @param out output A*s + e (N x N_BAR)
+ * @param s array (N x N_BAR)
+ * @param e array (N x N_BAR)
+ * @param seed_A seed for matrix A
+ * @param use_aes if TRUE use AES128 for matrix A, otherwise use SHAKE128
+ * @return TRUE if successful
+ */
+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);
+
+/**
+ * Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s'
+ * on the left.
+ *
+ * @param params parameter set
+ * @param out output s'*A + e' (N_BAR x N)
+ * @param s array (N_BAR x N)
+ * @param e array (N_BAR x N)
+ * @param seed_A seed for matrix A
+ * @param use_aes if TRUE use AES128 for matrix A, otherwise use SHAKE128
+ * @return TRUE if successful
+ */
+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);
+
+/**
+ * Multiply by s on the left and add e.
+ *
+ * @param params parameter set
+ * @param b array (N x N_BAR)
+ * @param s array (N_BAR x N)
+ * @param e array (N_BAR x N_BAR)
+ * @param out output s*b + e (N_BAR x N_BAR)
+ */
+void frodo_mul_add_sb_plus_e(const frodo_params_t *params, uint16_t *out,
+ uint16_t *b, uint16_t *s, uint16_t *e);
+
+/**
+ * Multiply by s on the right.
+ *
+ * @param params parameter set
+ * @param out output b*s (N_BAR x N_BAR)
+ * @param b array (N_BAR x N),
+ * @param s array (N x N_BAR)
+ */
+ void frodo_mul_bs(const frodo_params_t *params, uint16_t *out,
+ uint16_t *b, uint16_t *s);
+
+/**
+ * Add a and b.
+ *
+ * @param params parameter set
+ * @param out output a + b (N_BAR x N_BAR)
+ * @param a array (N_BAR x N_BAR)
+ * @param b array (N_BAR x N_BAR)
+ */
+void frodo_add(const frodo_params_t *params, uint16_t *out,
+ uint16_t *a, uint16_t *b);
+
+/**
+ * Subtract a and b.
+ *
+ * @param params parameter set
+ * @param out output a - b (N_BAR x N_BAR)
+ * @param a array (N_BAR x N_BAR)
+ * @param b array (N_BAR x N_BAR)
+ */
+void frodo_sub(const frodo_params_t *params, uint16_t *out,
+ uint16_t *a, uint16_t *b);
+
+/**
+ * Encode a bit string as matrix.
+ *
+ * @param params parameter set
+ * @param out encoded matrix
+ * @param in bit string to be encoded
+ */
+void frodo_key_encode(const frodo_params_t *params, uint16_t *out, uint16_t *in);
+
+/**
+ * Decode a matrix as bit string.
+ *
+ * @param params parameter set
+ * @param out decoded bit string
+ * @param in matrix to be decoded
+ */
+void frodo_key_decode(const frodo_params_t *params, uint16_t *out, uint16_t *in);
+
+#endif /** FRODO_UTILS_H_ @}*/