summaryrefslogtreecommitdiff
path: root/src/libstrongswan/credentials/keys/signature_params.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/credentials/keys/signature_params.c')
-rw-r--r--src/libstrongswan/credentials/keys/signature_params.c366
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, &parameters);
+ 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, &parameters))
+ {
+ 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;
+}