From 2b9b579af9ddf80e56ed1eb10bbbb8b76a2a299b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Feb 2022 16:38:34 +0100 Subject: [PATCH] openssl: Add a prf+ implementation based on OpenSSL's HKDF implementation The HKDF-Expand() function defined in RFC 5869 is basically the same as IKEv2's prf+(), so we can use the former to implement the latter. However, we can only support HMAC-based PRFs this way, which should be fine as others are rarely used. --- src/libstrongswan/plugins/openssl/Makefile.am | 1 + .../plugins/openssl/openssl_kdf.c | 175 ++++++++++++++++++ .../plugins/openssl/openssl_kdf.h | 45 +++++ .../plugins/openssl/openssl_plugin.c | 6 + 4 files changed, 227 insertions(+) create mode 100644 src/libstrongswan/plugins/openssl/openssl_kdf.c create mode 100644 src/libstrongswan/plugins/openssl/openssl_kdf.h diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index b1073ed5eb..4df4a81b41 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -33,6 +33,7 @@ libstrongswan_openssl_la_SOURCES = \ openssl_pkcs12.c openssl_pkcs12.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h \ + openssl_kdf.c openssl_kdf.h \ openssl_aead.c openssl_aead.h \ openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \ openssl_ed_private_key.c openssl_ed_private_key.h \ diff --git a/src/libstrongswan/plugins/openssl/openssl_kdf.c b/src/libstrongswan/plugins/openssl/openssl_kdf.c new file mode 100644 index 0000000000..3e632476a3 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_kdf.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#if !defined(OPENSSL_NO_HMAC) && OPENSSL_VERSION_NUMBER >= 0x10101000L + +#include +#include + +#include "openssl_kdf.h" + +typedef struct private_kdf_t private_kdf_t; + +/** + * Private data. + */ +struct private_kdf_t { + + /** + * Public interface. + */ + kdf_t public; + + /** + * Hasher to use for underlying PRF. + */ + const EVP_MD *hasher; + + /** + * Key for KDF. Stored here because OpenSSL's HKDF API does not provide a + * way to clear the "info" field in the context, new data is always + * appended (up to 1024 bytes). + */ + chunk_t key; + + /** + * Salt for prf+ (see above). + */ + chunk_t salt; +}; + +METHOD(kdf_t, get_type, key_derivation_function_t, + private_kdf_t *this) +{ + return KDF_PRF_PLUS; +} + +METHOD(kdf_t, get_bytes, bool, + private_kdf_t *this, size_t out_len, uint8_t *buffer) +{ + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + + if (!ctx || + EVP_PKEY_derive_init(ctx) <= 0 || + EVP_PKEY_CTX_set_hkdf_md(ctx, this->hasher) <= 0 || + EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0 || + EVP_PKEY_CTX_set1_hkdf_key(ctx, this->key.ptr, this->key.len) <= 0 || + EVP_PKEY_CTX_add1_hkdf_info(ctx, this->salt.ptr, this->salt.len) <= 0 || + EVP_PKEY_derive(ctx, buffer, &out_len) <= 0) + { + EVP_PKEY_CTX_free(ctx); + return FALSE; + } + EVP_PKEY_CTX_free(ctx); + return TRUE; +} + +METHOD(kdf_t, allocate_bytes, bool, + private_kdf_t *this, size_t out_len, chunk_t *chunk) +{ + *chunk = chunk_alloc(out_len); + + if (!get_bytes(this, out_len, chunk->ptr)) + { + chunk_free(chunk); + return FALSE; + } + return TRUE; +} + +METHOD(kdf_t, set_param, bool, + private_kdf_t *this, kdf_param_t param, ...) +{ + chunk_t chunk; + + switch (param) + { + case KDF_PARAM_KEY: + VA_ARGS_GET(param, chunk); + chunk_clear(&this->key); + this->key = chunk_clone(chunk); + break; + case KDF_PARAM_SALT: + VA_ARGS_GET(param, chunk); + chunk_clear(&this->salt); + this->salt = chunk_clone(chunk); + break; + } + return TRUE; +} + +METHOD(kdf_t, destroy, void, + private_kdf_t *this) +{ + chunk_clear(&this->salt); + chunk_clear(&this->key); + free(this); +} + +/* + * Described in header + */ +kdf_t *openssl_kdf_create(key_derivation_function_t algo, va_list args) +{ + private_kdf_t *this; + pseudo_random_function_t prf_alg; + char *name, buf[8]; + + if (algo != KDF_PRF_PLUS) + { + return NULL; + } + + VA_ARGS_VGET(args, prf_alg); + name = enum_to_name(hash_algorithm_short_names, + hasher_algorithm_from_prf(prf_alg)); + if (!name) + { + return NULL; + } + + INIT(this, + .public = { + .get_type = _get_type, + .get_bytes = _get_bytes, + .allocate_bytes = _allocate_bytes, + .set_param = _set_param, + .destroy = _destroy, + }, + .hasher = EVP_get_digestbyname(name), + /* use a lengthy key to test the implementation below to make sure the + * algorithms are usable, see openssl_hmac.c for details */ + .key = chunk_clone(chunk_from_str("00000000000000000000000000000000")), + ); + + if (!this->hasher || !get_bytes(this, sizeof(buf), buf)) + { + destroy(this); + return NULL; + } + return &this->public; +} + +#endif /* OPENSSL_NO_HMAC && OPENSSL_VERSION_NUMBER */ diff --git a/src/libstrongswan/plugins/openssl/openssl_kdf.h b/src/libstrongswan/plugins/openssl/openssl_kdf.h new file mode 100644 index 0000000000..4480c38c2a --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_kdf.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Implements key derivation functions (KDF) via OpenSSL, in particular prf+, + * which is implemented via OpenSSL's HKDF implementation. + * + * @defgroup openssl_kdf openssl_kdf + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_KDF_H_ +#define OPENSSL_KDF_H_ + +#include + +/** + * Creates a new kdf_t object. + * + * @param algo algorithm to instantiate + * @param args algorithm-specific arguments + * @return kdf_t object, NULL if not supported + */ +kdf_t *openssl_kdf_create(key_derivation_function_t algo, va_list args); + +#endif /** OPENSSL_KDF_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 1491d5cf83..86f1e29755 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -52,6 +52,7 @@ #include "openssl_pkcs12.h" #include "openssl_rng.h" #include "openssl_hmac.h" +#include "openssl_kdf.h" #include "openssl_aead.h" #include "openssl_x_diffie_hellman.h" #include "openssl_ed_public_key.h" @@ -654,6 +655,11 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + /* HKDF is available since 1.1.0, expand-only mode only since 1.1.1 */ + PLUGIN_REGISTER(KDF, openssl_kdf_create), + PLUGIN_PROVIDE(KDF, KDF_PRF_PLUS), +#endif #endif /* OPENSSL_NO_HMAC */ #if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_AES)) || \ (OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_CHACHA)) -- 2.47.2