diff options
Diffstat (limited to 'src/libstrongswan/plugins/openssl')
16 files changed, 828 insertions, 266 deletions
diff --git a/src/libstrongswan/plugins/openssl/Makefile.in b/src/libstrongswan/plugins/openssl/Makefile.in index dcf4c2c8a..856055c6a 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.in +++ b/src/libstrongswan/plugins/openssl/Makefile.in @@ -254,9 +254,11 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +FUZZING_LDFLAGS = @FUZZING_LDFLAGS@ GEM = @GEM@ GENHTML = @GENHTML@ GPERF = @GPERF@ +GPERF_LEN_TYPE = @GPERF_LEN_TYPE@ GPRBUILD = @GPRBUILD@ GREP = @GREP@ INSTALL = @INSTALL@ diff --git a/src/libstrongswan/plugins/openssl/openssl_crl.c b/src/libstrongswan/plugins/openssl/openssl_crl.c index 61cf3e884..88f7a67c2 100644 --- a/src/libstrongswan/plugins/openssl/openssl_crl.c +++ b/src/libstrongswan/plugins/openssl/openssl_crl.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -47,14 +50,13 @@ #include <credentials/certificates/x509.h> #if OPENSSL_VERSION_NUMBER < 0x10100000L -static inline void X509_CRL_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg, const X509_CRL *crl) { +static inline void X509_CRL_get0_signature(const X509_CRL *crl, ASN1_BIT_STRING **psig, X509_ALGOR **palg) { if (psig) { *psig = crl->signature; } if (palg) { *palg = crl->sig_alg; } } #define X509_REVOKED_get0_serialNumber(r) ({ (r)->serialNumber; }) #define X509_REVOKED_get0_revocationDate(r) ({ (r)->revocationDate; }) #define X509_CRL_get0_extensions(c) ({ (c)->crl->extensions; }) -#define X509_ALGOR_get0(oid, ppt, ppv, alg) ({ *(oid) = (alg)->algorithm; }) #endif typedef struct private_openssl_crl_t private_openssl_crl_t; @@ -85,6 +87,16 @@ struct private_openssl_crl_t { chunk_t serial; /** + * Number of base CRL (deltaCrlIndicator), if a delta CRL + */ + chunk_t base; + + /** + * List of Freshest CRL distribution points + */ + linked_list_t *crl_uris; + + /** * AuthorityKeyIdentifier of the issuing CA */ chunk_t authKeyIdentifier; @@ -107,7 +119,7 @@ struct private_openssl_crl_t { /** * Signature scheme used in this CRL */ - signature_scheme_t scheme; + signature_params_t *scheme; /** * References to this CRL @@ -140,6 +152,11 @@ typedef struct { int i; } crl_enumerator_t; +/** + * from openssl_x509 + */ +bool openssl_parse_crlDistributionPoints(X509_EXTENSION *ext, + linked_list_t *list); METHOD(enumerator_t, crl_enumerate, bool, crl_enumerator_t *this, va_list args) @@ -215,6 +232,26 @@ METHOD(crl_t, get_serial, chunk_t, return this->serial; } +METHOD(crl_t, is_delta_crl, bool, + private_openssl_crl_t *this, chunk_t *base_crl) +{ + if (this->base.len) + { + if (base_crl) + { + *base_crl = this->base; + } + return TRUE; + } + return FALSE; +} + +METHOD(crl_t, create_delta_crl_uri_enumerator, enumerator_t*, + private_openssl_crl_t *this) +{ + return this->crl_uris->create_enumerator(this->crl_uris); +} + METHOD(crl_t, get_authKeyIdentifier, chunk_t, private_openssl_crl_t *this) { @@ -246,7 +283,7 @@ METHOD(certificate_t, has_subject_or_issuer, id_match_t, METHOD(certificate_t, issued_by, bool, private_openssl_crl_t *this, certificate_t *issuer, - signature_scheme_t *scheme) + signature_params_t **scheme) { chunk_t fingerprint, tbs; public_key_t *key; @@ -283,23 +320,20 @@ METHOD(certificate_t, issued_by, bool, return FALSE; } } - if (this->scheme == SIGN_UNKNOWN) - { - return FALSE; - } /* i2d_re_X509_CRL_tbs() was added with 1.1.0 when X509_CRL became opaque */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L tbs = openssl_i2chunk(re_X509_CRL_tbs, this->crl); #else tbs = openssl_i2chunk(X509_CRL_INFO, this->crl->crl); #endif - X509_CRL_get0_signature(&sig, NULL, this->crl); - valid = key->verify(key, this->scheme, tbs, openssl_asn1_str2chunk(sig)); + X509_CRL_get0_signature(this->crl, &sig, NULL); + valid = key->verify(key, this->scheme->scheme, this->scheme->params, tbs, + openssl_asn1_str2chunk(sig)); free(tbs.ptr); key->destroy(key); if (valid && scheme) { - *scheme = this->scheme; + *scheme = signature_params_clone(this->scheme); } return valid; } @@ -379,8 +413,12 @@ METHOD(certificate_t, destroy, void, { X509_CRL_free(this->crl); } + signature_params_destroy(this->scheme); + this->crl_uris->destroy_function(this->crl_uris, + (void*)x509_cdp_destroy); DESTROY_IF(this->issuer); free(this->authKeyIdentifier.ptr); + free(this->base.ptr); free(this->serial.ptr); free(this->encoding.ptr); free(this); @@ -413,11 +451,12 @@ static private_openssl_crl_t *create_empty() }, .get_serial = _get_serial, .get_authKeyIdentifier = _get_authKeyIdentifier, - .is_delta_crl = (void*)return_false, - .create_delta_crl_uri_enumerator = (void*)enumerator_create_empty, + .is_delta_crl = _is_delta_crl, + .create_delta_crl_uri_enumerator = _create_delta_crl_uri_enumerator, .create_enumerator = _create_enumerator, }, }, + .crl_uris = linked_list_create(), .ref = 1, ); return this; @@ -444,21 +483,19 @@ static bool parse_authKeyIdentifier_ext(private_openssl_crl_t *this, } /** - * Parse the crlNumber extension + * Quick and dirty INTEGER unwrap for crlNumber/deltaCrlIndicator extensions */ -static bool parse_crlNumber_ext(private_openssl_crl_t *this, - X509_EXTENSION *ext) +static bool parse_integer_ext(X509_EXTENSION *ext, chunk_t *out) { chunk_t chunk; chunk = openssl_asn1_str2chunk(X509_EXTENSION_get_data(ext)); - /* quick and dirty INTEGER unwrap */ if (chunk.len > 1 && chunk.ptr[0] == V_ASN1_INTEGER && chunk.ptr[1] == chunk.len - 2) { chunk = chunk_skip(chunk, 2); - free(this->serial.ptr); - this->serial = chunk_clone(chunk); + free(out->ptr); + *out = chunk_clone(chunk); return TRUE; } return FALSE; @@ -488,7 +525,13 @@ static bool parse_extensions(private_openssl_crl_t *this) ok = parse_authKeyIdentifier_ext(this, ext); break; case NID_crl_number: - ok = parse_crlNumber_ext(this, ext); + ok = parse_integer_ext(ext, &this->serial); + break; + case NID_delta_crl: + ok = parse_integer_ext(ext, &this->base); + break; + case NID_freshest_crl: + ok = openssl_parse_crlDistributionPoints(ext, this->crl_uris); break; case NID_issuing_distribution_point: /* TODO support of IssuingDistributionPoints */ @@ -520,7 +563,7 @@ static bool parse_extensions(private_openssl_crl_t *this) static bool parse_crl(private_openssl_crl_t *this) { const unsigned char *ptr = this->encoding.ptr; - ASN1_OBJECT *oid; + chunk_t sig_scheme; X509_ALGOR *alg; this->crl = d2i_X509_CRL(NULL, &ptr, this->encoding.len); @@ -529,28 +572,16 @@ static bool parse_crl(private_openssl_crl_t *this) return FALSE; } - X509_CRL_get0_signature(NULL, &alg, this->crl); - X509_ALGOR_get0(&oid, NULL, NULL, alg); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - if (!chunk_equals( - openssl_asn1_obj2chunk(this->crl->crl->sig_alg->algorithm), - openssl_asn1_obj2chunk(this->crl->sig_alg->algorithm))) + X509_CRL_get0_signature(this->crl, NULL, &alg); + sig_scheme = openssl_i2chunk(X509_ALGOR, alg); + INIT(this->scheme); + if (!signature_params_parse(sig_scheme, 0, this->scheme)) { + DBG1(DBG_ASN, "unable to parse signature algorithm"); + free(sig_scheme.ptr); return FALSE; } -#elif 0 - /* FIXME: we currently can't do this if X509_CRL is opaque (>= 1.1.0) as - * X509_CRL_get0_tbs_sigalg() does not exist and there does not seem to be - * another easy way to get the algorithm from the tbsCertList of the CRL */ - alg = X509_CRL_get0_tbs_sigalg(this->crl); - X509_ALGOR_get0(&oid_tbs, NULL, NULL, alg); - if (!chunk_equals(openssl_asn1_obj2chunk(oid), - openssl_asn1_obj2chunk(oid_tbs))) - { - return FALSE; - } -#endif - this->scheme = signature_scheme_from_oid(openssl_asn1_known_oid(oid)); + free(sig_scheme.ptr); this->issuer = openssl_x509_name2id(X509_CRL_get_issuer(this->crl)); if (!this->issuer) diff --git a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c index f08dfff7e..8e9c1183f 100644 --- a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c +++ b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c @@ -193,7 +193,7 @@ METHOD(diffie_hellman_t, destroy, void, * Described in header. */ openssl_diffie_hellman_t *openssl_diffie_hellman_create( - diffie_hellman_group_t group, chunk_t g, chunk_t p) + diffie_hellman_group_t group, ...) { private_openssl_diffie_hellman_t *this; const BIGNUM *privkey; @@ -225,6 +225,9 @@ openssl_diffie_hellman_t *openssl_diffie_hellman_create( if (group == MODP_CUSTOM) { + chunk_t g, p; + + VA_ARGS_GET(group, g, p); if (!DH_set0_pqg(this->dh, BN_bin2bn(p.ptr, p.len, NULL), NULL, BN_bin2bn(g.ptr, g.len, NULL))) { diff --git a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.h b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.h index 53dc59c78..5de5520b5 100644 --- a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.h +++ b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.h @@ -40,12 +40,11 @@ struct openssl_diffie_hellman_t { * Creates a new openssl_diffie_hellman_t object. * * @param group Diffie Hellman group number to use - * @param g custom generator, if MODP_CUSTOM - * @param p custom prime, if MODP_CUSTOM + * @param ... expects generator and prime as chunk_t if MODP_CUSTOM * @return openssl_diffie_hellman_t object, NULL if not supported */ openssl_diffie_hellman_t *openssl_diffie_hellman_create( - diffie_hellman_group_t group, chunk_t g, chunk_t p); + diffie_hellman_group_t group, ...); #endif /** OPENSSL_DIFFIE_HELLMAN_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c index 22bbf6dc7..364190758 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c @@ -49,6 +49,11 @@ struct private_openssl_ec_private_key_t { EC_KEY *ec; /** + * TRUE if the key is from an OpenSSL ENGINE and might not be readable + */ + bool engine; + + /** * reference count */ refcount_t ref; @@ -146,7 +151,7 @@ static bool build_der_signature(private_openssl_ec_private_key_t *this, METHOD(private_key_t, sign, bool, private_openssl_ec_private_key_t *this, signature_scheme_t scheme, - chunk_t data, chunk_t *signature) + void *params, chunk_t data, chunk_t *signature) { switch (scheme) { @@ -226,6 +231,11 @@ METHOD(private_key_t, get_encoding, bool, { u_char *p; + if (this->engine) + { + return FALSE; + } + switch (type) { case PRIVKEY_ASN1_DER: @@ -307,7 +317,7 @@ static private_openssl_ec_private_key_t *create_empty(void) /* * See header. */ -private_key_t *openssl_ec_private_key_create(EVP_PKEY *key) +private_key_t *openssl_ec_private_key_create(EVP_PKEY *key, bool engine) { private_openssl_ec_private_key_t *this; EC_KEY *ec; @@ -320,6 +330,7 @@ private_key_t *openssl_ec_private_key_create(EVP_PKEY *key) } this = create_empty(); this->ec = ec; + this->engine = engine; return &this->public.key; } diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.h b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.h index 84314f671..56c59cfc8 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.h +++ b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.h @@ -67,8 +67,9 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type, * Wrap an EVP_PKEY object of type EVP_PKEY_EC * * @param key EVP_PKEY_EC key object (adopted) + * @param engine whether the key was loaded via an engine * @return loaded key, NULL on failure */ -private_key_t *openssl_ec_private_key_create(EVP_PKEY *key); +private_key_t *openssl_ec_private_key_create(EVP_PKEY *key, bool engine); #endif /** OPENSSL_EC_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c index a1e56fc5e..faa940839 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c @@ -151,7 +151,7 @@ METHOD(public_key_t, get_type, key_type_t, METHOD(public_key_t, verify, bool, private_openssl_ec_public_key_t *this, signature_scheme_t scheme, - chunk_t data, chunk_t signature) + void *params, chunk_t data, chunk_t signature) { switch (scheme) { diff --git a/src/libstrongswan/plugins/openssl/openssl_hasher.c b/src/libstrongswan/plugins/openssl/openssl_hasher.c index 96ee230c9..eb6c50508 100644 --- a/src/libstrongswan/plugins/openssl/openssl_hasher.c +++ b/src/libstrongswan/plugins/openssl/openssl_hasher.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2008 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2008-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 @@ -91,16 +91,24 @@ METHOD(hasher_t, destroy, void, /* * Described in header */ -openssl_hasher_t *openssl_hasher_create(hash_algorithm_t algo) +const EVP_MD *openssl_get_md(hash_algorithm_t hash) { - private_openssl_hasher_t *this; - char* name; + char *name; - name = enum_to_name(hash_algorithm_short_names, algo); + name = enum_to_name(hash_algorithm_short_names, hash); if (!name) { return NULL; } + return EVP_get_digestbyname(name); +} + +/* + * Described in header + */ +openssl_hasher_t *openssl_hasher_create(hash_algorithm_t algo) +{ + private_openssl_hasher_t *this; INIT(this, .public = { @@ -114,7 +122,7 @@ openssl_hasher_t *openssl_hasher_create(hash_algorithm_t algo) }, ); - this->hasher = EVP_get_digestbyname(name); + this->hasher = openssl_get_md(algo); if (!this->hasher) { /* OpenSSL does not support the requested algo */ diff --git a/src/libstrongswan/plugins/openssl/openssl_hasher.h b/src/libstrongswan/plugins/openssl/openssl_hasher.h index b03f6891b..66b9b505e 100644 --- a/src/libstrongswan/plugins/openssl/openssl_hasher.h +++ b/src/libstrongswan/plugins/openssl/openssl_hasher.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2008 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2008-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 @@ -25,6 +25,8 @@ typedef struct openssl_hasher_t openssl_hasher_t; #include <crypto/hashers/hasher.h> +#include <openssl/evp.h> + /** * Implementation of hashers using OpenSSL. */ @@ -37,6 +39,14 @@ struct openssl_hasher_t { }; /** + * Determine EVP_MD for the given hash algorithm + * + * @param hash hash algorithm + * @return EVP_MD or NULL if not found/supported + */ +const EVP_MD *openssl_get_md(hash_algorithm_t hash); + +/** * Constructor to create openssl_hasher_t. * * @param algo algorithm diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c index 83ac8df5b..f94767cf5 100644 --- a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c @@ -256,7 +256,7 @@ static auth_cfg_t *verify_signature(CMS_SignerInfo *si, int hash_oid) key = cert->get_public_key(cert); if (key) { - if (key->verify(key, signature_scheme_from_oid(hash_oid), + if (key->verify(key, signature_scheme_from_oid(hash_oid), NULL, attrs, sig)) { found = auth->clone(auth); diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index ab73d718f..8b0a7c5c7 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -301,11 +301,11 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args) { #ifndef OPENSSL_NO_RSA case EVP_PKEY_RSA: - return openssl_rsa_private_key_create(key); + return openssl_rsa_private_key_create(key, FALSE); #endif #ifndef OPENSSL_NO_ECDSA case EVP_PKEY_EC: - return openssl_ec_private_key_create(key); + return openssl_ec_private_key_create(key, FALSE); #endif default: EVP_PKEY_free(key); @@ -316,6 +316,152 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args) return NULL; } +#ifndef OPENSSL_NO_ENGINE +/** + * Login to engine with a PIN specified for a keyid + */ +static bool login(ENGINE *engine, chunk_t keyid) +{ + enumerator_t *enumerator; + shared_key_t *shared; + identification_t *id; + chunk_t key; + char pin[64]; + bool found = FALSE, success = FALSE; + + id = identification_create_from_encoding(ID_KEY_ID, keyid); + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PIN, id, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + found = TRUE; + key = shared->get_key(shared); + if (snprintf(pin, sizeof(pin), + "%.*s", (int)key.len, key.ptr) >= sizeof(pin)) + { + continue; + } + if (ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0)) + { + success = TRUE; + break; + } + else + { + DBG1(DBG_CFG, "setting PIN on engine failed"); + } + } + enumerator->destroy(enumerator); + id->destroy(id); + if (!found) + { + DBG1(DBG_CFG, "no PIN found for %#B", &keyid); + } + return success; +} +#endif /* OPENSSL_NO_ENGINE */ + +/** + * Load private key via engine + */ +static private_key_t *openssl_private_key_connect(key_type_t type, + va_list args) +{ +#ifndef OPENSSL_NO_ENGINE + char *engine_id = NULL; + char keyname[BUF_LEN]; + chunk_t keyid = chunk_empty;; + EVP_PKEY *key; + ENGINE *engine; + int slot = -1; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_PKCS11_KEYID: + keyid = va_arg(args, chunk_t); + continue; + case BUILD_PKCS11_SLOT: + slot = va_arg(args, int); + continue; + case BUILD_PKCS11_MODULE: + engine_id = va_arg(args, char*); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + if (!keyid.len || keyid.len > 40) + { + return NULL; + } + + memset(keyname, 0, sizeof(keyname)); + if (slot != -1) + { + snprintf(keyname, sizeof(keyname), "%d:", slot); + } + if (sizeof(keyname) - strlen(keyname) <= keyid.len * 4 / 3 + 1) + { + return NULL; + } + chunk_to_hex(keyid, keyname + strlen(keyname), FALSE); + + if (!engine_id) + { + engine_id = lib->settings->get_str(lib->settings, + "%s.plugins.openssl.engine_id", "pkcs11", lib->ns); + } + engine = ENGINE_by_id(engine_id); + if (!engine) + { + DBG2(DBG_LIB, "engine '%s' is not available", engine_id); + return NULL; + } + if (!ENGINE_init(engine)) + { + DBG1(DBG_LIB, "failed to initialize engine '%s'", engine_id); + ENGINE_free(engine); + return NULL; + } + if (!login(engine, keyid)) + { + DBG1(DBG_LIB, "login to engine '%s' failed", engine_id); + ENGINE_free(engine); + return NULL; + } + key = ENGINE_load_private_key(engine, keyname, NULL, NULL); + if (!key) + { + DBG1(DBG_LIB, "failed to load private key with ID '%s' from " + "engine '%s'", keyname, engine_id); + ENGINE_free(engine); + return NULL; + } + ENGINE_free(engine); + + switch (EVP_PKEY_base_id(key)) + { +#ifndef OPENSSL_NO_RSA + case EVP_PKEY_RSA: + return openssl_rsa_private_key_create(key, TRUE); +#endif +#ifndef OPENSSL_NO_ECDSA + case EVP_PKEY_EC: + return openssl_ec_private_key_create(key, TRUE); +#endif + default: + EVP_PKEY_free(key); + break; + } +#endif /* OPENSSL_NO_ENGINE */ + return NULL; +} + METHOD(plugin_t, get_name, char*, private_openssl_plugin_t *this) { @@ -469,8 +615,6 @@ METHOD(plugin_t, get_features, int, /* RSA private/public key loading */ PLUGIN_REGISTER(PRIVKEY, openssl_rsa_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), - PLUGIN_REGISTER(PRIVKEY, openssl_rsa_private_key_connect, FALSE), - PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_REGISTER(PRIVKEY_GEN, openssl_rsa_private_key_gen, FALSE), PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_RSA), PLUGIN_REGISTER(PUBKEY, openssl_rsa_public_key_load, TRUE), @@ -480,6 +624,10 @@ METHOD(plugin_t, get_features, int, /* signature/encryption schemes */ PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_NULL), PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL), +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PSS), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PSS), +#endif #ifndef OPENSSL_NO_SHA1 PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA1), PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1), @@ -554,6 +702,8 @@ METHOD(plugin_t, get_features, int, /* generic key loader */ PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), + PLUGIN_REGISTER(PRIVKEY, openssl_private_key_connect, FALSE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_REGISTER(RNG, openssl_rng_create), PLUGIN_PROVIDE(RNG, RNG_STRONG), PLUGIN_PROVIDE(RNG, RNG_WEAK), diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c index 54ecf2542..401a51a0b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Tobias Brunner + * Copyright (C) 2008-2017 Tobias Brunner * Copyright (C) 2009 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -20,16 +20,15 @@ #include "openssl_rsa_private_key.h" #include "openssl_rsa_public_key.h" +#include "openssl_hasher.h" #include "openssl_util.h" #include <utils/debug.h> +#include <credentials/keys/signature_params.h> #include <openssl/bn.h> #include <openssl/evp.h> #include <openssl/rsa.h> -#ifndef OPENSSL_NO_ENGINE -#include <openssl/engine.h> -#endif /* OPENSSL_NO_ENGINE */ /** * Public exponent to use for key generation. @@ -40,6 +39,7 @@ OPENSSL_KEY_FALLBACK(RSA, key, n, e, d) OPENSSL_KEY_FALLBACK(RSA, factors, p, q) OPENSSL_KEY_FALLBACK(RSA, crt_params, dmp1, dmq1, iqmp) +#define BN_secure_new() BN_new() #endif typedef struct private_openssl_rsa_private_key_t private_openssl_rsa_private_key_t; @@ -72,8 +72,126 @@ struct private_openssl_rsa_private_key_t { /* implemented in rsa public key */ bool openssl_rsa_fingerprint(RSA *rsa, cred_encoding_type_t type, chunk_t *fp); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + /** - * Build an EMPSA PKCS1 signature described in PKCS#1 + * Build RSA signature + */ +static bool build_signature(private_openssl_rsa_private_key_t *this, + const EVP_MD *md, rsa_pss_params_t *pss, + chunk_t data, chunk_t *sig) +{ + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *mctx = NULL; + EVP_PKEY *key; + bool success = FALSE; + + mctx = EVP_MD_CTX_create(); + key = EVP_PKEY_new(); + if (!mctx || !key) + { + goto error; + } + if (!EVP_PKEY_set1_RSA(key, this->rsa)) + { + goto error; + } + if (EVP_DigestSignInit(mctx, &pctx, md, NULL, key) <= 0) + { + goto error; + } + if (pss) + { + const EVP_MD *mgf1md = openssl_get_md(pss->mgf1_hash); + int slen = EVP_MD_size(md); + if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) + { + slen = pss->salt_len; + } + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, slen) <= 0 || + EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md) <= 0) + { + goto error; + } + } + if (EVP_DigestSignUpdate(mctx, data.ptr, data.len) <= 0) + { + goto error; + } + success = (EVP_DigestSignFinal(mctx, sig->ptr, &sig->len) == 1); + +error: + if (key) + { + EVP_PKEY_free(key); + } + if (mctx) + { + EVP_MD_CTX_destroy(mctx); + } + return success; +} + +/** + * Build an EMSA PKCS1 signature described in PKCS#1 + */ +static bool build_emsa_pkcs1_signature(private_openssl_rsa_private_key_t *this, + int type, chunk_t data, chunk_t *sig) +{ + const EVP_MD *md; + + *sig = chunk_alloc(RSA_size(this->rsa)); + + if (type == NID_undef) + { + if (RSA_private_encrypt(data.len, data.ptr, sig->ptr, this->rsa, + RSA_PKCS1_PADDING) == sig->len) + { + return TRUE; + } + } + else + { + md = EVP_get_digestbynid(type); + if (md && build_signature(this, md, NULL, data, sig)) + { + return TRUE; + } + } + chunk_free(sig); + return FALSE; +} + +/** + * Build an EMSA PSS signature described in PKCS#1 + */ +static bool build_emsa_pss_signature(private_openssl_rsa_private_key_t *this, + rsa_pss_params_t *params, chunk_t data, + chunk_t *sig) +{ + const EVP_MD *md; + + if (!params) + { + return FALSE; + } + + *sig = chunk_alloc(RSA_size(this->rsa)); + + md = openssl_get_md(params->hash); + if (md && build_signature(this, md, params, data, sig)) + { + return TRUE; + } + chunk_free(sig); + return FALSE; +} + +#else /* OPENSSL_VERSION_NUMBER < 1.0 */ + +/** + * Build an EMSA PKCS1 signature described in PKCS#1 */ static bool build_emsa_pkcs1_signature(private_openssl_rsa_private_key_t *this, int type, chunk_t data, chunk_t *sig) @@ -92,15 +210,15 @@ static bool build_emsa_pkcs1_signature(private_openssl_rsa_private_key_t *this, } else { - EVP_MD_CTX *ctx; - EVP_PKEY *key; + EVP_MD_CTX *ctx = NULL; + EVP_PKEY *key = NULL; const EVP_MD *hasher; u_int len; hasher = EVP_get_digestbynid(type); if (!hasher) { - return FALSE; + goto error; } ctx = EVP_MD_CTX_create(); @@ -142,7 +260,7 @@ error: } return success; } - +#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ METHOD(private_key_t, get_type, key_type_t, private_openssl_rsa_private_key_t *this) @@ -152,7 +270,7 @@ METHOD(private_key_t, get_type, key_type_t, METHOD(private_key_t, sign, bool, private_openssl_rsa_private_key_t *this, signature_scheme_t scheme, - chunk_t data, chunk_t *signature) + void *params, chunk_t data, chunk_t *signature) { switch (scheme) { @@ -170,6 +288,10 @@ METHOD(private_key_t, sign, bool, return build_emsa_pkcs1_signature(this, NID_sha1, data, signature); case SIGN_RSA_EMSA_PKCS1_MD5: return build_emsa_pkcs1_signature(this, NID_md5, data, signature); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + case SIGN_RSA_EMSA_PSS: + return build_emsa_pss_signature(this, params, data, signature); +#endif default: DBG1(DBG_LIB, "signature scheme %N not supported in RSA", signature_scheme_names, scheme); @@ -386,7 +508,7 @@ error: /* * See header */ -private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key) +private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key, bool engine) { private_openssl_rsa_private_key_t *this; RSA *rsa; @@ -399,9 +521,199 @@ private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key) } this = create_empty(); this->rsa = rsa; + this->engine = engine; return &this->public.key; } +/** + * Recover the primes from n, e and d using the algorithm described in + * Appendix C of NIST SP 800-56B. + */ +static bool calculate_pq(BIGNUM *n, BIGNUM *e, BIGNUM *d, + BIGNUM **p, BIGNUM **q) +{ + BN_CTX *ctx; + BIGNUM *k, *r, *g, *y, *n1, *x; + int i, t, j; + bool success = FALSE; + + ctx = BN_CTX_new(); + if (!ctx) + { + return FALSE; + } + BN_CTX_start(ctx); + k = BN_CTX_get(ctx); + r = BN_CTX_get(ctx); + g = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + n1 = BN_CTX_get(ctx); + x = BN_CTX_get(ctx); + if (!x) + { + goto error; + } + /* k = (d * e) - 1 */ + if (!BN_mul(k, d, e, ctx) || !BN_sub(k, k, BN_value_one())) + { + goto error; + } + /* k must be even */ + if (BN_is_odd(k)) + { + goto error; + } + /* k = 2^t * r, where r is the largest odd integer dividing k, and t >= 1 */ + if (!BN_copy(r, k)) + { + goto error; + } + for (t = 0; !BN_is_odd(r); t++) + { /* r = r/2 */ + if (!BN_rshift(r, r, 1)) + { + goto error; + } + } + /* we need n-1 below */ + if (!BN_sub(n1, n, BN_value_one())) + { + goto error; + } + for (i = 0; i < 100; i++) + { /* generate random integer g in [0, n-1] */ + if (!BN_pseudo_rand_range(g, n)) + { + goto error; + } + /* y = g^r mod n */ + if (!BN_mod_exp(y, g, r, n, ctx)) + { + goto error; + } + /* try again if y == 1 or y == n-1 */ + if (BN_is_one(y) || BN_cmp(y, n1) == 0) + { + continue; + } + for (j = 0; j < t; j++) + { /* x = y^2 mod n */ + if (!BN_mod_sqr(x, y, n, ctx)) + { + goto error; + } + /* stop if x == 1 */ + if (BN_is_one(x)) + { + goto done; + } + /* retry with new g if x = n-1 */ + if (BN_cmp(x, n1) == 0) + { + break; + } + /* y = x */ + if (!BN_copy(y, x)) + { + goto error; + } + } + } + goto error; + +done: + /* p = gcd(y-1, n) */ + if (!BN_sub(y, y, BN_value_one())) + { + goto error; + } + *p = BN_secure_new(); + if (!BN_gcd(*p, y, n, ctx)) + { + BN_clear_free(*p); + goto error; + } + /* q = n/p */ + *q = BN_secure_new(); + if (!BN_div(*q, NULL, n, *p, ctx)) + { + BN_clear_free(*p); + BN_clear_free(*q); + goto error; + } + success = TRUE; + +error: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return success; +} + +/** + * Calculates dp = d (mod p-1) or dq = d (mod q-1) for the Chinese remainder + * algorithm. + */ +static BIGNUM *dmodpq1(BIGNUM *d, BIGNUM *pq) +{ + BN_CTX *ctx; + BIGNUM *res = NULL, *pq1; + + ctx = BN_CTX_new(); + if (!ctx) + { + return NULL; + } + BN_CTX_start(ctx); + pq1 = BN_CTX_get(ctx); + /* p|q - 1 */ + if (!BN_sub(pq1, pq, BN_value_one())) + { + goto error; + } + /* d (mod p|q -1) */ + res = BN_secure_new(); + if (!BN_mod(res, d, pq1, ctx)) + { + BN_clear_free(res); + res = NULL; + goto error; + } + +error: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return res; +} + +/** + * Calculates qinv = q^-1 (mod p) for the Chinese remainder algorithm. + */ +static BIGNUM *qinv(BIGNUM *q, BIGNUM *p) +{ + BN_CTX *ctx; + BIGNUM *res = NULL; + + ctx = BN_CTX_new(); + if (!ctx) + { + return NULL; + } + BN_CTX_start(ctx); + /* q^-1 (mod p) */ + res = BN_secure_new(); + if (!BN_mod_inverse(res, q, p, ctx)) + { + BN_clear_free(res); + res = NULL; + goto error; + } + +error: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return res; +} + /* * See header */ @@ -460,7 +772,7 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type, return &this->public; } } - else if (n.ptr && e.ptr && d.ptr && p.ptr && q.ptr && coeff.ptr) + else if (n.ptr && e.ptr && d.ptr) { BIGNUM *bn_n, *bn_e, *bn_d, *bn_p, *bn_q; BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; @@ -472,178 +784,58 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type, bn_d = BN_bin2bn((const u_char*)d.ptr, d.len, NULL); if (!RSA_set0_key(this->rsa, bn_n, bn_e, bn_d)) { - destroy(this); - return NULL; + goto error; } - bn_p = BN_bin2bn((const u_char*)p.ptr, p.len, NULL); - bn_q = BN_bin2bn((const u_char*)q.ptr, q.len, NULL); + if (p.ptr && q.ptr) + { + bn_p = BN_bin2bn((const u_char*)p.ptr, p.len, NULL); + bn_q = BN_bin2bn((const u_char*)q.ptr, q.len, NULL); + } + else + { + if (!calculate_pq(bn_n, bn_e, bn_d, &bn_p, &bn_q)) + { + goto error; + } + } if (!RSA_set0_factors(this->rsa, bn_p, bn_q)) { - destroy(this); - return NULL; + goto error; } if (exp1.ptr) { dmp1 = BN_bin2bn((const u_char*)exp1.ptr, exp1.len, NULL); } - if (exp2.ptr) + else { - dmq1 = BN_bin2bn((const u_char*)exp2.ptr, exp2.len, NULL); + dmp1 = dmodpq1(bn_d, bn_p); } - iqmp = BN_bin2bn((const u_char*)coeff.ptr, coeff.len, NULL); - if (RSA_set0_crt_params(this->rsa, dmp1, dmq1, iqmp) && - RSA_check_key(this->rsa) == 1) + if (exp2.ptr) { - return &this->public; + dmq1 = BN_bin2bn((const u_char*)exp2.ptr, exp2.len, NULL); } - } - destroy(this); - return NULL; -} - -#ifndef OPENSSL_NO_ENGINE -/** - * Login to engine with a PIN specified for a keyid - */ -static bool login(ENGINE *engine, chunk_t keyid) -{ - enumerator_t *enumerator; - shared_key_t *shared; - identification_t *id; - chunk_t key; - char pin[64]; - bool found = FALSE, success = FALSE; - - id = identification_create_from_encoding(ID_KEY_ID, keyid); - enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, - SHARED_PIN, id, NULL); - while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) - { - found = TRUE; - key = shared->get_key(shared); - if (snprintf(pin, sizeof(pin), - "%.*s", (int)key.len, key.ptr) >= sizeof(pin)) + else { - continue; + dmq1 = dmodpq1(bn_d, bn_q); } - if (ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0)) + if (coeff.ptr) { - success = TRUE; - break; + iqmp = BN_bin2bn((const u_char*)coeff.ptr, coeff.len, NULL); } else { - DBG1(DBG_CFG, "setting PIN on engine failed"); + iqmp = qinv(bn_q, bn_p); } - } - enumerator->destroy(enumerator); - id->destroy(id); - if (!found) - { - DBG1(DBG_CFG, "no PIN found for %#B", &keyid); - } - return success; -} -#endif /* OPENSSL_NO_ENGINE */ - -/* - * See header. - */ -openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type, - va_list args) -{ -#ifndef OPENSSL_NO_ENGINE - private_openssl_rsa_private_key_t *this; - char *engine_id = NULL; - char keyname[64]; - chunk_t keyid = chunk_empty;; - EVP_PKEY *key; - ENGINE *engine; - int slot = -1; - - while (TRUE) - { - switch (va_arg(args, builder_part_t)) + if (RSA_set0_crt_params(this->rsa, dmp1, dmq1, iqmp) && + RSA_check_key(this->rsa) == 1) { - case BUILD_PKCS11_KEYID: - keyid = va_arg(args, chunk_t); - continue; - case BUILD_PKCS11_SLOT: - slot = va_arg(args, int); - continue; - case BUILD_PKCS11_MODULE: - engine_id = va_arg(args, char*); - continue; - case BUILD_END: - break; - default: - return NULL; + return &this->public; } - break; - } - if (!keyid.len || keyid.len > 40) - { - return NULL; - } - - memset(keyname, 0, sizeof(keyname)); - if (slot != -1) - { - snprintf(keyname, sizeof(keyname), "%d:", slot); - } - if (sizeof(keyname) - strlen(keyname) <= keyid.len * 4 / 3 + 1) - { - return NULL; - } - chunk_to_hex(keyid, keyname + strlen(keyname), FALSE); - - if (!engine_id) - { - engine_id = lib->settings->get_str(lib->settings, - "%s.plugins.openssl.engine_id", "pkcs11", lib->ns); - } - engine = ENGINE_by_id(engine_id); - if (!engine) - { - DBG2(DBG_LIB, "engine '%s' is not available", engine_id); - return NULL; - } - if (!ENGINE_init(engine)) - { - DBG1(DBG_LIB, "failed to initialize engine '%s'", engine_id); - ENGINE_free(engine); - return NULL; - } - if (!login(engine, keyid)) - { - DBG1(DBG_LIB, "login to engine '%s' failed", engine_id); - ENGINE_free(engine); - return NULL; - } - key = ENGINE_load_private_key(engine, keyname, NULL, NULL); - if (!key) - { - DBG1(DBG_LIB, "failed to load private key with ID '%s' from " - "engine '%s'", keyname, engine_id); - ENGINE_free(engine); - return NULL; - } - ENGINE_free(engine); - - this = create_empty(); - this->rsa = EVP_PKEY_get1_RSA(key); - this->engine = TRUE; - if (!this->rsa) - { - destroy(this); - return NULL; } - - return &this->public; -#else /* OPENSSL_NO_ENGINE */ +error: + destroy(this); return NULL; -#endif /* OPENSSL_NO_ENGINE */ } #endif /* OPENSSL_NO_RSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.h b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.h index 34ce4c776..783181c1d 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.h +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.h @@ -67,9 +67,10 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_load(key_type_t type, * Wrap an EVP_PKEY object of type EVP_PKEY_RSA * * @param key EVP_PKEY_RSA key object (adopted) + * @param engine whether the key was loaded via an engine * @return loaded key, NULL on failure */ -private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key); +private_key_t *openssl_rsa_private_key_create(EVP_PKEY *key, bool engine); /** * Connect to a RSA private key on a smartcard. diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c index d3a644f72..20bf30ae9 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c @@ -1,7 +1,7 @@ /* + * Copyright (C) 2008-2017 Tobias Brunner * Copyright (C) 2009 Martin Willi - * Copyright (C) 2008 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * 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 @@ -19,9 +19,11 @@ #ifndef OPENSSL_NO_RSA #include "openssl_rsa_public_key.h" +#include "openssl_hasher.h" #include "openssl_util.h" #include <utils/debug.h> +#include <credentials/keys/signature_params.h> #include <openssl/bn.h> #include <openssl/evp.h> @@ -54,8 +56,138 @@ struct private_openssl_rsa_public_key_t { refcount_t ref; }; + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + +/** + * Verify RSA signature + */ +static bool verify_signature(private_openssl_rsa_public_key_t *this, + const EVP_MD *md, rsa_pss_params_t *pss, + chunk_t data, chunk_t signature) +{ + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *mctx = NULL; + EVP_PKEY *key; + int rsa_size = RSA_size(this->rsa); + bool valid = FALSE; + + /* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */ + if (signature.len > rsa_size) + { + signature = chunk_skip(signature, signature.len - rsa_size); + } + + mctx = EVP_MD_CTX_create(); + key = EVP_PKEY_new(); + if (!mctx || !key) + { + goto error; + } + if (!EVP_PKEY_set1_RSA(key, this->rsa)) + { + goto error; + } + if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, key) <= 0) + { + goto error; + } + if (pss) + { + const EVP_MD *mgf1md = openssl_get_md(pss->mgf1_hash); + int slen = EVP_MD_size(md); + if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) + { + slen = pss->salt_len; + } + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, slen) <= 0 || + EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md) <= 0) + { + goto error; + } + } + if (EVP_DigestVerifyUpdate(mctx, data.ptr, data.len) <= 0) + { + goto error; + } + valid = (EVP_DigestVerifyFinal(mctx, signature.ptr, signature.len) == 1); + +error: + if (key) + { + EVP_PKEY_free(key); + } + if (mctx) + { + EVP_MD_CTX_destroy(mctx); + } + return valid; +} + /** - * Verification of an EMPSA PKCS1 signature described in PKCS#1 + * Verification of a signature without hashing + */ +static bool verify_plain_signature(private_openssl_rsa_public_key_t *this, + chunk_t data, chunk_t signature) +{ + char *buf; + int len, rsa_size = RSA_size(this->rsa); + bool valid = FALSE; + + /* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */ + if (signature.len > rsa_size) + { + signature = chunk_skip(signature, signature.len - rsa_size); + } + buf = malloc(rsa_size); + len = RSA_public_decrypt(signature.len, signature.ptr, buf, this->rsa, + RSA_PKCS1_PADDING); + if (len != -1) + { + valid = chunk_equals_const(data, chunk_create(buf, len)); + } + free(buf); + return valid; +} + +/** + * Verification of an EMSA PKCS1 signature described in PKCS#1 + */ +static bool verify_emsa_pkcs1_signature(private_openssl_rsa_public_key_t *this, + int type, chunk_t data, chunk_t signature) +{ + const EVP_MD *md; + + if (type == NID_undef) + { + return verify_plain_signature(this, data, signature); + } + md = EVP_get_digestbynid(type); + return md && verify_signature(this, md, NULL, data, signature); +} + +/** + * Verification of an EMSA PSS signature described in PKCS#1 + */ +static bool verify_emsa_pss_signature(private_openssl_rsa_public_key_t *this, + rsa_pss_params_t *params, chunk_t data, + chunk_t signature) +{ + const EVP_MD *md; + + if (!params) + { + return FALSE; + } + md = openssl_get_md(params->hash); + return md && verify_signature(this, md, params, data, signature); +} + +#else /* OPENSSL_VERSION_NUMBER < 1.0 */ + +/** + * Verification of an EMSA PKCS1 signature described in PKCS#1 */ static bool verify_emsa_pkcs1_signature(private_openssl_rsa_public_key_t *this, int type, chunk_t data, chunk_t signature) @@ -129,6 +261,8 @@ error: return valid; } +#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ + METHOD(public_key_t, get_type, key_type_t, private_openssl_rsa_public_key_t *this) { @@ -137,7 +271,7 @@ METHOD(public_key_t, get_type, key_type_t, METHOD(public_key_t, verify, bool, private_openssl_rsa_public_key_t *this, signature_scheme_t scheme, - chunk_t data, chunk_t signature) + void *params, chunk_t data, chunk_t signature) { switch (scheme) { @@ -155,6 +289,10 @@ METHOD(public_key_t, verify, bool, return verify_emsa_pkcs1_signature(this, NID_sha1, data, signature); case SIGN_RSA_EMSA_PKCS1_MD5: return verify_emsa_pkcs1_signature(this, NID_md5, data, signature); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + case SIGN_RSA_EMSA_PSS: + return verify_emsa_pss_signature(this, params, data, signature); +#endif default: DBG1(DBG_LIB, "signature scheme %N not supported in RSA", signature_scheme_names, scheme); diff --git a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c index f6df03f12..3a6d2f193 100644 --- a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c +++ b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c @@ -20,6 +20,7 @@ #include "openssl_sha1_prf.h" #include <openssl/sha.h> +#include <crypto/hashers/hasher.h> typedef struct private_openssl_sha1_prf_t private_openssl_sha1_prf_t; diff --git a/src/libstrongswan/plugins/openssl/openssl_x509.c b/src/libstrongswan/plugins/openssl/openssl_x509.c index e03a4255d..60c08770b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_x509.c +++ b/src/libstrongswan/plugins/openssl/openssl_x509.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2011-2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG @@ -154,7 +154,7 @@ struct private_openssl_x509_t { /** * Signature scheme of the certificate */ - signature_scheme_t scheme; + signature_params_t *scheme; /** * subjectAltNames @@ -189,16 +189,6 @@ struct private_openssl_x509_t { }; /** - * Destroy a CRL URI struct - */ -static void crl_uri_destroy(x509_cdp_t *this) -{ - free(this->uri); - DESTROY_IF(this->issuer); - free(this); -} - -/** * Convert a GeneralName to an identification_t. */ static identification_t *general_name2id(GENERAL_NAME *name) @@ -394,7 +384,7 @@ METHOD(certificate_t, has_issuer, id_match_t, METHOD(certificate_t, issued_by, bool, private_openssl_x509_t *this, certificate_t *issuer, - signature_scheme_t *scheme) + signature_params_t **scheme) { public_key_t *key; bool valid; @@ -406,7 +396,8 @@ METHOD(certificate_t, issued_by, bool, { if (this->flags & X509_SELF_SIGNED) { - return TRUE; + valid = TRUE; + goto out; } } else @@ -424,10 +415,6 @@ METHOD(certificate_t, issued_by, bool, return FALSE; } } - if (this->scheme == SIGN_UNKNOWN) - { - return FALSE; - } key = issuer->get_public_key(issuer); if (!key) { @@ -440,12 +427,15 @@ METHOD(certificate_t, issued_by, bool, tbs = openssl_i2chunk(X509_CINF, this->x509->cert_info); #endif X509_get0_signature(&sig, NULL, this->x509); - valid = key->verify(key, this->scheme, tbs, openssl_asn1_str2chunk(sig)); + valid = key->verify(key, this->scheme->scheme, this->scheme->params, tbs, + openssl_asn1_str2chunk(sig)); free(tbs.ptr); key->destroy(key); + +out: if (valid && scheme) { - *scheme = this->scheme; + *scheme = signature_params_clone(this->scheme); } return valid; } @@ -538,6 +528,7 @@ METHOD(certificate_t, destroy, void, { X509_free(this->x509); } + signature_params_destroy(this->scheme); DESTROY_IF(this->subject); DESTROY_IF(this->issuer); DESTROY_IF(this->pubkey); @@ -549,7 +540,8 @@ METHOD(certificate_t, destroy, void, offsetof(identification_t, destroy)); this->issuerAltNames->destroy_offset(this->issuerAltNames, offsetof(identification_t, destroy)); - this->crl_uris->destroy_function(this->crl_uris, (void*)crl_uri_destroy); + this->crl_uris->destroy_function(this->crl_uris, + (void*)x509_cdp_destroy); this->ocsp_uris->destroy_function(this->ocsp_uris, free); this->ipAddrBlocks->destroy_offset(this->ipAddrBlocks, offsetof(traffic_selector_t, destroy)); @@ -739,15 +731,15 @@ static bool parse_extKeyUsage_ext(private_openssl_x509_t *this, /** * Parse CRL distribution points */ -static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this, - X509_EXTENSION *ext) +bool openssl_parse_crlDistributionPoints(X509_EXTENSION *ext, + linked_list_t *list) { CRL_DIST_POINTS *cdps; DIST_POINT *cdp; identification_t *id, *issuer; x509_cdp_t *entry; char *uri; - int i, j, k, point_num, name_num, issuer_num; + int i, j, k, point_num, name_num, issuer_num, len; cdps = X509V3_EXT_d2i(ext); if (!cdps) @@ -770,7 +762,12 @@ static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this, cdp->distpoint->name.fullname, j)); if (id) { - if (asprintf(&uri, "%Y", id) > 0) + len = asprintf(&uri, "%Y", id); + if (!len) + { + free(uri); + } + else if (len > 0) { if (cdp->CRLissuer) { @@ -785,8 +782,7 @@ static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this, .uri = strdup(uri), .issuer = issuer, ); - this->crl_uris->insert_last( - this->crl_uris, entry); + list->insert_last(list, entry); } } free(uri); @@ -796,7 +792,7 @@ static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this, INIT(entry, .uri = uri, ); - this->crl_uris->insert_last(this->crl_uris, entry); + list->insert_last(list, entry); } } id->destroy(id); @@ -820,7 +816,7 @@ static bool parse_authorityInfoAccess_ext(private_openssl_x509_t *this, AUTHORITY_INFO_ACCESS *infos; ACCESS_DESCRIPTION *desc; identification_t *id; - int i, num; + int i, num, len; char *uri; infos = X509V3_EXT_d2i(ext); @@ -839,7 +835,12 @@ static bool parse_authorityInfoAccess_ext(private_openssl_x509_t *this, id = general_name2id(desc->location); if (id) { - if (asprintf(&uri, "%Y", id) > 0) + len = asprintf(&uri, "%Y", id); + if (!len) + { + free(uri); + } + else if (len > 0) { this->ocsp_uris->insert_last(this->ocsp_uris, uri); } @@ -1025,7 +1026,7 @@ static bool parse_extensions(private_openssl_x509_t *this) ok = parse_extKeyUsage_ext(this, ext); break; case NID_crl_distribution_points: - ok = parse_crlDistributionPoints_ext(this, ext); + ok = openssl_parse_crlDistributionPoints(ext, this->crl_uris); break; #ifndef OPENSSL_NO_RFC3779 case NID_sbgp_ipAddrBlock: @@ -1063,8 +1064,8 @@ static bool parse_certificate(private_openssl_x509_t *this) { const unsigned char *ptr = this->encoding.ptr; hasher_t *hasher; - chunk_t chunk; - ASN1_OBJECT *oid, *oid_tbs; + chunk_t chunk, sig_scheme, sig_scheme_tbs; + ASN1_OBJECT *oid; X509_ALGOR *alg; this->x509 = d2i_X509(NULL, &ptr, this->encoding.len); @@ -1089,6 +1090,10 @@ static bool parse_certificate(private_openssl_x509_t *this) } switch (openssl_asn1_known_oid(oid)) { + case OID_RSASSA_PSS: + /* TODO: we should treat such keys special and use the params as + * restrictions regarding the use of this key (or rather the + * associated private key) */ case OID_RSA_ENCRYPTION: this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_ASN1_DER, @@ -1119,15 +1124,25 @@ static bool parse_certificate(private_openssl_x509_t *this) /* while X509_ALGOR_cmp() is declared in the headers of older OpenSSL * versions, at least on Ubuntu 14.04 it is not actually defined */ X509_get0_signature(NULL, &alg, this->x509); - X509_ALGOR_get0(&oid, NULL, NULL, alg); + sig_scheme = openssl_i2chunk(X509_ALGOR, alg); alg = X509_get0_tbs_sigalg(this->x509); - X509_ALGOR_get0(&oid_tbs, NULL, NULL, alg); - if (!chunk_equals(openssl_asn1_obj2chunk(oid), - openssl_asn1_obj2chunk(oid_tbs))) + sig_scheme_tbs = openssl_i2chunk(X509_ALGOR, alg); + if (!chunk_equals(sig_scheme, sig_scheme_tbs)) + { + free(sig_scheme_tbs.ptr); + free(sig_scheme.ptr); + return FALSE; + } + free(sig_scheme_tbs.ptr); + + INIT(this->scheme); + if (!signature_params_parse(sig_scheme, 0, this->scheme)) { + DBG1(DBG_ASN, "unable to parse signature algorithm"); + free(sig_scheme.ptr); return FALSE; } - this->scheme = signature_scheme_from_oid(openssl_asn1_known_oid(oid)); + free(sig_scheme.ptr); if (!parse_extensions(this)) { |