diff --git a/configure.ac b/configure.ac index 6a83fde29..854694981 100644 --- a/configure.ac +++ b/configure.ac @@ -154,6 +154,7 @@ ARG_ENABL_SET([mgf1], [enable the MGF1 software implementation plugin. ARG_ENABL_SET([newhope], [enable New Hope crypto plugin.]) ARG_DISBL_SET([nonce], [disable nonce generation plugin.]) ARG_ENABL_SET([ntru], [enables the NTRU crypto plugin.]) +ARG_ENABL_SET([frodo], [enable FrodoKEM Post Quantum Safe plugin.]) ARG_ENABL_SET([oqs], [enable Open Quantum Safe (liboqs) plugin.]) ARG_ENABL_SET([openssl], [enables the OpenSSL crypto plugin.]) ARG_ENABL_SET([wolfssl], [enables the wolfSSL crypto plugin.]) @@ -1503,6 +1504,7 @@ ADD_PLUGIN([ctr], [s charon scripts nm cmd]) ADD_PLUGIN([ccm], [s charon scripts nm cmd]) ADD_PLUGIN([gcm], [s charon scripts nm cmd]) ADD_PLUGIN([ntru], [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([newhope], [s charon scripts nm cmd]) @@ -1676,6 +1678,7 @@ AM_CONDITIONAL(USE_NEWHOPE, test x$newhope = xtrue) AM_CONDITIONAL(USE_BLISS, test x$bliss = xtrue) AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue) AM_CONDITIONAL(USE_OQS, test x$oqs = xtrue) +AM_CONDITIONAL(USE_FRODO, test x$frodo = xtrue) # charon plugins # ---------------- @@ -1959,6 +1962,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/bliss/tests/Makefile src/libstrongswan/plugins/newhope/Makefile src/libstrongswan/plugins/newhope/tests/Makefile + src/libstrongswan/plugins/frodo/Makefile src/libstrongswan/plugins/oqs/Makefile src/libstrongswan/plugins/oqs/tests/Makefile src/libstrongswan/plugins/test_vectors/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index a2bd8268a..ffb83cee5 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -681,6 +681,13 @@ if MONOLITHIC 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 diff --git a/src/libstrongswan/plugins/frodo/Makefile.am b/src/libstrongswan/plugins/frodo/Makefile.am new file mode 100644 index 000000000..2cb0e6076 --- /dev/null +++ b/src/libstrongswan/plugins/frodo/Makefile.am @@ -0,0 +1,26 @@ +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.h frodo.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 diff --git a/src/libstrongswan/plugins/frodo/frodo.c b/src/libstrongswan/plugins/frodo/frodo.c new file mode 100644 index 000000000..47bdff582 --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo.c @@ -0,0 +1,596 @@ +/* + * MIT License + * + * Copyright (C) Microsoft Corporation + * + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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.h" +#include "frodo_params.h" +#include "frodo_utils.h" + +#include + +typedef struct private_frodo_t private_frodo_t; + +/** + * Private data of an frodo_t object. + */ +struct private_frodo_t { + + /** + * Public frodo_t interface. + */ + frodo_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 + */ + uint8_t *public_key; + + /** + * Secret Key + */ + uint8_t *secret_key; + + /** + * Ciphertext + */ + uint8_t *ciphertext; + + /** + * Shared secret + */ + uint8_t *shared_secret; + + /** + * NIST CTR DRBG + */ + drbg_t *drbg; + + /** + * SHAKE-128 or SHAKE-256 eXtended Output Function + */ + xof_t *xof; + +}; + +/** + * + */ +static bool need_drbg(private_frodo_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_TRUE); + if (!entropy) + { + DBG1(DBG_LIB, "could not attach 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 DRBG at %u bit security", strength); + entropy->destroy(entropy); + return FALSE; + } + + return TRUE; +} + +/** + * Generator function shared between encaps and decaps shared secret + */ +static bool generate(private_frodo_t *this, 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; + + uint8_t *mu = G2in.ptr + ss_len; + uint8_t *pk_seedA = this->public_key; + uint8_t *pk_b = this->public_key + 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]; + + chunk_t seedSE = chunk_alloc(1 + ss_len); + + 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, + n_x_nb * sizeof(uint16_t), (uint8_t*)Sp) || + !this->xof->get_bytes(this->xof, + n_x_nb * sizeof(uint16_t), (uint8_t*)Ep) || + !this->xof->get_bytes(this->xof, + nb_x_nb * sizeof(uint16_t), (uint8_t*)Epp)) + { + return FALSE; + } + + frodo_sample_n(this->params, Sp, n_x_nb); + frodo_sample_n(this->params, Ep, n_x_nb); + 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, nb_x_nb); + frodo_unpack(B, n_x_nb, 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((uint8_t *)Sp, n_x_nb * sizeof(uint16_t)); + memwipe((uint8_t *)Ep, n_x_nb * sizeof(uint16_t)); + memwipe((uint8_t *)Epp, nb_x_nb * sizeof(uint16_t)); + memwipe((uint8_t *)V, nb_x_nb * sizeof(uint16_t)); + chunk_clear(&seedSE); + + return TRUE; +} + +/** + * Generate the shared secret and encrypt it with the configured public key + */ +static bool encaps_shared_secret(private_frodo_t *this) +{ + 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 pk = chunk_create(this->public_key, pk_len); + chunk_t G2in = chunk_alloca(ss_len + mu_len); + uint8_t *pkh = G2in.ptr; + uint8_t *mu = G2in.ptr + ss_len; + uint8_t *ct_c1, *ct_c2; + uint8_t Fin[ct_len + ss_len]; + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[ct_len]; + uint8_t k[ss_len]; + uint16_t Bp[n_x_nb], C[nb_x_nb]; + + if (!this->ciphertext) + { + this->ciphertext = malloc(ct_len); + } + ct_c1 = this->ciphertext; + ct_c2 = this->ciphertext + ct_c1_len; + + /* pkh <- G_1(pk) */ + if (!this->xof->set_seed(this->xof, pk) || + !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, G2in, k, Bp, C)) + { + return FALSE; + } + frodo_pack(ct_c1, ct_c1_len, Bp, n_x_nb, log_q); + frodo_pack(ct_c2, ct_c2_len, C, nb_x_nb, log_q); + + /* Compute ss = F(ct||KK) */ + memcpy(Fin_ct, this->ciphertext, ct_len); + memcpy(Fin_k, k, ss_len); + + if (!this->xof->set_seed(this->xof, chunk_create(Fin, ct_len + ss_len)) || + !this->xof->get_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_frodo_t *this) +{ + 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); + uint8_t *pkh = G2in.ptr; + uint8_t *muprime = G2in.ptr + ss_len; + uint8_t *ct_c1 = this->ciphertext; + uint8_t *ct_c2 = this->ciphertext + ct_c1_len; + uint8_t *sk_s = this->secret_key; + uint16_t *sk_S = (uint16_t *)(this->secret_key + ss_len + pk_len); + uint8_t *sk_pkh = this->secret_key + ss_len + pk_len + 2*n_x_nb; + uint8_t Fin[ct_len + ss_len]; + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[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, n_x_nb, ct_c1, ct_c1_len, log_q); + frodo_unpack(C, nb_x_nb, 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); + + if (!generate(this, G2in, kprime, BBp, CC)) + { + return FALSE; + } + + /* Prepare input to F */ + memcpy(Fin_ct, this->ciphertext, ct_len); + + /* Reducing BBp modulo q */ + for (int i = 0; i < n_x_nb; i++) + { + BBp[i] = BBp[i] & ((1 << log_q) - 1); + } + + /* Is (Bp == BBp & C == CC) = true */ + if (memcmp(Bp, BBp, n_x_nb * sizeof(uint16_t)) == 0 && + memcmp(C, CC, nb_x_nb * sizeof(uint16_t)) == 0) + { + /* Load k' to do ss = F(ct || k') */ + memcpy(Fin_k, kprime, ss_len); + } + else + { + /* Load s to do ss = F(ct || s) */ + memcpy(Fin_k, sk_s, ss_len); + } + + if (!this->xof->set_seed(this->xof, chunk_create(Fin, ct_len + ss_len)) || + !this->xof->get_bytes(this->xof, ss_len, this->shared_secret)) + { + return FALSE; + } + + /* Cleanup: */ + memwipe((uint8_t *)W, nb_x_nb * sizeof(uint16_t)); + memwipe(muprime, mu_len); + memwipe(kprime, ss_len); + memwipe(Fin_k, ss_len); + + return TRUE; +} + +/** + * + */ +static bool set_ciphertext(private_frodo_t *this, chunk_t value) +{ + 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; + } + this->ciphertext = malloc(value.len); + memcpy(this->ciphertext, value.ptr, value.len); + + return decaps_shared_secret(this); +} + +METHOD(key_exchange_t, get_public_key, bool, + private_frodo_t *this, chunk_t *value) +{ + /* responder action */ + if (this->ciphertext) + { + *value = chunk_clone( + chunk_create(this->ciphertext, this->params->ct_len)); + return TRUE; + } + + /* initiator action */ + if (!this->secret_key) + { + 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->secret_key = malloc(this->params->sk_len); + pk_seedA = this->public_key; + pk_b = this->public_key + seed_A_len; + + /* Do we need an entropy source? */ + /* TODO */ + /* Generate the secret value s and the seeds for S, E and seed_A */ + if (!need_drbg(this) || + !this->drbg->generate(this->drbg, 2*ss_len + seed_A_len, randomness)) + { + DBG1(DBG_LIB, "could not generate 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, + n_x_nb * sizeof(uint16_t), (uint8_t*)S) || + !this->xof->get_bytes(this->xof, + n_x_nb * sizeof(uint16_t), (uint8_t*)E)) + { + return FALSE; + } + + frodo_sample_n(this->params, S, n_x_nb); + frodo_sample_n(this->params, E, n_x_nb); + + if (!frodo_mul_add_as_plus_e(this->params, B, S, E, this->public_key, + this->use_aes)) + { + return FALSE; + } + + /* Encode the second part of the public key */ + frodo_pack(pk_b, pk_len - seed_A_len, B, n_x_nb, log_q); + + /* Add s, pk and S to the secret key */ + sk_pos = this->secret_key; + memcpy(sk_pos, randomness_s, ss_len); + sk_pos += ss_len; + memcpy(sk_pos, this->public_key, pk_len); + sk_pos += pk_len; + memcpy(sk_pos, S, n_x_nb * sizeof(uint16_t)); + sk_pos += n_x_nb * sizeof(uint16_t); + + /* Add H(pk) to the secret key */ + if (!this->xof->set_seed(this->xof, + chunk_create(this->public_key, pk_len)) || + !this->xof->get_bytes(this->xof, ss_len, sk_pos)) + { + return FALSE; + } + + /* Cleanup */ + memwipe((uint8_t *)S, n_x_nb * sizeof(uint16_t)); + memwipe((uint8_t *)E, n_x_nb * sizeof(uint16_t)); + memwipe(randomness, ss_len + ss_len + seed_A_len); + memwipe(seedSE, 1 + ss_len); + } + *value = chunk_clone(chunk_create(this->public_key, this->params->pk_len)); + + return TRUE; +} + + +METHOD(key_exchange_t, get_shared_secret, bool, + private_frodo_t *this, chunk_t *secret) +{ + *secret = chunk_clone( + chunk_create(this->shared_secret, this->params->ss_len)); + return TRUE; +} + +METHOD(key_exchange_t, set_public_key, bool, + private_frodo_t *this, chunk_t value) +{ + /* initiator action */ + if (this->secret_key) + { + return set_ciphertext(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; + } + memcpy(this->public_key, value.ptr, value.len); + + return need_drbg(this) && encaps_shared_secret(this); +} + + +METHOD(key_exchange_t, get_method, key_exchange_method_t, + private_frodo_t *this) +{ + return this->method; +} + +METHOD(key_exchange_t, set_seed, bool, + private_frodo_t *this, chunk_t value, drbg_t *drbg) +{ + DESTROY_IF(this->drbg); + this->drbg = drbg->get_ref(drbg); + + return TRUE; +} + +METHOD(key_exchange_t, destroy, void, + private_frodo_t *this) +{ + DESTROY_IF(this->drbg); + this->xof->destroy(this->xof); + + memwipe(this->secret_key, this->params->sk_len); + free(this->secret_key); + memwipe(this->shared_secret, this->params->ss_len); + free(this->shared_secret); + free(this->public_key); + free(this->ciphertext); + free(this); +} + +/* + * Described in header. + */ +frodo_t *frodo_create(key_exchange_method_t method) +{ + private_frodo_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 = { + .ke = { + .get_method = _get_method, + .get_public_key = _get_public_key, + .set_public_key = _set_public_key, + .get_shared_secret = _get_shared_secret, + .set_seed = _set_seed, + .destroy = _destroy, + }, + }, + .method = method, + .use_aes = use_aes, + .params = params, + .xof = xof, + .public_key = malloc(params->pk_len), + .shared_secret = malloc(params->ss_len), + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/frodo/frodo.h b/src/libstrongswan/plugins/frodo/frodo.h new file mode 100644 index 000000000..6ba57c40e --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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 frodo + * @{ @ingroup frodo_p + */ + +#ifndef FRODO_H_ +#define FRODO_H_ + +typedef struct frodo_t frodo_t; + +#include + +/** + * Quantum-safe key encapsulation implementation using FrodoKEM + */ +struct frodo_t { + + /** + * Implements key_exchange_t interface. + */ + key_exchange_t ke; +}; + +/** + * Creates a new frodo_t object. + * + * @param method key exchange method + * @return frodo_t object, NULL if not supported + */ +frodo_t *frodo_create(key_exchange_method_t method); + +#endif /** FRODO_H_ @}*/ diff --git a/src/libstrongswan/plugins/frodo/frodo_params.c b/src/libstrongswan/plugins/frodo/frodo_params.c new file mode 100644 index 000000000..94f7a543b --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_params.c @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (C) Microsoft Corporation + * + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 */ + 13, /* 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 */ + 11, /* 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 */ + 7, /* 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; +} diff --git a/src/libstrongswan/plugins/frodo/frodo_params.h b/src/libstrongswan/plugins/frodo/frodo_params.h new file mode 100644 index 000000000..55c557636 --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_params.h @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (C) Microsoft Corporation + * + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 + +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 + */ + const uint32_t ct_len; + + /** + * Size of public key + */ + const uint32_t pk_len; + + /** + * Size of secret key + */ + 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_ @}*/ diff --git a/src/libstrongswan/plugins/frodo/frodo_plugin.c b/src/libstrongswan/plugins/frodo/frodo_plugin.c new file mode 100644 index 000000000..8c3266d37 --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_plugin.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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.h" + +#include + +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_create), + PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L1), + PLUGIN_DEPENDS(XOF, XOF_SHAKE_128), + PLUGIN_DEPENDS(DRBG, DRBG_CTR_AES256), + PLUGIN_DEPENDS(RNG, RNG_TRUE), + 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_TRUE), + 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_TRUE), + 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_TRUE), + 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_TRUE), + 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_TRUE), + }; + *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; +} diff --git a/src/libstrongswan/plugins/frodo/frodo_plugin.h b/src/libstrongswan/plugins/frodo/frodo_plugin.h new file mode 100644 index 000000000..d40fb72ad --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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 + +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_ @}*/ diff --git a/src/libstrongswan/plugins/frodo/frodo_utils.c b/src/libstrongswan/plugins/frodo/frodo_utils.c new file mode 100644 index 000000000..6de0a498a --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_utils.c @@ -0,0 +1,507 @@ +/* + * MIT License + * + * Copyright (C) Microsoft Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "frodo_utils.h" + +/** + * See header + */ +void frodo_pack(u_char *out, size_t outlen, uint16_t *in, size_t inlen, + u_char lsb) +{ + size_t i = 0; /* whole bytes already filled in */ + size_t j = 0; /* whole uint16_t already copied */ + uint16_t w = 0; /* the leftover, not yet copied */ + u_char bits = 0; /* the number of lsb in w */ + + memset(out, 0x00, outlen); + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) + { + u_char b = 0; /* bits in out[i] already filled in */ + + /** + * in: | | |********|********| + * ^ + * j + * w : | ****| + * ^ + * bits + * out:|**|**|**|**|**|**|**|**|* | + * ^^ + * ib + */ + while (b < 8) + { + int nbits = min(8 - b, bits); + uint16_t mask = (1 << nbits) - 1; + u_char t = (w >> (bits - nbits)) & mask; /* the bits to copy from w to out */ + + out[i] = out[i] + (t << (8 - b - nbits)); + b += nbits; + bits -= nbits; + + /* not strictly necessary; mostly for debugging */ + w &= ~(mask << bits); + + if (bits == 0) + { + if (j < inlen) + { + w = in[j]; + bits = lsb; + j++; + } + else + { + break; /* the input vector is exhausted */ + } + } + } + if (b == 8) + { + i++; /* out[i] is filled in */ + } + } +} + +/** + * See header + */ +void frodo_unpack(uint16_t *out, size_t outlen, u_char *in, size_t inlen, + u_char lsb) +{ + size_t i = 0; /* whole uint16_t already filled in */ + size_t j = 0; /* whole bytes already copied */ + u_char w = 0; /* the leftover, not yet copied */ + u_char bits = 0; /* the number of lsb bits of w */ + + memset(out, 0, outlen * sizeof(uint16_t)); + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) + { + u_char b = 0; /* bits in out[i] already filled in */ + + /** + * in: | | | | | | |**|**|... + * ^ + * j + * w : | *| + * ^ + * bits + * out:| *****| *****| *** | |... + * ^ ^ + * i b + */ + while (b < lsb) + { + int nbits = min(lsb - b, bits); + uint16_t mask = (1 << nbits) - 1; + u_char t = (w >> (bits - nbits)) & mask; /* the bits to copy from w to out */ + + out[i] = out[i] + (t << (lsb - b - nbits)); + b += nbits; + bits -= nbits; + + /* not strictly necessary; mostly for debugging */ + w &= ~(mask << bits); + + if (bits == 0) + { + if (j < inlen) + { + w = in[j]; + bits = 8; + j++; + } + else + { + break; /* the input vector is exhausted */ + } + } + } + if (b == lsb) + { + i++; /* out[i] is filled in */ + } + } +} + +/** + * See header + */ +void frodo_sample_n(const frodo_params_t *params, uint16_t *s, size_t n) +{ + int i, j; + + for (i = 0; i < n; i++) + { + uint8_t sample = 0; + uint16_t prnd = s[i] >> 1; /* Drop the least significant bit */ + uint8_t sign = s[i] & 0x1; /* Pick the least significant bit */ + + /* No need to compare with the last value */ + for (j = 0; j < params->cdf_table_len - 1; j++) + { + /** + * Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. + * Uses the fact that CDF_TABLE[j] and s fit in 15 bits + */ + sample += (uint16_t)(params->cdf_table[j] - prnd) >> 15; + } + /* Assuming that sign is either 0 or 1, flips sample iff sign = 1 */ + s[i] = ((-sign) ^ sample) + sign; + } +} + +/** + * Generate square matrix A by using SHAKE128 + */ +static bool generate_matrix_by_shake(const frodo_params_t *params, uint16_t *A, + uint8_t *seed_A) +{ + const uint32_t seed_A_len = params->seed_A_len; + const uint32_t n = params->n; + + uint8_t seed_A_separated[2 + seed_A_len]; + uint16_t *seed_A_origin = (uint16_t*)&seed_A_separated; + uint16_t i; + bool success = FALSE; + xof_t *xof; + + memcpy(&seed_A_separated[2], seed_A, seed_A_len); + + /* Instantiate a SHAKE-128 eXtended Output Function */ + xof = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128); + if (!xof) + { + DBG1(DBG_LIB, "could not instantiate %N", ext_out_function_names, + XOF_SHAKE_128); + return FALSE; + } + for (i = 0; i < n; i++) + { + seed_A_origin[0] = i; + + if (!xof->set_seed(xof, chunk_create(seed_A_separated, 2 + seed_A_len))) + { + goto err; + } + if (!xof->get_bytes(xof, 2*n, (uint8_t*)(A + i*n))) + { + goto err; + } + } + success = TRUE; + +err: + xof->destroy(xof); + return success; +} + +/** + * Generate square matrix A by using AES128 + */ +static bool generate_matrix_by_aes(const frodo_params_t *params, uint16_t *A, + uint8_t *seed_A) +{ + const uint32_t n = params->n; + const uint32_t A_len = n * n * sizeof(uint16_t); + + crypter_t *crypter; + chunk_t A_chunk; + bool success = FALSE; + uint32_t block_len, step, k; + uint16_t i, j; + + memset((uint8_t*)A, 0x00, A_len); + + crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_ECB, 16); + if (!crypter) + { + DBG1(DBG_LIB, "could not instantiate AES_ECB-128"); + return FALSE; + } + block_len = crypter->get_block_size(crypter); + step = block_len / sizeof(uint16_t); + + if (!crypter->set_key(crypter, chunk_create(seed_A, params->seed_A_len))) + { + goto err; + } + + /* ECB encryption */ + for (i = 0; i < n; i++) + { + for (j = 0; j < n; j += step) + { + k = i*n + j; + A[k] = i; + A[k + 1] = j; + } + } + A_chunk = chunk_create((uint8_t*)A, A_len); + + if (!crypter->encrypt(crypter, A_chunk, chunk_empty, NULL)) + { + goto err; + } + success = TRUE; + +err: + crypter->destroy(crypter); + return success; +} + +/** + * See header + */ +bool frodo_mul_add_as_plus_e(const frodo_params_t *params, uint16_t *out, + uint16_t *s, uint16_t *e, uint8_t *seed_A, + bool use_aes) +{ + const uint32_t n = params->n; + const uint32_t nb = params->nb; + const uint32_t n_x_n = n * n; + + int16_t A[n_x_n]; + int i, j, k; + + if (use_aes ? !generate_matrix_by_aes (params, A, seed_A) : + !generate_matrix_by_shake(params, A, seed_A)) + { + return FALSE; + } + memcpy(out, e, nb * n * sizeof(uint16_t)); + + for (i = 0; i < n; i++) + { + /* Matrix multiplication-addition A*s + e */ + for (k = 0; k < nb; k++) + { + uint16_t sum = 0; + + for (j = 0; j < n; j++) + { + sum += A[i*n + j] * s[k*n + j]; + } + + /* Adding e. No need to reduce modulo 2^15, + * extra bits are taken care of during packing later on. + */ + out[i*nb + k] += sum;} + } + return TRUE; +} + +/** + * See header + */ +bool frodo_mul_add_sa_plus_e(const frodo_params_t *params, uint16_t *out, + uint16_t *s, uint16_t *e, uint8_t *seed_A, + bool use_aes) +{ + const uint32_t n = params->n; + const uint32_t nb = params->nb; + const uint32_t n_x_n = n * n; + + int16_t A[n_x_n]; + int i, j, k; + + if (use_aes ? !generate_matrix_by_aes (params, A, seed_A) : + !generate_matrix_by_shake(params, A, seed_A)) + { + return FALSE; + } + memcpy(out, e, nb * n * sizeof(uint16_t)); + + /* Matrix multiplication-addition A*s + e*/ + for (i = 0; i < n; i++) + { + for (k = 0; k < nb; k++) + { + uint16_t sum = 0; + + for (j = 0; j < n; j++) + { + sum += A[j*n + i] * s[k*n + j]; + } + + /* Adding e. No need to reduce modulo 2^15, + * extra bits are taken care of during packing later on. + */ + out[k*n + i] += sum; + } + } + + return TRUE; +} + +/** + * See header + */ +void frodo_mul_add_sb_plus_e(const frodo_params_t *params, uint16_t *out, + uint16_t *b, uint16_t *s, uint16_t *e) +{ + const uint32_t n = params->n; + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + + int i, j, k; + + for (k = 0; k < nb; k++) + { + for (i = 0; i < nb; i++) + { + out[k*nb + i] = e[k*nb + i]; + for (j = 0; j < n; j++) + { + out[k*nb + i] += s[k*n + j] * b[j*nb + i]; + } + out[k*nb + i] = (uint32_t)(out[k*nb + i]) & ((1 << log_q) - 1); + } + } +} + +/** + * See header + */ +void frodo_mul_bs(const frodo_params_t *params, uint16_t *out, + uint16_t *b, uint16_t *s) +{ + const uint32_t n = params->n; + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + + int i, j, k; + + for (i = 0; i < nb; i++) + { + for (j = 0; j < nb; j++) + { + out[i*nb + j] = 0; + for (k = 0; k < n; k++) + { + out[i*nb + j] += b[i*n + k] * s[j*n + k]; + } + out[i*nb + j] = (uint32_t)(out[i*nb + j]) & ((1 << log_q) - 1); + } + } +} + +/** + * See header + */ +void frodo_add(const frodo_params_t *params, uint16_t *out, + uint16_t *a, uint16_t *b) +{ + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + + u_int i; + + for (i = 0; i < (nb * nb); i++) + { + out[i] = (a[i] + b[i]) & ((1 << log_q) - 1); + } +} + +void frodo_sub(const frodo_params_t *params, uint16_t *out, + uint16_t *a, uint16_t *b) +{ + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + + u_int i; + + for (i = 0; i < (nb * nb); i++) + { + out[i] = (a[i] - b[i]) & ((1 << log_q) - 1); + } +} + +/** + * See header + */ +void frodo_key_encode(const frodo_params_t *params, uint16_t *out, uint16_t *in) +{ + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + const uint32_t extr_bits = params->extr_bits; + + u_int i, j, npieces_word = 8; + u_int nwords = (nb * nb)/8; + uint64_t temp, mask = ((uint64_t)1 << extr_bits) - 1; + uint16_t* pos = out; + + for (i = 0; i < nwords; i++) + { + temp = 0; + for(j = 0; j < extr_bits; j++) + { + temp |= ((uint64_t)((uint8_t*)in)[i*extr_bits + j]) << (8*j); + } + for (j = 0; j < npieces_word; j++) + { + *pos = (uint16_t)((temp & mask) << (log_q - extr_bits)); + temp >>= extr_bits; + pos++; + } + } +} + +/** + * See header + */ +void frodo_key_decode(const frodo_params_t *params, uint16_t *out, uint16_t *in) +{ + const uint32_t nb = params->nb; + const uint32_t log_q = params->log_q; + const uint32_t extr_bits = params->extr_bits; + + u_int i, j, index = 0, npieces_word = 8; + u_int nwords = (nb * nb) / 8; + uint16_t temp; + u_int maskex = ((uint16_t)1 << extr_bits) - 1; + u_int maskq = ((uint16_t)1 << log_q) - 1; + uint8_t *pos = (uint8_t*)out; + uint64_t templong; + + for (i = 0; i < nwords; i++) + { + templong = 0; + for (j = 0; j < npieces_word; j++) + { + /* temp = floor(in*2^{-11}+0.5) */ + temp = ((in[index] & maskq) + + (1 << (log_q - extr_bits - 1))) >> (log_q - extr_bits); + templong |= ((uint64_t)(temp & maskex)) << (extr_bits * j); + index++; + } + for(j = 0; j < extr_bits; j++) + { + pos[i*extr_bits + j] = (templong >> (8*j)) & 0xFF; + } + } +} \ No newline at end of file diff --git a/src/libstrongswan/plugins/frodo/frodo_utils.h b/src/libstrongswan/plugins/frodo/frodo_utils.h new file mode 100644 index 000000000..fc35c8632 --- /dev/null +++ b/src/libstrongswan/plugins/frodo/frodo_utils.h @@ -0,0 +1,169 @@ +/* + * 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 + +/** + * 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 + * @param outlen + * @param in + * @param inlen + * @param lsb + */ +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 + * @param outlen + * @param in + * @param inlen + * @param lsb + */ +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 (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 out = 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, + * + * @param params parameter set + * @param out out = 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 + * + * @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 out = 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 out = 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 c = a + b (N_BAR x N_AR) + * @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 c = a - b (N_BAR x N_AR) + * @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 + * + * @param params parameter set + * @param out encoded key + * @param in key to be encoded + */ +void frodo_key_encode(const frodo_params_t *params, uint16_t *out, uint16_t *in); + +/** + * Decode + * + * @param params parameter set + * @param out decoded key + * @param in key to be decoded + */ +void frodo_key_decode(const frodo_params_t *params, uint16_t *out, uint16_t *in); + +#endif /** FRODO_UTILS_H_ @}*/