systemd/0605-openssl-add-openssl_hm...

286 lines
16 KiB
Diff

From 95440b3e8b6729252acd5e0940aed3dd42b96b0e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 26 Jun 2023 17:40:18 -0400
Subject: [PATCH] openssl: add openssl_hmac_many()
Add function to perform HMAC on multiple buffers.
Also update test-openssl with associated testing, and replace some memcmp()
with memcmp_nn().
(cherry picked from commit a95e8fa2a39cf8f2992fab9abc31b3df95c1ca00)
Related: RHEL-16182
---
src/shared/openssl-util.c | 97 ++++++++++++++++++++++++++++++++++++++
src/shared/openssl-util.h | 9 ++++
src/test/test-openssl.c | 99 +++++++++++++++++++++++++++++++++++----
3 files changed, 197 insertions(+), 8 deletions(-)
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c
index 7a69db4195..10664e362c 100644
--- a/src/shared/openssl-util.c
+++ b/src/shared/openssl-util.c
@@ -139,6 +139,103 @@ int openssl_digest_many(
return 0;
}
+/* Calculate the HMAC digest hash value for the provided data, using the provided key and specified digest
+ * algorithm. Returns 0 on success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any
+ * other error. */
+int openssl_hmac_many(
+ const char *digest_alg,
+ const void *key,
+ size_t key_size,
+ const struct iovec data[],
+ size_t n_data,
+ void **ret_digest,
+ size_t *ret_digest_size) {
+
+ assert(digest_alg);
+ assert(key);
+ 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);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_(EVP_MAC_freep) EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ if (!mac)
+ return log_openssl_errors("Failed to create new EVP_MAC");
+
+ _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac);
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_MAC_CTX");
+
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld)
+ return log_openssl_errors("Failed to create new OSSL_PARAM_BLD");
+
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, (char*) digest_alg, 0))
+ return log_openssl_errors("Failed to set HMAC OSSL_MAC_PARAM_DIGEST");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_openssl_errors("Failed to build HMAC OSSL_PARAM");
+
+ if (!EVP_MAC_init(ctx, key, key_size, params))
+ return log_openssl_errors("Failed to initializate EVP_MAC_CTX");
+#else
+ _cleanup_(HMAC_CTX_freep) HMAC_CTX *ctx = HMAC_CTX_new();
+ if (!ctx)
+ return log_openssl_errors("Failed to create new HMAC_CTX");
+
+ if (!HMAC_Init_ex(ctx, key, key_size, md, NULL))
+ return log_openssl_errors("Failed to initialize HMAC_CTX");
+#endif
+
+ for (size_t i = 0; i < n_data; i++)
+#if OPENSSL_VERSION_MAJOR >= 3
+ if (!EVP_MAC_update(ctx, data[i].iov_base, data[i].iov_len))
+#else
+ if (!HMAC_Update(ctx, data[i].iov_base, data[i].iov_len))
+#endif
+ return log_openssl_errors("Failed to update HMAC");
+
+ size_t digest_size;
+#if OPENSSL_VERSION_MAJOR >= 3
+ digest_size = EVP_MAC_CTX_get_mac_size(ctx);
+#else
+ digest_size = HMAC_size(ctx);
+#endif
+ if (digest_size == 0)
+ return log_openssl_errors("Failed to get HMAC digest size");
+
+ _cleanup_free_ void *buf = malloc(digest_size);
+ if (!buf)
+ return log_oom_debug();
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ size_t size;
+ if (!EVP_MAC_final(ctx, buf, &size, digest_size))
+#else
+ unsigned int size;
+ if (!HMAC_Final(ctx, buf, &size))
+#endif
+ return log_openssl_errors("Failed to finalize HMAC");
+
+ 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 a37c6e3a50..8079946ab5 100644
--- a/src/shared/openssl-util.h
+++ b/src/shared/openssl-util.h
@@ -40,11 +40,14 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL);
#if OPENSSL_VERSION_MAJOR >= 3
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MAC*, EVP_MAC_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MAC_CTX*, EVP_MAC_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD*, EVP_MD_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM*, OSSL_PARAM_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM_BLD*, OSSL_PARAM_BLD_free, NULL);
#else
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(HMAC_CTX*, HMAC_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL);
#endif
@@ -65,6 +68,12 @@ static inline int openssl_digest(const char *digest_alg, const void *buf, size_t
return openssl_digest_many(digest_alg, &IOVEC_MAKE((void*) buf, len), 1, ret_digest, ret_digest_size);
}
+int openssl_hmac_many(const char *digest_alg, const void *key, size_t key_size, const struct iovec data[], size_t n_data, void **ret_digest, size_t *ret_digest_size);
+
+static inline int openssl_hmac(const char *digest_alg, const void *key, size_t key_size, const void *buf, size_t len, void **ret_digest, size_t *ret_digest_size) {
+ return openssl_hmac_many(digest_alg, key, key_size, &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 35ac980d25..676438f76d 100644
--- a/src/test/test-openssl.c
+++ b/src/test/test-openssl.c
@@ -17,12 +17,10 @@ TEST(openssl_pkey_from_pem) {
assert_se(curve_id == NID_X9_62_prime256v1);
DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de");
- assert_se(x_len == expected_x_len);
- assert_se(memcmp(x, expected_x, x_len) == 0);
+ assert_se(memcmp_nn(x, x_len, expected_x, expected_x_len) == 0);
DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5");
- assert_se(y_len == expected_y_len);
- assert_se(memcmp(y, expected_y, y_len) == 0);
+ assert_se(memcmp_nn(y, y_len, expected_y, expected_y_len) == 0);
DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a");
_cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_rsa = NULL;
@@ -33,12 +31,10 @@ TEST(openssl_pkey_from_pem) {
assert_se(rsa_pkey_to_n_e(pkey_rsa, &n, &n_len, &e, &e_len) >= 0);
DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b");
- assert_se(n_len == expected_n_len);
- assert_se(memcmp(n, expected_n, n_len) == 0);
+ assert_se(memcmp_nn(n, n_len, expected_n, expected_n_len) == 0);
DEFINE_HEX_PTR(expected_e, "010001");
- assert_se(e_len == expected_e_len);
- assert_se(memcmp(e, expected_e, e_len) == 0);
+ assert_se(memcmp_nn(e, e_len, expected_e, expected_e_len) == 0);
}
TEST(rsa_pkey_n_e) {
@@ -214,4 +210,91 @@ TEST(digest_many) {
DEFINE_SHA256_TEST("8fe8b8d1899c44bfb82e1edc4ff92642db5b2cb25c4210ea06c3846c757525a8", i1, i1, i1, i4, i4, i4, i4, i3, i3, i2);
}
+static void verify_hmac(
+ const char *digest_alg,
+ const char *key,
+ const struct iovec *data,
+ size_t n_data,
+ const char *expect) {
+
+ DEFINE_HEX_PTR(k, key);
+ DEFINE_HEX_PTR(e, expect);
+ _cleanup_free_ void *digest = NULL;
+ size_t digest_size;
+
+ if (n_data == 0) {
+ assert_se(openssl_hmac(digest_alg, k, k_len, NULL, 0, &digest, &digest_size) == 0);
+ assert_se(memcmp_nn(e, e_len, digest, digest_size) == 0);
+ digest = mfree(digest);
+ } else if(n_data == 1) {
+ assert_se(openssl_hmac(digest_alg, k, k_len, data[0].iov_base, data[0].iov_len, &digest, &digest_size) == 0);
+ assert_se(memcmp_nn(e, e_len, digest, digest_size) == 0);
+ digest = mfree(digest);
+ }
+
+ assert_se(openssl_hmac_many(digest_alg, k, k_len, data, n_data, &digest, &digest_size) == 0);
+ assert_se(memcmp_nn(e, e_len, digest, digest_size) == 0);
+}
+
+#define _DEFINE_HMAC_TEST(uniq, alg, key, expect, ...) \
+ const struct iovec UNIQ_T(i, uniq)[] = { __VA_ARGS__ }; \
+ verify_hmac(alg, \
+ key, \
+ UNIQ_T(i, uniq), \
+ ELEMENTSOF(UNIQ_T(i, uniq)), \
+ expect);
+#define DEFINE_HMAC_TEST(alg, key, expect, ...) _DEFINE_HMAC_TEST(UNIQ, alg, key, expect, __VA_ARGS__)
+#define DEFINE_HMAC_SHA1_TEST(key, expect, ...) DEFINE_HMAC_TEST("SHA1", key, expect, __VA_ARGS__)
+#define DEFINE_HMAC_SHA256_TEST(key, expect, ...) DEFINE_HMAC_TEST("SHA256", key, expect, __VA_ARGS__)
+#define DEFINE_HMAC_SHA384_TEST(key, expect, ...) DEFINE_HMAC_TEST("SHA384", key, expect, __VA_ARGS__)
+#define DEFINE_HMAC_SHA512_TEST(key, expect, ...) DEFINE_HMAC_TEST("SHA512", key, expect, __VA_ARGS__)
+
+TEST(hmac_many) {
+ const char *key1 = "760eb6845073862c1914c6d188bf8214",
+ *key2 = "0628d1a5f83fce99779e12e2336d87046d42d74b755f00d9f72350668860fd00",
+ *key3 = "b61158912b76348c54f104629924be4178b8a9c9459c3a6e9daa1885445a61fccc1aa0f749c31f3ade4e227f64dd0e86a94b25c2e181f044af22d0a8c07074c3";
+ const struct iovec test = IOVEC_MAKE_STRING("test");
+
+ /* Empty digests */
+ DEFINE_HMAC_SHA1_TEST(key1, "EB9725FC9A99A652C3171E0863984AC42461F88B");
+ DEFINE_HMAC_SHA256_TEST(key1, "82A15D4DD5F583CF8F06D3E447DF0FDFF95A24E29229934B48BD0A5B4E0ADC85");
+ DEFINE_HMAC_SHA384_TEST(key1, "C60F15C4E18736750D91095ADA148C4179825A487CCA3AE047A2FB94F85A5587AB6AF57678AA79715FEF848129C108C3");
+ DEFINE_HMAC_SHA512_TEST(key1, "2B10DC9BFC0349400F8965482EA149C1C51C865BB7B16097623F41C14CF6C8A678724BFAE0CE842EED899C12CC17B5D8C4287F72BE788532FE7CF0BE2EBCD447");
+
+ DEFINE_HMAC_SHA1_TEST(key2, "F9AA74F129681E91807EB264EA6E1B5C5F9B4CFD");
+ DEFINE_HMAC_SHA256_TEST(key2, "B4ADEBF8B3044A5B0668B742C0A49B61D8380F89938C84794C92567F5A33CC7D");
+ DEFINE_HMAC_SHA384_TEST(key2, "E5EACAB7A13CF5BE60FA228D771E183CD6E57536BB9EAFC34A6BB52B1B1324BD6FB8A1713F91EC040790AE97F5672D53");
+ DEFINE_HMAC_SHA512_TEST(key2, "75A597D83A6270FC3204DE741E76DEFCF42D3E1812C71E41EEA8C0F23C07315822E83BE8B54705CB00FEF4CE1BAF80E3975414925C83BF3719CEBC27DD133F7D");
+
+ DEFINE_HMAC_SHA1_TEST(key3, "4B8EACB3C3935ACC8C58995C89F16020FC993569");
+ DEFINE_HMAC_SHA256_TEST(key3, "520E8C0323A1994D58EF5456611BCB6CD701399B24F8FBA0B5A3CD3186780E8E");
+ DEFINE_HMAC_SHA384_TEST(key3, "52ADAF691EFDC377B7349EAA45EE1BFAFA27CAC1FFE08B942C80426D1CA9F3464E3A71D611DA0B415435E82D6EE9F34A");
+ DEFINE_HMAC_SHA512_TEST(key3, "22D8C17BAF591E07CD2BD58A1B3D76D5904EC45C9099F0171A243F07611E25208A395833BC3F9BBD425636FD8D574BE1A1A367DCB6C40AD3C06E2B57E8FD2729");
+
+ /* test message */
+ DEFINE_HMAC_SHA1_TEST(key2, "DEE6313BE6391523D0B2B326890F13A65F3965B2", test);
+ DEFINE_HMAC_SHA256_TEST(key2, "496FF3E9DA52B2B490CD5EAE23457F8A33E61AB7B42F6E6374B7629CFBE1FCED", test);
+ DEFINE_HMAC_SHA384_TEST(key2, "F5223F750D671453CA6159C1354242DB13E0189CB79AC73E4964F623181B00C811A596F7CE3408DDE06B96C6D792F41E", test);
+ DEFINE_HMAC_SHA512_TEST(key2, "8755A8B0D85D89AFFE7A15702BBA0F835CDE454334EC952ED777A30035D6BD9407EA5DF8DCB89814C1DF7EE215022EA68D9D2BC4E4B299CD6F55CD60C269A706", 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_HMAC_SHA1_TEST(key2, "28C041532012BFF1B7C87B2A15A8C43EB8037D27", i1, i2, i3, i4);
+ DEFINE_HMAC_SHA256_TEST(key2, "F8A1FBDEE3CD383EA2B4940A3C8E72F443DB5B247016C9F84E2D2FEF3C5A0A23", i1, i2, i3, i4);
+ DEFINE_HMAC_SHA384_TEST(key2, "4D2AB0516F1F5C73BD0761407E0AF42361C1CAE761685FC65D1199598315EE3DCA4DB88E4D96FB06C2DA215A33FA9CE9", i1, i2, i3, i4);
+ DEFINE_HMAC_SHA512_TEST(key2, "E9BF8FC6FDE75FD5E4EF2DF399EE675C57B60C59A7B331F30535FDE68D8072185552E9A8BFA2008C52437F1BCC1472D16FBCF2A77C37339752938E42D2642150", i1, i2, i3, i4);
+
+ DEFINE_HMAC_SHA256_TEST(key3, "94D4E4B55368A533F6A7FDCC3B93E1F283BB1CA387BB5D14FAFF44A009EDF040", i1, i1, i1, i4);
+
+ DEFINE_HMAC_SHA256_TEST(key3, "5BE1F4D9C2AFAA2BB3F58FCE967BC7D3084BB8F512659875BDA634991145B0F0", i1, i1, i1, i4, i4, i4, i4, i3, i3, i2);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);