From fe351e0c111c98e1ad317df05b87705a233c46d6 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 5 Jul 2023 12:28:39 -0400 Subject: [PATCH] openssl: add openssl_digest_many() Add function to perform openssl digest calculation on multiple buffers. (cherry picked from commit bed4831ce227a59d40b3712a3b1deee9fe0440f5) Related: RHEL-16182 --- src/shared/openssl-util.c | 58 +++++++++++++++++++++++++++++++++++++ src/shared/openssl-util.h | 7 +++++ src/test/test-openssl.c | 60 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index ecdb418402..0aef979e8c 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -118,6 +118,64 @@ int openssl_digest_size(const char *digest_alg, size_t *ret_digest_size) { return 0; } +/* Calculate the digest hash value for the provided data, using the specified digest algorithm. Returns 0 on + * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */ +int openssl_digest_many( + const char *digest_alg, + const struct iovec data[], + size_t n_data, + void **ret_digest, + size_t *ret_digest_size) { + + int r; + + assert(digest_alg); + assert(data || n_data == 0); + assert(ret_digest); + /* ret_digest_size is optional, as caller may already know the digest size */ + +#if OPENSSL_VERSION_MAJOR >= 3 + _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); +#else + const EVP_MD *md = EVP_get_digestbyname(digest_alg); +#endif + if (!md) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Digest algorithm '%s' not supported.", digest_alg); + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if (!ctx) + return log_openssl_errors("Failed to create new EVP_MD_CTX"); + + if (!EVP_DigestInit_ex(ctx, md, NULL)) + return log_openssl_errors("Failed to initializate EVP_MD_CTX"); + + for (size_t i = 0; i < n_data; i++) + if (!EVP_DigestUpdate(ctx, data[i].iov_base, data[i].iov_len)) + return log_openssl_errors("Failed to update Digest"); + + size_t digest_size; + r = openssl_digest_size(digest_alg, &digest_size); + if (r < 0) + return r; + + _cleanup_free_ void *buf = malloc(digest_size); + if (!buf) + return log_oom_debug(); + + unsigned int size; + if (!EVP_DigestFinal_ex(ctx, buf, &size)) + return log_openssl_errors("Failed to finalize Digest"); + + assert(size == digest_size); + + *ret_digest = TAKE_PTR(buf); + if (ret_digest_size) + *ret_digest_size = size; + + return 0; +} + int rsa_encrypt_bytes( EVP_PKEY *pkey, const void *decrypted_key, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 309dc16805..f1c84c102e 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "io-util.h" #include "macro.h" #include "sha256.h" @@ -60,6 +61,12 @@ int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, uint8_t *re int openssl_digest_size(const char *digest_alg, size_t *ret_digest_size); +int openssl_digest_many(const char *digest_alg, const struct iovec data[], size_t n_data, void **ret_digest, size_t *ret_digest_size); + +static inline int openssl_digest(const char *digest_alg, const void *buf, size_t len, void **ret_digest, size_t *ret_digest_size) { + return openssl_digest_many(digest_alg, &IOVEC_MAKE((void*) buf, len), 1, ret_digest, ret_digest_size); +} + int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size); int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size); diff --git a/src/test/test-openssl.c b/src/test/test-openssl.c index a8a2b534a4..35ac980d25 100644 --- a/src/test/test-openssl.c +++ b/src/test/test-openssl.c @@ -154,4 +154,64 @@ TEST(digest_size) { assert_se(openssl_digest_size("invalid.alg", &size) == -EOPNOTSUPP); } +static void verify_digest(const char *digest_alg, const struct iovec *data, size_t n_data, const char *expect) { + _cleanup_free_ void *digest = NULL; + size_t digest_size; + int r; + + r = openssl_digest_many(digest_alg, data, n_data, &digest, &digest_size); + if (r == -EOPNOTSUPP) + return; + assert_se(r >= 0); + + DEFINE_HEX_PTR(e, expect); + assert_se(memcmp_nn(e, e_len, digest, digest_size) == 0); +} + +#define _DEFINE_DIGEST_TEST(uniq, alg, expect, ...) \ + const struct iovec UNIQ_T(i, uniq)[] = { __VA_ARGS__ }; \ + verify_digest(alg, \ + UNIQ_T(i, uniq), \ + ELEMENTSOF(UNIQ_T(i, uniq)), \ + expect); +#define DEFINE_DIGEST_TEST(alg, expect, ...) _DEFINE_DIGEST_TEST(UNIQ, alg, expect, __VA_ARGS__) +#define DEFINE_SHA1_TEST(expect, ...) DEFINE_DIGEST_TEST("SHA1", expect, __VA_ARGS__) +#define DEFINE_SHA256_TEST(expect, ...) DEFINE_DIGEST_TEST("SHA256", expect, __VA_ARGS__) +#define DEFINE_SHA384_TEST(expect, ...) DEFINE_DIGEST_TEST("SHA384", expect, __VA_ARGS__) +#define DEFINE_SHA512_TEST(expect, ...) DEFINE_DIGEST_TEST("SHA512", expect, __VA_ARGS__) + +TEST(digest_many) { + const struct iovec test = IOVEC_MAKE_STRING("test"); + + /* Empty digests */ + DEFINE_SHA1_TEST("da39a3ee5e6b4b0d3255bfef95601890afd80709"); + DEFINE_SHA256_TEST("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + DEFINE_SHA384_TEST("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); + DEFINE_SHA512_TEST("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + + DEFINE_SHA1_TEST("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", test); + DEFINE_SHA256_TEST("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", test); + DEFINE_SHA384_TEST("768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9", test); + DEFINE_SHA512_TEST("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", test); + + DEFINE_HEX_PTR(h1, "e9ff2b6dfbc03b8dd0471a0f23840334e3ef51c64a325945524563c0375284a092751eca8d084fae22f74a104559a0ee8339d1845538481e674e6d31d4f63089"); + DEFINE_HEX_PTR(h2, "5b6e809933a1b8d5a4a6bb62e20b36ae82d9408141e7479d0aa067273bd2d04007fb1977bad549d54330a49ed98f82b495ba"); + DEFINE_HEX_PTR(h3, "d2aeef94d7ba2a"); + DEFINE_HEX_PTR(h4, "1557db45ded3e38c79b5bb25c83ade42fa7d13047ef1b9a0b21a3c2ab2d4eee5c75e2927ce643163addbda65331035850a436c0acffc723f419e1d1cbf04c9064e6d850580c0732a12600f9feb"); + + const struct iovec i1 = IOVEC_MAKE(h1, h1_len); + const struct iovec i2 = IOVEC_MAKE(h2, h2_len); + const struct iovec i3 = IOVEC_MAKE(h3, h3_len); + const struct iovec i4 = IOVEC_MAKE(h4, h4_len); + + DEFINE_SHA1_TEST("8e7c659a6331508b06adf98b430759dafb92fc43", i1, i2, i3, i4); + DEFINE_SHA256_TEST("4d6be38798786a5500651c1a02d96aa010e9d7b2bece1695294cd396d456cde8", i1, i2, i3, i4); + DEFINE_SHA384_TEST("82e6ec14f8d90f1ae1fd4fb7f415ea6fdb674515b13092e3e548a8d37a8faed30cda8ea613ec2a015a51bc578dacc995", i1, i2, i3, i4); + DEFINE_SHA512_TEST("21fe5beb15927257a9143ff59010e51d4c65c7c5237b0cd9a8db3c3fabe429be3a0759f9ace3cdd70f6ea543f998bec9bc3308833d70aa1bd380364de872a62c", i1, i2, i3, i4); + + DEFINE_SHA256_TEST("0e0ed67d6717dc08dd6f472f6c35107a92b8c2695dcba344b884436f97a9eb4d", i1, i1, i1, i4); + + DEFINE_SHA256_TEST("8fe8b8d1899c44bfb82e1edc4ff92642db5b2cb25c4210ea06c3846c757525a8", i1, i1, i1, i4, i4, i4, i4, i3, i3, i2); +} + DEFINE_TEST_MAIN(LOG_DEBUG);