diff options
Diffstat (limited to 'src/libstrongswan/credentials/keys/signature_params.c')
-rw-r--r-- | src/libstrongswan/credentials/keys/signature_params.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/src/libstrongswan/credentials/keys/signature_params.c b/src/libstrongswan/credentials/keys/signature_params.c new file mode 100644 index 000000000..6b4d22e7b --- /dev/null +++ b/src/libstrongswan/credentials/keys/signature_params.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2017 Tobias Brunner + * 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 "signature_params.h" + +#include <asn1/oid.h> +#include <asn1/asn1_parser.h> + +/** + * Determine the salt length in case it is not configured + */ +static ssize_t rsa_pss_salt_length(rsa_pss_params_t *pss) +{ + ssize_t salt_len = pss->salt_len; + + if (salt_len <= RSA_PSS_SALT_LEN_DEFAULT) + { + salt_len = hasher_hash_size(pss->hash); + if (!salt_len) + { + return -1; + } + } + return salt_len; +} + +/** + * Compare two signature schemes and their parameters + */ +static bool compare_params(signature_params_t *a, signature_params_t *b, + bool strict) +{ + if (!a && !b) + { + return TRUE; + } + if (!a || !b) + { + return FALSE; + } + if (a->scheme != b->scheme) + { + return FALSE; + } + if (!a->params && !b->params) + { + return TRUE; + } + if (a->params && b->params) + { + switch (a->scheme) + { + case SIGN_RSA_EMSA_PSS: + { + rsa_pss_params_t *pss_a = a->params, *pss_b = b->params; + + return pss_a->hash == pss_b->hash && + pss_a->mgf1_hash == pss_b->mgf1_hash && + (!strict || + rsa_pss_salt_length(pss_a) == rsa_pss_salt_length(pss_b)); + } + default: + break; + } + } + return FALSE; +} + +/* + * Described in header + */ +bool signature_params_equal(signature_params_t *a, signature_params_t *b) +{ + return compare_params(a, b, TRUE); +} + +/* + * Described in header + */ +bool signature_params_comply(signature_params_t *c, signature_params_t *s) +{ /* the salt is variable, so it does not necessarily have to be the same */ + return compare_params(c, s, FALSE); +} + +/* + * Described in header + */ +signature_params_t *signature_params_clone(signature_params_t *this) +{ + signature_params_t *clone; + + if (!this) + { + return NULL; + } + + INIT(clone, + .scheme = this->scheme, + ); + if (this->params) + { + switch (this->scheme) + { + case SIGN_RSA_EMSA_PSS: + { + rsa_pss_params_t *pss, *pss_clone; + + pss = this->params; + INIT(pss_clone, + .hash = pss->hash, + .mgf1_hash = pss->mgf1_hash, + .salt_len = pss->salt_len, + /* ignore salt as only used for unit tests */ + ); + clone->params = pss_clone; + break; + } + default: + break; + } + } + return clone; +} + +/* + * Described in header + */ +void signature_params_destroy(signature_params_t *this) +{ + if (this) + { + free(this->params); + free(this); + } +} + +/* + * Described in header + */ +void signature_params_clear(signature_params_t *this) +{ + if (this) + { + free(this->params); + this->params = NULL; + this->scheme = SIGN_UNKNOWN; + } +} + +/* + * Described in header + */ +bool signature_params_parse(chunk_t asn1, int level0, + signature_params_t *params) +{ + chunk_t parameters = chunk_empty; + int oid; + + oid = asn1_parse_algorithmIdentifier(asn1, level0, ¶meters); + params->scheme = signature_scheme_from_oid(oid); + switch (params->scheme) + { + case SIGN_UNKNOWN: + return FALSE; + case SIGN_RSA_EMSA_PSS: + { + rsa_pss_params_t *pss = malloc_thing(rsa_pss_params_t); + + if (!rsa_pss_params_parse(parameters, level0+1, pss)) + { + DBG1(DBG_IKE, "failed parsing RSASSA-PSS parameters"); + free(pss); + return FALSE; + } + params->params = pss; + break; + } + default: + params->params = NULL; + break; + } + return TRUE; +} + +/* + * Described in header + */ +bool signature_params_build(signature_params_t *params, chunk_t *asn1) +{ + chunk_t parameters = chunk_empty; + int oid; + + oid = signature_scheme_to_oid(params->scheme); + if (oid == OID_UNKNOWN) + { + return FALSE; + } + if (params->scheme == SIGN_RSA_EMSA_PSS && + !rsa_pss_params_build(params->params, ¶meters)) + { + return FALSE; + } + if (parameters.len) + { + *asn1 = asn1_algorithmIdentifier_params(oid, parameters); + } + else + { + *asn1 = asn1_algorithmIdentifier(oid); + } + return TRUE; +} + +/** + * ASN.1 definition of RSASSA-PSS-params + */ +static const asn1Object_t RSASSAPSSParamsObjects[] = { + { 0, "RSASSA-PSS-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "DEFAULT SHA-1", ASN1_CONTEXT_C_0, ASN1_DEF }, /* 1 */ + { 2, "hashAlgorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 1, "DEFAULT MGF1SHA1", ASN1_CONTEXT_C_1, ASN1_DEF }, /* 3 */ + { 2, "maskGenAlgorithm",ASN1_EOC, ASN1_RAW }, /* 4 */ + { 1, "DEFAULT 20", ASN1_CONTEXT_C_2, ASN1_DEF }, /* 5 */ + { 2, "saltLength", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 1, "DEFAULT 1", ASN1_CONTEXT_C_3, ASN1_DEF }, /* 7 */ + { 2, "trailerField", ASN1_INTEGER, ASN1_BODY }, /* 8 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define RSASSA_PSS_PARAMS_HASH_ALG 2 +#define RSASSA_PSS_PARAMS_MGF_ALG 4 +#define RSASSA_PSS_PARAMS_SALT_LEN 6 +#define RSASSA_PSS_PARAMS_TRAILER 8 + +/* + * Described in header + */ +bool rsa_pss_params_parse(chunk_t asn1, int level0, rsa_pss_params_t *params) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID, alg; + bool success = FALSE; + + params->hash = HASH_SHA1; + params->mgf1_hash = HASH_SHA1; + params->salt_len = HASH_SIZE_SHA1; + + parser = asn1_parser_create(RSASSAPSSParamsObjects, asn1); + parser->set_top_level(parser, level0); + + while (parser->iterate(parser, &objectID, &object)) + { + u_int level = parser->get_level(parser)+1; + + switch (objectID) + { + case RSASSA_PSS_PARAMS_HASH_ALG: + if (object.len) + { + alg = asn1_parse_algorithmIdentifier(object, level, NULL); + params->hash = hasher_algorithm_from_oid(alg); + if (params->hash == HASH_UNKNOWN) + { + goto end; + } + } + break; + case RSASSA_PSS_PARAMS_MGF_ALG: + if (object.len) + { + chunk_t hash; + + alg = asn1_parse_algorithmIdentifier(object, level, &hash); + if (alg != OID_MGF1) + { + goto end; + } + alg = asn1_parse_algorithmIdentifier(hash, level+1, NULL); + params->mgf1_hash = hasher_algorithm_from_oid(alg); + if (params->mgf1_hash == HASH_UNKNOWN) + { + goto end; + } + } + break; + case RSASSA_PSS_PARAMS_SALT_LEN: + if (object.len) + { + params->salt_len = (size_t)asn1_parse_integer_uint64(object); + } + break; + case RSASSA_PSS_PARAMS_TRAILER: + if (object.len && (object.len != 1 || *object.ptr != 1)) + { + goto end; + } + break; + default: + break; + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + return success; +} + +/* + * Described in header + */ +bool rsa_pss_params_build(rsa_pss_params_t *params, chunk_t *asn1) +{ + chunk_t hash = chunk_empty, mgf = chunk_empty, slen = chunk_empty; + ssize_t salt_len; + int alg; + + if (params->hash != HASH_SHA1) + { /* with SHA-1 we MUST omit the field */ + alg = hasher_algorithm_to_oid(params->hash); + if (alg == OID_UNKNOWN) + { + return FALSE; + } + hash = asn1_algorithmIdentifier(alg); + } + if (params->mgf1_hash != HASH_SHA1) + { /* with MGF1-SHA1 we MUST omit the field */ + alg = hasher_algorithm_to_oid(params->mgf1_hash); + if (alg == OID_UNKNOWN) + { + chunk_free(&hash); + return FALSE; + } + mgf = asn1_algorithmIdentifier_params(OID_MGF1, + asn1_algorithmIdentifier(alg)); + } + salt_len = rsa_pss_salt_length(params); + if (salt_len < 0) + { + chunk_free(&hash); + chunk_free(&mgf); + return FALSE; + } + else if (salt_len != HASH_SIZE_SHA1) + { + slen = asn1_integer("m", asn1_integer_from_uint64(salt_len)); + } + *asn1 = asn1_wrap(ASN1_SEQUENCE, "mmm", + hash.len ? asn1_wrap(ASN1_CONTEXT_C_0, "m", hash) : chunk_empty, + mgf.len ? asn1_wrap(ASN1_CONTEXT_C_1, "m", mgf) : chunk_empty, + slen.len ? asn1_wrap(ASN1_CONTEXT_C_2, "m", slen) : chunk_empty); + return TRUE; +} |