diff options
Diffstat (limited to 'src/libstrongswan/plugins/bliss/bliss_public_key.c')
-rw-r--r-- | src/libstrongswan/plugins/bliss/bliss_public_key.c | 515 |
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, ¶m); + if (oid != OID_BLISS_PUBLICKEY) + { + goto end; + } + if (!asn1_parse_simple_object(¶m, 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; +} + |