summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/bliss/bliss_private_key.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/bliss/bliss_private_key.c')
-rw-r--r--src/libstrongswan/plugins/bliss/bliss_private_key.c1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/bliss/bliss_private_key.c b/src/libstrongswan/plugins/bliss/bliss_private_key.c
new file mode 100644
index 000000000..e1064d2f2
--- /dev/null
+++ b/src/libstrongswan/plugins/bliss/bliss_private_key.c
@@ -0,0 +1,1316 @@
+/*
+ * Copyright (C) 2014-2015 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 <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 "bliss_private_key.h"
+#include "bliss_public_key.h"
+#include "bliss_param_set.h"
+#include "bliss_utils.h"
+#include "bliss_sampler.h"
+#include "bliss_signature.h"
+#include "bliss_bitpacker.h"
+#include "bliss_fft.h"
+
+#include <crypto/mgf1/mgf1_bitspender.h>
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
+#include <asn1/oid.h>
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+
+typedef struct private_bliss_private_key_t private_bliss_private_key_t;
+
+#define SECRET_KEY_TRIALS_MAX 50
+
+/**
+ * Private data of a bliss_private_key_t object.
+ */
+struct private_bliss_private_key_t {
+ /**
+ * Public interface for this signer.
+ */
+ bliss_private_key_t public;
+
+ /**
+ * BLISS signature parameter set
+ */
+ bliss_param_set_t *set;
+
+ /**
+ * BLISS secret key S1 (coefficients of polynomial f)
+ */
+ int8_t *s1;
+
+ /**
+ * BLISS secret key S2 (coefficients of polynomial 2g + 1)
+ */
+ int8_t *s2;
+
+ /**
+ * NTT of BLISS public key a (coefficients of polynomial (2g + 1)/f)
+ */
+ uint32_t *A;
+
+ /**
+ * reference count
+ */
+ refcount_t ref;
+};
+
+METHOD(private_key_t, get_type, key_type_t,
+ private_bliss_private_key_t *this)
+{
+ return KEY_BLISS;
+}
+
+/**
+ * Multiply secret vector s with binary challenge vector c
+ */
+static void multiply_by_c(int8_t *s, int n, uint16_t *c_indices,
+ uint16_t kappa, int32_t *product)
+{
+ int i, j, index;
+
+ for (i = 0; i < n; i++)
+ {
+ product[i] = 0;
+
+ for (j = 0; j < kappa; j++)
+ {
+ index = c_indices[j];
+ if (i - index < 0)
+ {
+ product[i] -= s[i - index + n];
+ }
+ else
+ {
+ product[i] += s[i - index];
+ }
+ }
+ }
+}
+
+/**
+ * BLISS-B GreedySC algorithm
+ */
+static void greedy_sc(int8_t *s1, int8_t *s2, int n, uint16_t *c_indices,
+ uint16_t kappa, int32_t *v1, int32_t *v2)
+{
+ int i, j, index;
+ int32_t sign;
+
+ for (i = 0; i < n; i++)
+ {
+ v1[i] = v2[i] = 0;
+ }
+ for (j = 0; j < kappa; j++)
+ {
+ index = c_indices[j];
+ sign = 0;
+
+ for (i = 0; i < index; i++)
+ {
+ sign -= (v1[i] * s1[i - index + n] + v2[i] * s2[i - index + n]);
+ }
+ for (i = index; i < n; i++)
+ {
+ sign += (v1[i] * s1[i - index] + v2[i] * s2[i - index]);
+ }
+ for (i = 0; i < index; i++)
+ {
+ if (sign > 0)
+ {
+ v1[i] += s1[i - index + n];
+ v2[i] += s2[i - index + n];
+ }
+ else
+ {
+ v1[i] -= s1[i - index + n];
+ v2[i] -= s2[i - index + n];
+ }
+ }
+ for (i = index; i < n; i++)
+ {
+ if (sign > 0)
+ {
+ v1[i] -= s1[i - index];
+ v2[i] -= s2[i - index];
+ }
+ else
+ {
+ v1[i] += s1[i - index];
+ v2[i] += s2[i - index];
+ }
+ }
+ }
+}
+
+/**
+ * Compute a BLISS signature
+ */
+static bool sign_bliss(private_bliss_private_key_t *this, hash_algorithm_t alg,
+ chunk_t data, chunk_t *signature)
+{
+ bliss_fft_t *fft;
+ bliss_signature_t *sig;
+ bliss_sampler_t *sampler = NULL;
+ rng_t *rng;
+ hasher_t *hasher;
+ hash_algorithm_t mgf1_alg;
+ size_t mgf1_seed_len;
+ uint8_t mgf1_seed_buf[HASH_SIZE_SHA512], data_hash_buf[HASH_SIZE_SHA512];
+ chunk_t mgf1_seed, data_hash;
+ uint16_t q, q2, p, p2, *c_indices, tests = 0;
+ uint32_t *ay;
+ int32_t *y1, *y2, *z1, *z2, *u, *s1c, *s2c;
+ int32_t y1_min = 0, y1i, y1_max = 0, y2_min = 0, y2i, y2_max = 0;
+ int32_t scalar, norm, ui;
+ int16_t *ud, *uz2d, *z2d, value;
+ int i, n;
+ double mean1 = 0, mean2 = 0, sigma1 = 0, sigma2 = 0;
+ bool accepted, positive, success = FALSE, use_bliss_b;
+
+ /* Initialize signature */
+ *signature = chunk_empty;
+
+ /* Create data hash */
+ hasher = lib->crypto->create_hasher(lib->crypto, alg);
+ if (!hasher)
+ {
+ return FALSE;
+ }
+ data_hash = chunk_create(data_hash_buf, hasher->get_hash_size(hasher));
+
+ if (!hasher->get_hash(hasher, data, data_hash_buf))
+ {
+ hasher->destroy(hasher);
+ return FALSE;
+ }
+ hasher->destroy(hasher);
+
+ /* Create SHA512 hasher for c_indices oracle */
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA512);
+ if (!hasher)
+ {
+ return FALSE;
+ }
+
+ /* Set MGF1 hash algorithm and seed length based on security strength */
+ if (this->set->strength > 160)
+ {
+ mgf1_alg = HASH_SHA256;
+ mgf1_seed_len = HASH_SIZE_SHA256;
+ }
+ else
+ {
+ mgf1_alg = HASH_SHA1;
+ mgf1_seed_len = HASH_SIZE_SHA1;
+ }
+ mgf1_seed = chunk_create(mgf1_seed_buf, mgf1_seed_len);
+
+ rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+ if (!rng)
+ {
+ hasher->destroy(hasher);
+ return FALSE;
+ }
+
+ /* Initialize a couple of needed variables */
+ n = this->set->n;
+ q = this->set->q;
+ p = this->set->p;
+ q2 = 2 * q;
+ p2 = p / 2;
+ ay = malloc(n * sizeof(uint32_t));
+ z2 = malloc(n * sizeof(int32_t));
+ s1c = malloc(n * sizeof(int32_t));
+ s2c = malloc(n * sizeof(int32_t));
+ u = malloc(n * sizeof(int32_t));
+ uz2d = malloc(n * sizeof(int16_t));
+
+ sig = bliss_signature_create(this->set);
+ sig->get_parameters(sig, &z1, &z2d, &c_indices);
+ y1 = z1;
+ y2 = z2;
+ ud = z2d;
+
+ fft = bliss_fft_create(this->set->fft_params);
+
+ /* Use of the enhanced BLISS-B signature algorithm? */
+ switch (this->set->id)
+ {
+ default:
+ case BLISS_I:
+ case BLISS_II:
+ case BLISS_III:
+ case BLISS_IV:
+ use_bliss_b = FALSE;
+ break;
+ case BLISS_B_I:
+ case BLISS_B_II:
+ case BLISS_B_III:
+ case BLISS_B_IV:
+ use_bliss_b = TRUE;
+ break;
+ }
+
+ while (true)
+ {
+ tests++;
+
+ if (!rng->get_bytes(rng, mgf1_seed_len, mgf1_seed_buf))
+ {
+ goto end;
+ }
+ DESTROY_IF(sampler);
+
+ sampler = bliss_sampler_create(mgf1_alg, mgf1_seed, this->set);
+ if (!sampler)
+ {
+ goto end;
+ }
+
+ /* Gaussian sampling for vectors y1 and y2 */
+ for (i = 0; i < n; i++)
+ {
+ if (!sampler->gaussian(sampler, &y1i) ||
+ !sampler->gaussian(sampler, &y2i))
+ {
+ goto end;
+ }
+ y1[i] = y1i;
+ y2[i] = y2i;
+
+ /* Collect statistical data on rejection sampling */
+ if (i == 0)
+ {
+ y1_min = y1_max = y1i;
+ y2_min = y2_max = y2i;
+ }
+ else
+ {
+ if (y1i < y1_min)
+ {
+ y1_min = y1i;
+ }
+ else if (y1i > y1_max)
+ {
+ y1_max = y1i;
+ }
+ if (y2i < y2_min)
+ {
+ y2_min = y2i;
+ }
+ else if (y2i > y2_max)
+ {
+ y2_max = y2i;
+ }
+ }
+ mean1 += y1i;
+ mean2 += y2i;
+ sigma1 += y1i * y1i;
+ sigma2 += y2i * y2i;
+
+ ay[i] = y1i < 0 ? q + y1i : y1i;
+ }
+
+ /* Compute statistics on vectors y1 and y2 */
+ mean1 /= n;
+ mean2 /= n;
+ sigma1 /= n;
+ sigma2 /= n;
+ sigma2 -= mean1 * mean1;
+ sigma2 -= mean2 * mean2;
+ DBG2(DBG_LIB, "y1 = %d..%d (sigma2 = %5.0f, mean = %4.1f)",
+ y1_min, y1_max, sigma1, mean1);
+ DBG2(DBG_LIB, "y2 = %d..%d (sigma2 = %5.0f, mean = %4.1f)",
+ y2_min, y2_max, sigma2, mean2);
+
+ fft->transform(fft, ay, ay, FALSE);
+
+ for (i = 0; i < n; i++)
+ {
+ ay[i] = (this->A[i] * ay[i]) % q;
+ }
+ fft->transform(fft, ay, ay, TRUE);
+
+ for (i = 0; i < n; i++)
+ {
+ ui = 2 * this->set->q2_inv * (int32_t)ay[i] + y2[i];
+ u[i] = ((ui < 0) ? q2 + ui : ui) % q2;
+ }
+ bliss_utils_round_and_drop(this->set, u, ud);
+
+ /* Detailed debugging information */
+ DBG3(DBG_LIB, " i u[i] ud[i]");
+ for (i = 0; i < n; i++)
+ {
+ DBG3(DBG_LIB, "%3d %6d %4d", i, u[i], ud[i]);
+ }
+
+ if (!bliss_utils_generate_c(hasher, data_hash, ud, n, this->set->kappa,
+ c_indices))
+ {
+ goto end;
+ }
+
+ if (use_bliss_b)
+ {
+ /* Compute v = (s1c, s2c) with the GreedySC algorithm */
+ greedy_sc(this->s1, this->s2, n, c_indices, this->set->kappa,
+ s1c, s2c);
+
+ /* Compute norm = ||v||^2 = ||Sc'||^2 */
+ norm = bliss_utils_scalar_product(s1c, s1c, n) +
+ bliss_utils_scalar_product(s2c, s2c, n);
+
+ /* Just in case. ||v||^2 <= P_max should always be fulfilled */
+ if (norm > this->set->p_max)
+ {
+ goto end;
+ }
+ }
+ else
+ {
+ /* Compute s*c */
+ multiply_by_c(this->s1, n, c_indices, this->set->kappa, s1c);
+ multiply_by_c(this->s2, n, c_indices, this->set->kappa, s2c);
+
+ /* Compute norm = |Sc||^2 */
+ norm = bliss_utils_scalar_product(s1c, s1c, n) +
+ bliss_utils_scalar_product(s2c, s2c, n);
+ }
+
+ if (!sampler->bernoulli_exp(sampler, this->set->M - norm, &accepted))
+ {
+ goto end;
+ }
+ if (use_bliss_b)
+ {
+ DBG2(DBG_LIB, "norm2(s1*c') + norm2(s2*c') = %u (%u max), %s",
+ norm, this->set->p_max, accepted ? "accepted" : "rejected");
+
+ }
+ else
+ {
+ DBG2(DBG_LIB, "norm2(s1*c) + norm2(s2*c) = %u, %s",
+ norm, accepted ? "accepted" : "rejected");
+ }
+ if (!accepted)
+ {
+ continue;
+ }
+
+ /* Compute z */
+ if (!sampler->sign(sampler, &positive))
+ {
+ goto end;
+ }
+ for (i = 0; i < n; i++)
+ {
+ if (positive)
+ {
+ z1[i] = y1[i] + s1c[i];
+ z2[i] = y2[i] + s2c[i];
+ }
+ else
+ {
+ z1[i] = y1[i] - s1c[i];
+ z2[i] = y2[i] - s2c[i];
+ }
+ }
+ /* Reject with probability 1/cosh(scalar/sigma^2) */
+ scalar = bliss_utils_scalar_product(z1, s1c, n) +
+ bliss_utils_scalar_product(z2, s2c, n);
+
+ if (!sampler->bernoulli_cosh(sampler, scalar, &accepted))
+ {
+ goto end;
+ }
+ DBG2(DBG_LIB, "scalar(z1,s1*c) + scalar(z2,s2*c) = %d, %s",
+ scalar, accepted ? "accepted" : "rejected");
+ if (!accepted)
+ {
+ continue;
+ }
+
+ /* Compute z2 with dropped bits */
+ for (i = 0; i < n; i++)
+ {
+ u[i] -= z2[i];
+ if (u[i] < 0)
+ {
+ u[i] += q2;
+ }
+ else if (u[i] >= q2)
+ {
+ u[i] -= q2;
+ }
+ }
+ bliss_utils_round_and_drop(this->set, u, uz2d);
+
+ for (i = 0; i < n; i++)
+ {
+ value = ud[i] - uz2d[i];
+ if (value <= -p2)
+ {
+ value += p;
+ }
+ else if (value > p2)
+ {
+ value -= p;
+ }
+ z2d[i] = value;
+ }
+
+ if (!bliss_utils_check_norms(this->set, z1, z2d))
+ {
+ continue;
+ }
+
+ *signature = sig->get_encoding(sig);
+ if (signature->len == 0)
+ {
+ DBG1(DBG_LIB, "inefficient Huffman coding of signature");
+ continue;
+ }
+ DBG2(DBG_LIB, "signature generation needed %u round%s", tests,
+ (tests == 1) ? "" : "s");
+ break;
+ }
+ success = TRUE;
+
+end:
+ /* cleanup */
+ DESTROY_IF(sampler);
+ hasher->destroy(hasher);
+ sig->destroy(sig);
+ fft->destroy(fft);
+ rng->destroy(rng);
+ memwipe(s1c, n * sizeof(int32_t));
+ memwipe(s2c, n * sizeof(int32_t));
+ free(s1c);
+ free(s2c);
+ free(ay);
+ free(z2);
+ free(u);
+ free(uz2d);
+
+ return success;
+}
+
+METHOD(private_key_t, sign, bool,
+ private_bliss_private_key_t *this, signature_scheme_t scheme,
+ chunk_t data, chunk_t *signature)
+{
+ switch (scheme)
+ {
+ case SIGN_BLISS_WITH_SHA256:
+ return sign_bliss(this, HASH_SHA256, data, signature);
+ case SIGN_BLISS_WITH_SHA384:
+ return sign_bliss(this, HASH_SHA384, data, signature);
+ case SIGN_BLISS_WITH_SHA512:
+ return sign_bliss(this, HASH_SHA512, data, signature);
+ default:
+ DBG1(DBG_LIB, "signature scheme %N not supported with BLISS",
+ signature_scheme_names, scheme);
+ return FALSE;
+ }
+}
+
+METHOD(private_key_t, decrypt, bool,
+ private_bliss_private_key_t *this, encryption_scheme_t scheme,
+ chunk_t crypto, chunk_t *plain)
+{
+ DBG1(DBG_LIB, "encryption scheme %N not supported",
+ encryption_scheme_names, scheme);
+ return FALSE;
+}
+
+METHOD(private_key_t, get_keysize, int,
+ private_bliss_private_key_t *this)
+{
+ return this->set->strength;
+}
+
+METHOD(private_key_t, get_public_key, public_key_t*,
+ private_bliss_private_key_t *this)
+{
+ public_key_t *public;
+ chunk_t pubkey;
+
+ pubkey = bliss_public_key_info_encode(this->set->oid, this->A, this->set);
+ public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_BLISS,
+ BUILD_BLOB_ASN1_DER, pubkey, BUILD_END);
+ free(pubkey.ptr);
+
+ return public;
+}
+
+METHOD(private_key_t, get_encoding, bool,
+ private_bliss_private_key_t *this, cred_encoding_type_t type,
+ chunk_t *encoding)
+{
+ switch (type)
+ {
+ case PRIVKEY_ASN1_DER:
+ case PRIVKEY_PEM:
+ {
+ chunk_t s1, s2, pubkey;
+ bliss_bitpacker_t *packer;
+ size_t s_bits;
+ int8_t value;
+ bool success = TRUE;
+ int i;
+
+ pubkey = bliss_public_key_encode(this->A, this->set);
+
+ /* Use either 2 or 3 bits per array element */
+ s_bits = 2 + (this->set->non_zero2 > 0);
+
+ /* Encode secret s1 */
+ packer = bliss_bitpacker_create(s_bits * this->set->n);
+ for (i = 0; i < this->set->n; i++)
+ {
+ packer->write_bits(packer, this->s1[i], s_bits);
+ }
+ s1 = packer->extract_buf(packer);
+ packer->destroy(packer);
+
+ /* Encode secret s2 */
+ packer = bliss_bitpacker_create(s_bits * this->set->n);
+ for (i = 0; i < this->set->n; i++)
+ {
+ value = this->s2[i];
+ if (i == 0)
+ {
+ value -= 1;
+ }
+ value /= 2;
+ packer->write_bits(packer, value, s_bits);
+ }
+ s2 = packer->extract_buf(packer);
+ packer->destroy(packer);
+
+ *encoding = asn1_wrap(ASN1_SEQUENCE, "mmss",
+ asn1_build_known_oid(this->set->oid),
+ asn1_bitstring("m", pubkey),
+ asn1_bitstring("m", s1),
+ asn1_bitstring("m", s2)
+ );
+ if (type == PRIVKEY_PEM)
+ {
+ chunk_t asn1_encoding = *encoding;
+
+ success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM,
+ NULL, encoding, CRED_PART_BLISS_PRIV_ASN1_DER,
+ asn1_encoding, CRED_PART_END);
+ chunk_clear(&asn1_encoding);
+ }
+ return success;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+METHOD(private_key_t, get_fingerprint, bool,
+ private_bliss_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
+{
+ bool success;
+
+ if (lib->encoding->get_cache(lib->encoding, type, this, fp))
+ {
+ return TRUE;
+ }
+ success = bliss_public_key_fingerprint(this->set->oid, this->A,
+ this->set, type, fp);
+ if (success)
+ {
+ lib->encoding->cache(lib->encoding, type, this, *fp);
+ }
+ return success;
+}
+
+METHOD(private_key_t, get_ref, private_key_t*,
+ private_bliss_private_key_t *this)
+{
+ ref_get(&this->ref);
+ return &this->public.key;
+}
+
+METHOD(private_key_t, destroy, void,
+ private_bliss_private_key_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ lib->encoding->clear_cache(lib->encoding, this);
+ if (this->s1)
+ {
+ memwipe(this->s1, this->set->n * sizeof(int8_t));
+ free(this->s1);
+ }
+ if (this->s2)
+ {
+ memwipe(this->s2, this->set->n * sizeof(int8_t));
+ free(this->s2);
+ }
+ free(this->A);
+ free(this);
+ }
+}
+
+/**
+ * Internal generic constructor
+ */
+static private_bliss_private_key_t *bliss_private_key_create_empty(void)
+{
+ private_bliss_private_key_t *this;
+
+ INIT(this,
+ .public = {
+ .key = {
+ .get_type = _get_type,
+ .sign = _sign,
+ .decrypt = _decrypt,
+ .get_keysize = _get_keysize,
+ .get_public_key = _get_public_key,
+ .equals = private_key_equals,
+ .belongs_to = private_key_belongs_to,
+ .get_fingerprint = _get_fingerprint,
+ .has_fingerprint = private_key_has_fingerprint,
+ .get_encoding = _get_encoding,
+ .get_ref = _get_ref,
+ .destroy = _destroy,
+ },
+ },
+ .ref = 1,
+ );
+ return this;
+}
+
+/**
+ * Compute the scalar product of a vector x with a negative wrapped vector y
+ */
+static int16_t wrapped_product(int8_t *x, int8_t *y, int n, int shift)
+{
+ int16_t product = 0;
+ int i;
+
+ for (i = 0; i < n - shift; i++)
+ {
+ product += x[i] * y[i + shift];
+ }
+ for (i = n - shift; i < n; i++)
+ {
+ product -= x[i] * y[i + shift - n];
+ }
+ return product;
+}
+
+/**
+ * Apply a negative wrapped rotation to a vector x
+ */
+static void wrap(int16_t *x, int n, int shift, int16_t *x_wrapped)
+{
+ int i;
+
+ for (i = 0; i < n - shift; i++)
+ {
+ x_wrapped[i + shift] = x[i];
+ }
+ for (i = n - shift; i < n; i++)
+ {
+ x_wrapped[i + shift - n] = -x[i];
+ }
+}
+
+/**
+ * int16_t compare function needed for qsort()
+ */
+static int compare(const int16_t *a, const int16_t *b)
+{
+ int16_t temp = *a - *b;
+
+ if (temp > 0)
+ {
+ return 1;
+ }
+ else if (temp < 0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * Compute the Nk(S) norm of S = (s1, s2)
+ */
+static uint32_t nks_norm(int8_t *s1, int8_t *s2, int n, uint16_t kappa)
+{
+ int16_t t[n], t_wrapped[n], max_kappa[n];
+ uint32_t nks = 0;
+ int i, j;
+
+ for (i = 0; i < n; i++)
+ {
+ t[i] = wrapped_product(s1, s1, n, i) + wrapped_product(s2, s2, n, i);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ wrap(t, n, i, t_wrapped);
+ qsort(t_wrapped, n, sizeof(int16_t), (__compar_fn_t)compare);
+ max_kappa[i] = 0;
+
+ for (j = 1; j <= kappa; j++)
+ {
+ max_kappa[i] += t_wrapped[n - j];
+ }
+ }
+ qsort(max_kappa, n, sizeof(int16_t), (__compar_fn_t)compare);
+
+ for (i = 1; i <= kappa; i++)
+ {
+ nks += max_kappa[n - i];
+ }
+ return nks;
+}
+
+/**
+ * Compute the inverse x1 of x modulo q as x^(-1) = x^(q-2) mod q
+ */
+static uint32_t invert(uint32_t x, uint16_t q)
+{
+ uint32_t x1, x2;
+ uint16_t q2;
+ int i, i_max;
+
+ q2 = q - 2;
+ x1 = (q2 & 1) ? x : 1;
+ x2 = x;
+ i_max = 15;
+
+ while ((q2 & (1 << i_max)) == 0)
+ {
+ i_max--;
+ }
+ for (i = 1; i <= i_max; i++)
+ {
+ x2 = (x2 * x2) % q;
+
+ if (q2 & (1 << i))
+ {
+ x1 = (x1 * x2) % q;
+ }
+ }
+
+ return x1;
+}
+
+/**
+ * Create a vector with sparse and small coefficients from seed
+ */
+static int8_t* create_vector_from_seed(private_bliss_private_key_t *this,
+ hash_algorithm_t alg, chunk_t seed)
+{
+ mgf1_bitspender_t *bitspender;
+ uint32_t index, sign;
+ int8_t *vector;
+ int non_zero;
+
+ bitspender = mgf1_bitspender_create(alg, seed, FALSE);
+ if (!bitspender)
+ {
+ return NULL;
+ }
+
+ vector = malloc(sizeof(int8_t) * this->set->n);
+ memset(vector, 0x00, this->set->n);
+
+ non_zero = this->set->non_zero1;
+ while (non_zero)
+ {
+ if (!bitspender->get_bits(bitspender, this->set->n_bits, &index))
+ {
+ free(vector);
+ return NULL;
+ }
+ if (vector[index] != 0)
+ {
+ continue;
+ }
+
+ if (!bitspender->get_bits(bitspender, 1, &sign))
+ {
+ free(vector);
+ return NULL;
+ }
+ vector[index] = sign ? 1 : -1;
+ non_zero--;
+ }
+
+ non_zero = this->set->non_zero2;
+ while (non_zero)
+ {
+ if (!bitspender->get_bits(bitspender, this->set->n_bits, &index))
+ {
+ free(vector);
+ return NULL;
+ }
+ if (vector[index] != 0)
+ {
+ continue;
+ }
+
+ if (!bitspender->get_bits(bitspender, 1, &sign))
+ {
+ free(vector);
+ return NULL;
+ }
+ vector[index] = sign ? 2 : -2;
+ non_zero--;
+ }
+ bitspender->destroy(bitspender);
+
+ return vector;
+}
+
+/**
+ * Generate the secret key S = (s1, s2) fulfilling the Nk(S) norm
+ */
+static bool create_secret(private_bliss_private_key_t *this, rng_t *rng,
+ int8_t **s1, int8_t **s2, int *trials)
+{
+ uint8_t seed_buf[32];
+ uint8_t *f, *g;
+ uint32_t l2_norm, nks;
+ int i, n;
+ chunk_t seed;
+ size_t seed_len;
+ hash_algorithm_t alg;
+
+ n = this->set->n;
+ *s1 = NULL;
+ *s2 = NULL;
+
+ /* Set MGF1 hash algorithm and seed length based on security strength */
+ if (this->set->strength > 160)
+ {
+ alg = HASH_SHA256;
+ seed_len = HASH_SIZE_SHA256;
+ }
+ else
+ {
+ alg = HASH_SHA1;
+ seed_len = HASH_SIZE_SHA1;
+ }
+ seed = chunk_create(seed_buf, seed_len);
+
+ while (*trials < SECRET_KEY_TRIALS_MAX)
+ {
+ (*trials)++;
+
+ if (!rng->get_bytes(rng, seed_len, seed_buf))
+ {
+ return FALSE;
+ }
+ f = create_vector_from_seed(this, alg, seed);
+ if (f == NULL)
+ {
+ return FALSE;
+ }
+ if (!rng->get_bytes(rng, seed_len, seed_buf))
+ {
+ free(f);
+ return FALSE;
+ }
+ g = create_vector_from_seed(this, alg, seed);
+ if (g == NULL)
+ {
+ free(f);
+ return FALSE;
+ }
+
+ /* Compute 2g + 1 */
+ for (i = 0; i < n; i++)
+ {
+ g[i] *= 2;
+ }
+ g[0] += 1;
+
+ l2_norm = wrapped_product(f, f, n, 0) + wrapped_product(g, g, n, 0);
+ nks = nks_norm(f, g, n, this->set->kappa);
+
+ switch (this->set->id)
+ {
+ case BLISS_I:
+ case BLISS_II:
+ case BLISS_III:
+ case BLISS_IV:
+ DBG2(DBG_LIB, "l2 norm of s1||s2: %d, Nk(S): %u (%u max)",
+ l2_norm, nks, this->set->nks_max);
+ if (nks < this->set->nks_max)
+ {
+ *s1 = f;
+ *s2 = g;
+ return TRUE;
+ }
+ free(f);
+ free(g);
+ break;
+ case BLISS_B_I:
+ case BLISS_B_II:
+ case BLISS_B_III:
+ case BLISS_B_IV:
+ DBG2(DBG_LIB, "l2 norm of s1||s2: %d, Nk(S): %u",
+ l2_norm, nks);
+ *s1 = f;
+ *s2 = g;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * See header.
+ */
+bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
+{
+ private_bliss_private_key_t *this;
+ u_int key_size = BLISS_B_I;
+ int i, n, trials = 0;
+ uint32_t *S1, *S2, *a;
+ uint16_t q;
+ bool success = FALSE;
+ bliss_param_set_t *set;
+ bliss_fft_t *fft;
+ rng_t *rng;
+
+ while (TRUE)
+ {
+ switch (va_arg(args, builder_part_t))
+ {
+ case BUILD_KEY_SIZE:
+ key_size = va_arg(args, u_int);
+ continue;
+ case BUILD_END:
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ }
+
+ if (lib->settings->get_bool(lib->settings, "%s.plugins.bliss.use_bliss_b",
+ TRUE, lib->ns))
+ {
+ switch (key_size)
+ {
+ case BLISS_I:
+ key_size = BLISS_B_I;
+ break;
+ case BLISS_II:
+ key_size = BLISS_B_II;
+ break;
+ case BLISS_III:
+ key_size = BLISS_B_III;
+ break;
+ case BLISS_IV:
+ key_size = BLISS_B_IV;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Only BLISS or BLISS-B types I, III, or IV are currently supported */
+ set = bliss_param_set_get_by_id(key_size);
+ if (!set)
+ {
+ DBG1(DBG_LIB, "BLISS parameter set %u not supported", key_size);
+ return NULL;
+ }
+
+ /* Some shortcuts for often used variables */
+ n = set->n;
+ q = set->q;
+
+ if (set->fft_params->n != n || set->fft_params->q != q)
+ {
+ DBG1(DBG_LIB, "FFT parameters do not match BLISS parameters");
+ return NULL;
+ }
+ this = bliss_private_key_create_empty();
+ this->set = set;
+
+ /* We derive the public key from the private key using the FFT */
+ fft = bliss_fft_create(set->fft_params);
+
+ /* Some vectors needed to derive the publi key */
+ S1 = malloc(n * sizeof(uint32_t));
+ S2 = malloc(n * sizeof(uint32_t));
+ a = malloc(n * sizeof(uint32_t));
+ this->A = malloc(n * sizeof(uint32_t));
+
+ /* Instantiate a true random generator */
+ rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE);
+
+ /* Loop until we have an invertible polynomial s1 */
+ do
+ {
+ if (!create_secret(this, rng, &this->s1, &this->s2, &trials))
+ {
+ break;
+ }
+
+ /* Convert signed arrays to unsigned arrays before FFT */
+ for (i = 0; i < n; i++)
+ {
+ S1[i] = (this->s1[i] < 0) ? this->s1[i] + q : this->s1[i];
+ S2[i] = (this->s2[i] > 0) ? q - this->s2[i] : -this->s2[i];
+ }
+ fft->transform(fft, S1, S1, FALSE);
+ fft->transform(fft, S2, S2, FALSE);
+
+ success = TRUE;
+ for (i = 0; i < n; i++)
+ {
+ if (S1[i] == 0)
+ {
+ DBG1(DBG_LIB, "S1[%d] is zero - s1 is not invertible", i);
+ free(this->s1);
+ free(this->s2);
+ this->s1 = NULL;
+ this->s2 = NULL;
+ success = FALSE;
+ break;
+ }
+ this->A[i] = invert(S1[i], q);
+ this->A[i] = (S2[i] * this->A[i]) % q;
+ }
+ }
+ while (!success && trials < SECRET_KEY_TRIALS_MAX);
+
+ DBG1(DBG_LIB, "secret key generation %s after %d trial%s",
+ success ? "succeeded" : "failed", trials, (trials == 1) ? "" : "s");
+
+ if (success)
+ {
+ fft->transform(fft, this->A, a, TRUE);
+
+ DBG4(DBG_LIB, " i f g a F G A");
+ for (i = 0; i < n; i++)
+ {
+ DBG4(DBG_LIB, "%4d %3d %3d %5u %5u %5u %5u",
+ i, this->s1[i], this->s2[i], a[i], S1[i], S2[i], this->A[i]);
+ }
+ }
+ else
+ {
+ destroy(this);
+ }
+
+ /* Cleanup */
+ fft->destroy(fft);
+ rng->destroy(rng);
+ memwipe(S1, n * sizeof(uint32_t));
+ memwipe(S2, n * sizeof(uint32_t));
+ free(S1);
+ free(S2);
+ free(a);
+
+ return success ? &this->public : NULL;
+}
+
+/**
+ * ASN.1 definition of a BLISS private key
+ */
+static const asn1Object_t privkeyObjects[] = {
+ { 0, "BLISSPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "keyType", ASN1_OID, ASN1_BODY }, /* 1 */
+ { 1, "public", ASN1_BIT_STRING, ASN1_BODY }, /* 2 */
+ { 1, "secret1", ASN1_BIT_STRING, ASN1_BODY }, /* 3 */
+ { 1, "secret2", ASN1_BIT_STRING, ASN1_BODY }, /* 4 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define PRIV_KEY_TYPE 1
+#define PRIV_KEY_PUBLIC 2
+#define PRIV_KEY_SECRET1 3
+#define PRIV_KEY_SECRET2 4
+
+/**
+ * See header.
+ */
+bliss_private_key_t *bliss_private_key_load(key_type_t type, va_list args)
+{
+ private_bliss_private_key_t *this;
+ chunk_t key = chunk_empty, object;
+ bliss_bitpacker_t *packer;
+ asn1_parser_t *parser;
+ size_t s_bits = 0;
+ int8_t s, s_min = 0, s_max = 0;
+ uint32_t s_sign = 0x02, s_mask = 0xfffffffc, value;
+ bool success = FALSE;
+ int objectID, oid, i;
+
+ while (TRUE)
+ {
+ switch (va_arg(args, builder_part_t))
+ {
+ case BUILD_BLOB_ASN1_DER:
+ key = va_arg(args, chunk_t);
+ continue;
+ case BUILD_END:
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ }
+
+ if (key.len == 0)
+ {
+ return NULL;
+ }
+ this = bliss_private_key_create_empty();
+
+ parser = asn1_parser_create(privkeyObjects, key);
+ parser->set_flags(parser, FALSE, TRUE);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case PRIV_KEY_TYPE:
+ oid = asn1_known_oid(object);
+ if (oid == OID_UNKNOWN)
+ {
+ goto end;
+ }
+ this->set = bliss_param_set_get_by_oid(oid);
+ if (this->set == NULL)
+ {
+ goto end;
+ }
+ if (lib->settings->get_bool(lib->settings,
+ "%s.plugins.bliss.use_bliss_b",TRUE, lib->ns))
+ {
+ switch (this->set->id)
+ {
+ case BLISS_I:
+ this->set = bliss_param_set_get_by_id(BLISS_B_I);
+ break;
+ case BLISS_III:
+ this->set = bliss_param_set_get_by_id(BLISS_B_III);
+ break;
+ case BLISS_IV:
+ this->set = bliss_param_set_get_by_id(BLISS_B_IV);
+ break;
+ default:
+ break;
+ }
+ }
+ if (this->set->non_zero2)
+ {
+ s_min = -2;
+ s_max = 2;
+ s_bits = 3;
+ }
+ else
+ {
+ s_min = -1;
+ s_max = 1;
+ s_bits = 2;
+ }
+ s_sign = 1 << (s_bits - 1);
+ s_mask = ((1 << (32 - s_bits)) - 1) << s_bits;
+ break;
+ case PRIV_KEY_PUBLIC:
+ if (!bliss_public_key_from_asn1(object, this->set, &this->A))
+ {
+ goto end;
+ }
+ break;
+ case PRIV_KEY_SECRET1:
+ if (object.len != 1 + (s_bits * this->set->n + 7)/8)
+ {
+ goto end;
+ }
+ this->s1 = malloc(this->set->n);
+
+ /* Skip unused bits octet */
+ object = chunk_skip(object, 1);
+ packer = bliss_bitpacker_create_from_data(object);
+ for (i = 0; i < this->set->n; i++)
+ {
+ packer->read_bits(packer, &value, s_bits);
+ s = (value & s_sign) ? value | s_mask : value;
+ if (s < s_min || s > s_max)
+ {
+ packer->destroy(packer);
+ goto end;
+ }
+ this->s1[i] = s;
+ }
+ packer->destroy(packer);
+ break;
+ case PRIV_KEY_SECRET2:
+ if (object.len != 1 + (s_bits * this->set->n + 7)/8)
+ {
+ goto end;
+ }
+ this->s2 = malloc(this->set->n);
+
+ /* Skip unused bits octet */
+ object = chunk_skip(object, 1);
+ packer = bliss_bitpacker_create_from_data(object);
+ for (i = 0; i < this->set->n; i++)
+ {
+ packer->read_bits(packer, &value, s_bits);
+ s = (value & s_sign) ? value | s_mask : value;
+ if (s < s_min || s > s_max)
+ {
+ packer->destroy(packer);
+ goto end;
+ }
+ this->s2[i] = 2 * s;
+ if (i == 0)
+ {
+ this->s2[0] += 1;
+ }
+ }
+ packer->destroy(packer);
+ break;
+ }
+ }
+ success = parser->success(parser);
+
+end:
+ parser->destroy(parser);
+ if (!success)
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
+