summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/bliss/bliss_public_key.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/bliss/bliss_public_key.c')
-rw-r--r--src/libstrongswan/plugins/bliss/bliss_public_key.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/bliss/bliss_public_key.c b/src/libstrongswan/plugins/bliss/bliss_public_key.c
new file mode 100644
index 000000000..0175b0f8e
--- /dev/null
+++ b/src/libstrongswan/plugins/bliss/bliss_public_key.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2014 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_public_key.h"
+#include "bliss_signature.h"
+#include "bliss_bitpacker.h"
+#include "bliss_fft.h"
+#include "bliss_utils.h"
+
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
+#include <asn1/oid.h>
+
+typedef struct private_bliss_public_key_t private_bliss_public_key_t;
+
+/**
+ * Private data structure with signing context.
+ */
+struct private_bliss_public_key_t {
+ /**
+ * Public interface for this signer.
+ */
+ bliss_public_key_t public;
+
+ /**
+ * BLISS signature parameter set
+ */
+ bliss_param_set_t *set;
+
+ /**
+ * NTT of BLISS public key a (coefficients of polynomial (2g + 1)/f)
+ */
+ uint32_t *A;
+
+ /**
+ * reference counter
+ */
+ refcount_t ref;
+};
+
+METHOD(public_key_t, get_type, key_type_t,
+ private_bliss_public_key_t *this)
+{
+ return KEY_BLISS;
+}
+
+/**
+ * Verify a BLISS signature based on a SHA-512 hash
+ */
+static bool verify_bliss(private_bliss_public_key_t *this, hash_algorithm_t alg,
+ chunk_t data, chunk_t signature)
+{
+ int i, n;
+ int32_t *z1, *u;
+ int16_t *ud, *z2d;
+ uint16_t q, q2, p, *c_indices, *indices;
+ uint32_t *az;
+ uint8_t data_hash_buf[HASH_SIZE_SHA512];
+ chunk_t data_hash;
+ hasher_t *hasher;
+ bliss_fft_t *fft;
+ bliss_signature_t *sig;
+ bool success = FALSE;
+
+ /* 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;
+ }
+
+ sig = bliss_signature_create_from_data(this->set, signature);
+ if (!sig)
+ {
+ hasher->destroy(hasher);
+ return FALSE;
+ }
+ sig->get_parameters(sig, &z1, &z2d, &c_indices);
+
+ if (!bliss_utils_check_norms(this->set, z1, z2d))
+ {
+ hasher->destroy(hasher);
+ sig->destroy(sig);
+ return FALSE;
+ }
+
+ /* Initialize a couple of needed variables */
+ n = this->set->n;
+ q = this->set->q;
+ p = this->set->p;
+ q2 = 2 * q;
+ az = malloc(n * sizeof(uint32_t));
+ u = malloc(n * sizeof(int32_t));
+ ud = malloc(n * sizeof(int16_t));
+ indices = malloc(this->set->kappa * sizeof(uint16_t));
+
+ for (i = 0; i < n; i++)
+ {
+ az[i] = z1[i] < 0 ? q + z1[i] : z1[i];
+ }
+ fft = bliss_fft_create(this->set->fft_params);
+ fft->transform(fft, az, az, FALSE);
+
+ for (i = 0; i < n; i++)
+ {
+ az[i] = (this->A[i] * az[i]) % q;
+ }
+ fft->transform(fft, az, az, TRUE);
+
+ for (i = 0; i < n; i++)
+ {
+ u[i] = (2 * this->set->q2_inv * az[i]) % q2;
+ }
+
+ for (i = 0; i < this->set->kappa; i++)
+ {
+ u[c_indices[i]] = (u[c_indices[i]] + q * this->set->q2_inv) % q2;
+ }
+ bliss_utils_round_and_drop(this->set, u, ud);
+
+ for (i = 0; i < n; i++)
+ {
+ ud[i] += z2d[i];
+ if (ud[i] < 0)
+ {
+ ud[i] += p;
+ }
+ else if (ud[i] >= p)
+ {
+ ud[i] -= p;
+ }
+ }
+
+ /* Detailed debugging information */
+ DBG3(DBG_LIB, " i u[i] ud[i] z2d[i]");
+ for (i = 0; i < n; i++)
+ {
+ DBG3(DBG_LIB, "%3d %6d %4d %4d", i, u[i], ud[i], z2d[i]);
+ }
+
+ if (!bliss_utils_generate_c(hasher, data_hash, ud, n, this->set->kappa,
+ indices))
+ {
+ goto end;
+ }
+
+ for (i = 0; i < this->set->kappa; i++)
+ {
+ if (indices[i] != c_indices[i])
+ {
+ DBG1(DBG_LIB, "signature verification failed");
+ goto end;
+ }
+ }
+ success = TRUE;
+
+end:
+ /* cleanup */
+ hasher->destroy(hasher);
+ sig->destroy(sig);
+ fft->destroy(fft);
+ free(az);
+ free(u);
+ free(ud);
+ free(indices);
+
+ return success;
+}
+
+METHOD(public_key_t, verify, bool,
+ private_bliss_public_key_t *this, signature_scheme_t scheme,
+ chunk_t data, chunk_t signature)
+{
+ switch (scheme)
+ {
+ case SIGN_BLISS_WITH_SHA256:
+ return verify_bliss(this, HASH_SHA256, data, signature);
+ case SIGN_BLISS_WITH_SHA384:
+ return verify_bliss(this, HASH_SHA384, data, signature);
+ case SIGN_BLISS_WITH_SHA512:
+ return verify_bliss(this, HASH_SHA512, data, signature);
+ default:
+ DBG1(DBG_LIB, "signature scheme %N not supported by BLISS",
+ signature_scheme_names, scheme);
+ return FALSE;
+ }
+}
+
+METHOD(public_key_t, encrypt_, bool,
+ private_bliss_public_key_t *this, encryption_scheme_t scheme,
+ chunk_t plain, chunk_t *crypto)
+{
+ DBG1(DBG_LIB, "encryption scheme %N not supported",
+ encryption_scheme_names, scheme);
+ return FALSE;
+}
+
+METHOD(public_key_t, get_keysize, int,
+ private_bliss_public_key_t *this)
+{
+ return this->set->strength;
+}
+
+METHOD(public_key_t, get_encoding, bool,
+ private_bliss_public_key_t *this, cred_encoding_type_t type,
+ chunk_t *encoding)
+{
+ bool success = TRUE;
+
+ *encoding = bliss_public_key_info_encode(this->set->oid, this->A, this->set);
+
+ if (type != PUBKEY_SPKI_ASN1_DER)
+ {
+ chunk_t asn1_encoding = *encoding;
+
+ success = lib->encoding->encode(lib->encoding, type,
+ NULL, encoding, CRED_PART_BLISS_PUB_ASN1_DER,
+ asn1_encoding, CRED_PART_END);
+ chunk_clear(&asn1_encoding);
+ }
+ return success;
+}
+
+METHOD(public_key_t, get_fingerprint, bool,
+ private_bliss_public_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(public_key_t, get_ref, public_key_t*,
+ private_bliss_public_key_t *this)
+{
+ ref_get(&this->ref);
+ return &this->public.key;
+}
+
+METHOD(public_key_t, destroy, void,
+ private_bliss_public_key_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ lib->encoding->clear_cache(lib->encoding, this);
+ free(this->A);
+ free(this);
+ }
+}
+
+/**
+ * ASN.1 definition of a BLISS public key
+ */
+static const asn1Object_t pubkeyObjects[] = {
+ { 0, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */
+ { 1, "algorithm", ASN1_EOC, ASN1_RAW }, /* 1 */
+ { 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 2 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define BLISS_SUBJECT_PUBLIC_KEY_ALGORITHM 1
+#define BLISS_SUBJECT_PUBLIC_KEY 2
+
+/**
+ * See header.
+ */
+bliss_public_key_t *bliss_public_key_load(key_type_t type, va_list args)
+{
+ private_bliss_public_key_t *this;
+ chunk_t blob = chunk_empty, object, param;
+ asn1_parser_t *parser;
+ bool success = FALSE;
+ int objectID, oid;
+
+ while (TRUE)
+ {
+ switch (va_arg(args, builder_part_t))
+ {
+ case BUILD_BLOB_ASN1_DER:
+ blob = va_arg(args, chunk_t);
+ continue;
+ case BUILD_END:
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ }
+
+ if (blob.len == 0)
+ {
+ return NULL;
+ }
+
+ INIT(this,
+ .public = {
+ .key = {
+ .get_type = _get_type,
+ .verify = _verify,
+ .encrypt = _encrypt_,
+ .equals = public_key_equals,
+ .get_keysize = _get_keysize,
+ .get_fingerprint = _get_fingerprint,
+ .has_fingerprint = public_key_has_fingerprint,
+ .get_encoding = _get_encoding,
+ .get_ref = _get_ref,
+ .destroy = _destroy,
+ },
+ },
+ .ref = 1,
+ );
+
+ parser = asn1_parser_create(pubkeyObjects, blob);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case BLISS_SUBJECT_PUBLIC_KEY_ALGORITHM:
+ {
+ oid = asn1_parse_algorithmIdentifier(object,
+ parser->get_level(parser)+1, &param);
+ if (oid != OID_BLISS_PUBLICKEY)
+ {
+ goto end;
+ }
+ if (!asn1_parse_simple_object(&param, ASN1_OID,
+ parser->get_level(parser)+3, "blissKeyType"))
+ {
+ goto end;
+ }
+ oid = asn1_known_oid(param);
+ if (oid == OID_UNKNOWN)
+ {
+ goto end;
+ }
+ this->set = bliss_param_set_get_by_oid(oid);
+ if (this->set == NULL)
+ {
+ goto end;
+ }
+ break;
+ }
+ case BLISS_SUBJECT_PUBLIC_KEY:
+ if (!bliss_public_key_from_asn1(object, this->set, &this->A))
+ {
+ goto end;
+ }
+ break;
+ }
+ }
+ success = parser->success(parser);
+
+end:
+ parser->destroy(parser);
+ if (!success)
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
+
+/**
+ * See header.
+ */
+bool bliss_public_key_from_asn1(chunk_t object, bliss_param_set_t *set,
+ uint32_t **pubkey)
+{
+ bliss_bitpacker_t *packer;
+ uint32_t coefficient;
+ uint16_t needed_bits;
+ int i;
+
+ /* skip initial bit string octet defining unused bits */
+ object = chunk_skip(object, 1);
+
+ needed_bits = set->n * set->q_bits;
+
+ if (8 * object.len < needed_bits)
+ {
+ return FALSE;
+ }
+ *pubkey = malloc(set->n * sizeof(uint32_t));
+
+ packer = bliss_bitpacker_create_from_data(object);
+
+ for (i = 0; i < set->n; i++)
+ {
+ packer->read_bits(packer, &coefficient, set->q_bits);
+ if (coefficient >= set->q)
+ {
+ packer->destroy(packer);
+ return FALSE;
+ }
+ (*pubkey)[i] = coefficient;
+ }
+ packer->destroy(packer);
+
+ return TRUE;
+}
+
+/**
+ * See header.
+ */
+chunk_t bliss_public_key_encode(uint32_t *pubkey, bliss_param_set_t *set)
+{
+ bliss_bitpacker_t *packer;
+ chunk_t encoding;
+ int i;
+
+ packer = bliss_bitpacker_create(set->n * set->q_bits);
+
+ for (i = 0; i < set->n; i++)
+ {
+ packer->write_bits(packer, pubkey[i], set->q_bits);
+ }
+ encoding = packer->extract_buf(packer);
+ packer->destroy(packer);
+
+ return encoding;
+}
+
+/**
+ * See header.
+ */
+chunk_t bliss_public_key_info_encode(int oid, uint32_t *pubkey,
+ bliss_param_set_t *set)
+{
+ chunk_t encoding, pubkey_encoding;
+
+ pubkey_encoding = bliss_public_key_encode(pubkey, set);
+
+ encoding = asn1_wrap(ASN1_SEQUENCE, "mm",
+ asn1_wrap(ASN1_SEQUENCE, "mm",
+ asn1_build_known_oid(OID_BLISS_PUBLICKEY),
+ asn1_build_known_oid(oid)),
+ asn1_bitstring("m", pubkey_encoding));
+
+ return encoding;
+}
+
+/**
+ * See header.
+ */
+bool bliss_public_key_fingerprint(int oid, uint32_t *pubkey,
+ bliss_param_set_t *set,
+ cred_encoding_type_t type, chunk_t *fp)
+{
+ hasher_t *hasher;
+ chunk_t key;
+
+ switch (type)
+ {
+ case KEYID_PUBKEY_SHA1:
+ key = bliss_public_key_encode(pubkey, set);
+ break;
+ case KEYID_PUBKEY_INFO_SHA1:
+ key = bliss_public_key_info_encode(oid, pubkey, set);
+ break;
+ default:
+ return FALSE;
+ }
+
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ if (!hasher || !hasher->allocate_hash(hasher, key, fp))
+ {
+ DBG1(DBG_LIB, "SHA1 hash algorithm not supported, fingerprinting failed");
+ DESTROY_IF(hasher);
+ free(key.ptr);
+
+ return FALSE;
+ }
+ hasher->destroy(hasher);
+ free(key.ptr);
+
+ return TRUE;
+}
+