]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
frodo: FrodoKEM KE method
authorAndreas Steffen <andreas.steffen@strongswan.org>
Wed, 6 Nov 2019 22:08:43 +0000 (23:08 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 23 May 2025 09:42:39 +0000 (11:42 +0200)
configure.ac
src/libstrongswan/Makefile.am
src/libstrongswan/plugins/frodo/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_kem.c [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_kem.h [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_params.c [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_params.h [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_plugin.c [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_plugin.h [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_utils.c [new file with mode: 0644]
src/libstrongswan/plugins/frodo/frodo_utils.h [new file with mode: 0644]

index c410523d1cce54dcf87de4fa7fb32e0127bf11e8..0ee3afebb7bbeaffe0ed33b63876ca0c33b43906 100644 (file)
@@ -148,6 +148,7 @@ ARG_ENABL_SET([md4],            [enable MD4 software implementation plugin.])
 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.])
@@ -1594,6 +1595,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([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])
@@ -1763,6 +1765,7 @@ AM_CONDITIONAL(USE_GCM, test x$gcm = xtrue)
 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
@@ -2045,6 +2048,7 @@ AC_CONFIG_FILES([
        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
index 81b0a7943b49cad9e5cdd75a625c686b29086a89..0519e9bfc6bc1e8c3e735226510b6b4c12989432 100644 (file)
@@ -697,6 +697,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 (file)
index 0000000..3c3e87e
--- /dev/null
@@ -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_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
diff --git a/src/libstrongswan/plugins/frodo/frodo_kem.c b/src/libstrongswan/plugins/frodo/frodo_kem.c
new file mode 100644 (file)
index 0000000..2485451
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * 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;
+}
diff --git a/src/libstrongswan/plugins/frodo/frodo_kem.h b/src/libstrongswan/plugins/frodo/frodo_kem.h
new file mode 100644 (file)
index 0000000..1078e31
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_ @}*/
diff --git a/src/libstrongswan/plugins/frodo/frodo_params.c b/src/libstrongswan/plugins/frodo/frodo_params.c
new file mode 100644 (file)
index 0000000..eb270be
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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;
+}
diff --git a/src/libstrongswan/plugins/frodo/frodo_params.h b/src/libstrongswan/plugins/frodo/frodo_params.h
new file mode 100644 (file)
index 0000000..960a1ca
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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_ @}*/
diff --git a/src/libstrongswan/plugins/frodo/frodo_plugin.c b/src/libstrongswan/plugins/frodo/frodo_plugin.c
new file mode 100644 (file)
index 0000000..2e6d8db
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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;
+}
diff --git a/src/libstrongswan/plugins/frodo/frodo_plugin.h b/src/libstrongswan/plugins/frodo/frodo_plugin.h
new file mode 100644 (file)
index 0000000..331175e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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_ @}*/
diff --git a/src/libstrongswan/plugins/frodo/frodo_utils.c b/src/libstrongswan/plugins/frodo/frodo_utils.c
new file mode 100644 (file)
index 0000000..ebb98ce
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * 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;
+               }
+       }
+}
diff --git a/src/libstrongswan/plugins/frodo/frodo_utils.h b/src/libstrongswan/plugins/frodo/frodo_utils.h
new file mode 100644 (file)
index 0000000..7d8ee99
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * 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_ @}*/