From: Arran Cudbard-Bell Date: Fri, 1 Oct 2021 17:34:13 +0000 (-0500) Subject: Update hmac_sha1 and hmac_md5 to use the EVP API X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce35fee912a6e9c91f5b37d79e236471973e412d;p=thirdparty%2Ffreeradius-server.git Update hmac_sha1 and hmac_md5 to use the EVP API Calling the HMAC functions directly is deprecated in OpenSSL 3.0 --- diff --git a/src/lib/util/all.mk b/src/lib/util/all.mk index 3ebbe514fc..733d0f8d2e 100644 --- a/src/lib/util/all.mk +++ b/src/lib/util/all.mk @@ -5,6 +5,7 @@ SUBMAKEFILES := \ dcursor_tests.mk \ dlist_tests.mk \ heap_tests.mk \ + hmac_tests.mk \ libfreeradius-util.mk \ lst_tests.mk \ pair_legacy_tests.mk \ diff --git a/src/lib/util/hmac_md5.c b/src/lib/util/hmac_md5.c index fcfc32c775..913bceeaa3 100644 --- a/src/lib/util/hmac_md5.c +++ b/src/lib/util/hmac_md5.c @@ -30,17 +30,18 @@ */ RCSID("$Id$") -#include #include +#include +#include #ifdef HAVE_OPENSSL_EVP_H # include -static _Thread_local HMAC_CTX *md5_hmac_ctx; +static _Thread_local EVP_MD_CTX *md5_hmac_ctx; static void _hmac_md5_ctx_free_on_exit(void *arg) { - HMAC_CTX_free(arg); + EVP_MD_CTX_free(arg); } /** Calculate HMAC using OpenSSL's MD5 implementation @@ -50,30 +51,56 @@ static void _hmac_md5_ctx_free_on_exit(void *arg) * @param inlen length of data stream. * @param key Pointer to authentication key. * @param key_len Length of authentication key. - * + * @return + * - 0 on success. + * - -1 on error. */ -void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, - uint8_t const *key, size_t key_len) +int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, + uint8_t const *key, size_t key_len) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; + EVP_PKEY *pkey; if (unlikely(!md5_hmac_ctx)) { - ctx = HMAC_CTX_new(); - if (unlikely(!ctx)) return; + ctx = EVP_MD_CTX_new(); + if (unlikely(!ctx)) { + fr_strerror_const("Failed allocating EVP_MD_CTX for HMAC-MD5"); + return -1; + } + EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT); fr_atexit_thread_local(md5_hmac_ctx, _hmac_md5_ctx_free_on_exit, ctx); } else { ctx = md5_hmac_ctx; } -#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW - /* Since MD5 is not allowed by FIPS, explicitly allow it. */ - HMAC_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); -#endif /* EVP_MD_CTX_FLAG_NON_FIPS_ALLOW */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len); + if (unlikely(pkey == NULL)) { + fr_strerror_const("Failed allocating pkey for HMAC-MD5"); + return -1; + } - HMAC_Init_ex(ctx, key, key_len, EVP_md5(), NULL); - HMAC_Update(ctx, in, inlen); - HMAC_Final(ctx, digest, NULL); - HMAC_CTX_reset(ctx); + if (unlikely(EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey) != 1)) { + fr_strerror_const("Failed initialising EVP_MD_CTX for HMAC-MD5"); + error: + EVP_PKEY_free(pkey); + return -1; + } + if (unlikely(EVP_DigestSignUpdate(ctx, in, inlen) != 1)) { + fr_strerror_const("Failed ingesting data for HMAC-MD5"); + goto error; + } + /* + * OpenSSL <= 1.1.1 requires a non-null pointer for len + */ + if (unlikely(EVP_DigestSignFinal(ctx, digest, &(size_t){ 0 }) != 1)) { + fr_strerror_const("Failed finalising HMAC-MD5"); + goto error; + } + + EVP_PKEY_free(pkey); + EVP_MD_CTX_reset(ctx); + + return 0; } #else /** Calculate HMAC using internal MD5 implementation @@ -83,10 +110,12 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t in * @param inlen length of data stream. * @param key Pointer to authentication key. * @param key_len Length of authentication key. - * + * @return + * - 0 on success. + * - -1 on error. */ -void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, - uint8_t const *key, size_t key_len) +int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, + uint8_t const *key, size_t key_len) { fr_md5_ctx_t *ctx; uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */ @@ -146,64 +175,7 @@ void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t in fr_md5_final(digest, ctx); /* finish up 2nd pass */ fr_md5_ctx_free(&ctx); -} -#endif /* HAVE_OPENSSL_EVP_H */ - -/* -Test Vectors (Trailing '\0' of a character string not included in test): - - key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b - key_len = 16 bytes - data = "Hi There" - data_len = 8 bytes - digest = 0x9294727a3638bb1c13f48ef8158bfc9d - - key = "Jefe" - data = "what do ya want for nothing?" - data_len = 28 bytes - digest = 0x750c783e6ab0b503eaa86e310a5db738 - - key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - - key_len 16 bytes - data = 0xDDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD - data_len = 50 bytes - digest = 0x56be34521d144c88dbb8c733f0e8b3f6 -*/ - -#ifdef TESTING -/* - * cc -DTESTING -I ../include/ hmac.c md5.c -o hmac - * - * ./hmac Jefe "what do ya want for nothing?" - */ -int main(int argc, char **argv) -{ - uint8_t digest[16]; - char *key; - int key_len; - char *text; - int text_len; - int i; - - key = argv[1]; - key_len = strlen(key); - - text = argv[2]; - text_len = strlen(text); - - fr_hmac_md5(digest, text, text_len, key, key_len); - - for (i = 0; i < 16; i++) { - printf("%02x", digest[i]); - } - printf("\n"); return 0; } - -#endif +#endif /* HAVE_OPENSSL_EVP_H */ diff --git a/src/lib/util/hmac_sha1.c b/src/lib/util/hmac_sha1.c index a87ec020bc..698d8f63df 100644 --- a/src/lib/util/hmac_sha1.c +++ b/src/lib/util/hmac_sha1.c @@ -33,6 +33,7 @@ RCSID("$Id$") #include #include +#include #ifdef HMAC_SHA1_DATA_PROBLEMS unsigned int sha1_data_problems = 0; @@ -41,11 +42,11 @@ unsigned int sha1_data_problems = 0; #ifdef HAVE_OPENSSL_EVP_H # include -static _Thread_local HMAC_CTX *sha1_hmac_ctx; +static _Thread_local EVP_MD_CTX *sha1_hmac_ctx; static void _hmac_sha1_ctx_free_on_exit(void *arg) { - HMAC_CTX_free(arg); + EVP_MD_CTX_free(arg); } /** Calculate HMAC using OpenSSL's SHA1 implementation @@ -55,25 +56,56 @@ static void _hmac_sha1_ctx_free_on_exit(void *arg) * @param inlen length of data stream. * @param key Pointer to authentication key. * @param key_len Length of authentication key. - + * @return + * - 0 on success. + * - -1 on error. */ -void fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, - uint8_t const *key, size_t key_len) +int fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, + uint8_t const *key, size_t key_len) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; + EVP_PKEY *pkey; if (unlikely(!sha1_hmac_ctx)) { - ctx = HMAC_CTX_new(); - if (unlikely(!ctx)) return; + ctx = EVP_MD_CTX_new(); + if (unlikely(!ctx)) { + fr_strerror_const("Failed allocating EVP_MD_CTX for HMAC-SHA1"); + return -1; + } + EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_ONESHOT); fr_atexit_thread_local(sha1_hmac_ctx, _hmac_sha1_ctx_free_on_exit, ctx); } else { ctx = sha1_hmac_ctx; } - HMAC_Init_ex(ctx, key, key_len, EVP_sha1(), NULL); - HMAC_Update(ctx, in, inlen); - HMAC_Final(ctx, digest, NULL); - HMAC_CTX_reset(ctx); + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len); + if (unlikely(pkey == NULL)) { + fr_strerror_const("Failed allocating pkey for HMAC-SHA1"); + return -1; + } + + if (unlikely(EVP_DigestSignInit(ctx, NULL, EVP_sha1(), NULL, pkey) != 1)) { + fr_strerror_const("Failed initialising EVP_MD_CTX for HMAC-SHA1"); + error: + EVP_PKEY_free(pkey); + return -1; + } + if (unlikely(EVP_DigestSignUpdate(ctx, in, inlen) != 1)) { + fr_strerror_const("Failed ingesting data for HMAC-SHA1"); + goto error; + } + /* + * OpenSSL <= 1.1.1 requires a non-null pointer for len + */ + if (unlikely(EVP_DigestSignFinal(ctx, digest, &(size_t){ 0 }) != 1)) { + fr_strerror_const("Failed finalising HMAC-SHA1"); + goto error; + } + + EVP_PKEY_free(pkey); + EVP_MD_CTX_reset(ctx); + + return 0; } #else /** Calculate HMAC using internal SHA1 implementation @@ -83,9 +115,12 @@ void fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *in, size_t * @param inlen length of data stream. * @param key Pointer to authentication key. * @param key_len Length of authentication key. + * @return + * - 0 on success. + * - -1 on error. */ -void fr_hmac_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, - uint8_t const *key, size_t key_len) +int fr_hmac_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, size_t inlen, + uint8_t const *key, size_t key_len) { fr_sha1_ctx ctx; uint8_t k_ipad[65]; /* inner padding - key XORd with ipad */ @@ -200,58 +235,6 @@ void fr_hmac_sha1(uint8_t digest[static SHA1_DIGEST_LENGTH], uint8_t const *in, printf("\n"); } #endif -} -#endif /* HAVE_OPENSSL_EVP_H */ - -/* -Test Vectors (Trailing '\0' of a character string not included in test): - - key = "Jefe" - data = "what do ya want for nothing?" - data_len = 28 bytes - digest = effcdf6ae5eb2fa2d27416d5f184df9c259a7c79 - - key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - - key_len 16 bytes - data = 0xDDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD... - ..DDDDDDDDDDDDDDDDDDDD - data_len = 50 bytes - digest = 0x56be34521d144c88dbb8c733f0e8b3f6 -*/ - -#ifdef TESTING -/* - * cc -DTESTING -I ../include/ hmac.c sha1.c -o hmac - * - * ./hmac Jefe "what do ya want for nothing?" - */ -int main(int argc, char **argv) -{ - uint8_t digest[20]; - char *key; - int key_len; - char *text; - int text_len; - int i; - - key = argv[1]; - key_len = strlen(key); - - text = argv[2]; - text_len = strlen(text); - - fr_hmac_sha1(digest, text, text_len, key, key_len); - - for (i = 0; i < 20; i++) { - printf("%02x", digest[i]); - } - printf("\n"); - return 0; } - -#endif +#endif /* HAVE_OPENSSL_EVP_H */ diff --git a/src/lib/util/hmac_tests.c b/src/lib/util/hmac_tests.c new file mode 100644 index 0000000000..1c96b7f8e4 --- /dev/null +++ b/src/lib/util/hmac_tests.c @@ -0,0 +1,216 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Tests for the internal hmac functions + * + * @file src/lib/util/hmac_tests.c + * + * @copyright 2021 Arran Cudbard-Bell + */ + +#include +#include +#include +#include + +/* +Test Vectors (Trailing '\0' of a character string not included in test): + + key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + key_len = 16 bytes + data = "Hi There" + data_len = 8 bytes + digest = 0x9294727a3638bb1c13f48ef8158bfc9d + + key = "Jefe" + data = "what do ya want for nothing?" + data_len = 28 bytes + digest = 0x750c783e6ab0b503eaa86e310a5db738 + + key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + key_len 16 bytes + data = 0xDDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD + data_len = 50 bytes + digest = 0x56be34521d144c88dbb8c733f0e8b3f6 +*/ +static void test_hmac_md5(void) +{ + uint8_t digest[16]; + uint8_t const *key; + int key_len; + uint8_t const *text; + int text_len; + + /* + * Test 1 + */ + key = (uint8_t[]){ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x00 + }; + key_len = strlen((char const *)key); + + text = (uint8_t const *)"Hi There"; + text_len = strlen((char const *)text); + + fr_hmac_md5(digest, text, text_len, key, key_len); + + TEST_CHECK_RET(memcmp(digest, + (uint8_t[]){ + 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, + 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d + }, + sizeof(digest)), 0); + + /* + * Test 2 + */ + key = (uint8_t const *)"Jefe"; + key_len = strlen((char const *)key); + + text = (uint8_t const *)"what do ya want for nothing?"; + text_len = strlen((char const *)text); + + fr_hmac_md5(digest, text, text_len, key, key_len); + + TEST_CHECK_RET(memcmp(digest, + (uint8_t[]){ + 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03, + 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 + }, + sizeof(digest)), 0); + + /* + * Test 3 + */ + key = (uint8_t[]){ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0x00 + }; + key_len = strlen((char const *)key); + + text = (uint8_t[]){ + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0x00 + }; + text_len = strlen((char const *)text); + + fr_hmac_md5(digest, text, text_len, key, key_len); + + TEST_CHECK_RET(memcmp(digest, + (uint8_t[]){ + 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88, + 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 + }, + sizeof(digest)), 0); +} + +/* +Test Vectors (Trailing '\0' of a character string not included in test): + + key = "Jefe" + data = "what do ya want for nothing?" + data_len = 28 bytes + digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79 + + key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + key_len 16 bytes + data = 0xDDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD... + ..DDDDDDDDDDDDDDDDDDDD + data_len = 50 bytes + digest = 0xd730594d167e35d5956fd8003d0db3d3f46dc7bb +*/ +static void test_hmac_sha1(void) +{ + + uint8_t digest[20]; + uint8_t const *key; + int key_len; + uint8_t const *text; + int text_len; + + /* + * Test 1 + */ + key = (uint8_t const *)"Jefe"; + key_len = strlen((char const *)key); + + text = (uint8_t const *)"what do ya want for nothing?"; + text_len = strlen((char const *)text); + + fr_hmac_sha1(digest, text, text_len, key, key_len); + + TEST_CHECK_RET(memcmp(digest, + (uint8_t[]){ + 0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2, 0xd2, 0x74, + 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c, 0x25, 0x9a, 0x7c, 0x79 + }, + sizeof(digest)), 0); + + /* + * Test 2 + */ + key = (uint8_t[]){ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0x00 + }; + key_len = strlen((char const *)key); + + text = (uint8_t[]){ + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0x00 + }; + text_len = strlen((char const *)text); + + fr_hmac_sha1(digest, text, text_len, key, key_len); + + TEST_CHECK_RET(memcmp(digest, + (uint8_t[]){ + 0xd7, 0x30, 0x59, 0x4d, 0x16, 0x7e, 0x35, 0xd5, 0x95, 0x6f, + 0xd8, 0x00, 0x3d, 0x0d, 0xb3, 0xd3, 0xf4, 0x6d, 0xc7, 0xbb + }, + sizeof(digest)), 0); +} + +TEST_LIST = { + /* + * Allocation and management + */ + { "hmac-md5", test_hmac_md5 }, + { "hmac-sha1", test_hmac_sha1 }, + + { NULL } +}; diff --git a/src/lib/util/hmac_tests.mk b/src/lib/util/hmac_tests.mk new file mode 100644 index 0000000000..3a6cbf2273 --- /dev/null +++ b/src/lib/util/hmac_tests.mk @@ -0,0 +1,7 @@ +TARGET := hmac_tests + +SOURCES := hmac_tests.c + +TGT_LDLIBS := $(LIBS) +TGT_LDFLAGS := $(LDFLAGS) +TGT_PREREQS := libfreeradius-util.a