summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-09-29 10:25:39 +0200
committerGitHub <noreply@github.com>2023-09-29 10:25:39 +0200
commit6ab6da0d1e46058b1e5a5dad74b33f618ff36fe9 (patch)
tree0986f75cc65c472a96a87590220a30f10163dfc8
parentautomount: fix unused value coverity warnings (diff)
parentopenssl: add kdf_ss_derive() (diff)
downloadsystemd-6ab6da0d1e46058b1e5a5dad74b33f618ff36fe9.tar.xz
systemd-6ab6da0d1e46058b1e5a5dad74b33f618ff36fe9.zip
Merge pull request #29183 from ddstreet/tpm2_openssl_functions
Add openssl functions for use by TPM2 sealing calculations
-rw-r--r--src/shared/openssl-util.c582
-rw-r--r--src/shared/openssl-util.h43
-rw-r--r--src/shared/tests.h2
-rw-r--r--src/test/test-cryptolib.c8
-rw-r--r--src/test/test-openssl.c394
5 files changed, 977 insertions, 52 deletions
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c
index 31a1a55df7..9ea6284afb 100644
--- a/src/shared/openssl-util.c
+++ b/src/shared/openssl-util.c
@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "fd-util.h"
-#include "openssl-util.h"
#include "alloc-util.h"
+#include "fd-util.h"
#include "hexdecoct.h"
+#include "openssl-util.h"
+#include "string-util.h"
#if HAVE_OPENSSL
/* For each error in the the Openssl thread error queue, log the provided message and the Openssl error
@@ -50,43 +51,445 @@ int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) {
return 0;
}
-int openssl_hash(const EVP_MD *alg,
- const void *msg,
- size_t msg_len,
- uint8_t *ret_hash,
- size_t *ret_hash_len) {
+/* Returns the number of bytes generated by the specified digest algorithm. This can be used only for
+ * fixed-size algorithms, e.g. md5, sha1, sha256, etc. Do not use this for variable-sized digest algorithms,
+ * e.g. shake128. Returns 0 on success, -EOPNOTSUPP if the algorithm is not supported, or < 0 for any other
+ * error. */
+int openssl_digest_size(const char *digest_alg, size_t *ret_digest_size) {
+ assert(digest_alg);
+ assert(ret_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);
+
+ size_t digest_size;
+#if OPENSSL_VERSION_MAJOR >= 3
+ digest_size = EVP_MD_get_size(md);
+#else
+ digest_size = EVP_MD_size(md);
+#endif
+ if (digest_size == 0)
+ return log_openssl_errors("Failed to get Digest size");
+
+ *ret_digest_size = 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) {
- _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL;
- unsigned len;
int r;
- ctx = EVP_MD_CTX_new();
+ 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)
- /* This function just calls OPENSSL_zalloc, so failure
- * here is almost certainly a failed allocation. */
- return -ENOMEM;
+ return log_openssl_errors("Failed to create new EVP_MD_CTX");
- /* The documentation claims EVP_DigestInit behaves just like
- * EVP_DigestInit_ex if passed NULL, except it also calls
- * EVP_MD_CTX_reset, which deinitializes the context. */
- r = EVP_DigestInit_ex(ctx, alg, NULL);
- if (r == 0)
- return -EIO;
+ if (!EVP_DigestInit_ex(ctx, md, NULL))
+ return log_openssl_errors("Failed to initializate EVP_MD_CTX");
- r = EVP_DigestUpdate(ctx, msg, msg_len);
- if (r == 0)
- return -EIO;
+ 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");
- r = EVP_DigestFinal_ex(ctx, ret_hash, &len);
- if (r == 0)
- return -EIO;
+ size_t digest_size;
+ r = openssl_digest_size(digest_alg, &digest_size);
+ if (r < 0)
+ return r;
- if (ret_hash_len)
- *ret_hash_len = len;
+ _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;
}
+/* 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;
+}
+
+/* Symmetric Cipher encryption using the alg-bits-mode cipher, e.g. AES-128-CFB. The key is required and must
+ * be at least the minimum required key length for the cipher. The IV is optional but, if provided, it must
+ * be at least the minimum iv length for the cipher. If no IV is provided and the cipher requires one, a
+ * buffer of zeroes is used. Returns 0 on success, -EOPNOTSUPP if the cipher algorithm is not supported, or <
+ * 0 on any other error. */
+int openssl_cipher_many(
+ const char *alg,
+ size_t bits,
+ const char *mode,
+ const void *key,
+ size_t key_size,
+ const void *iv,
+ size_t iv_size,
+ const struct iovec data[],
+ size_t n_data,
+ void **ret,
+ size_t *ret_size) {
+
+ assert(alg);
+ assert(bits > 0);
+ assert(mode);
+ assert(key);
+ assert(iv || iv_size == 0);
+ assert(data || n_data == 0);
+ assert(ret);
+ assert(ret_size);
+
+ _cleanup_free_ char *cipher_alg = NULL;
+ if (asprintf(&cipher_alg, "%s-%zu-%s", alg, bits, mode) < 0)
+ return log_oom_debug();
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_(EVP_CIPHER_freep) EVP_CIPHER *cipher = EVP_CIPHER_fetch(NULL, cipher_alg, NULL);
+#else
+ const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_alg);
+#endif
+ if (!cipher)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Cipher algorithm '%s' not supported.", cipher_alg);
+
+ _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_CIPHER_CTX");
+
+ /* Verify enough key data was provided. */
+ int cipher_key_length = EVP_CIPHER_key_length(cipher);
+ assert(cipher_key_length >= 0);
+ if ((size_t) cipher_key_length > key_size)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough key bytes provided, require %d", cipher_key_length);
+
+ /* Verify enough IV data was provided or, if no IV was provided, use a zeroed buffer for IV data. */
+ int cipher_iv_length = EVP_CIPHER_iv_length(cipher);
+ assert(cipher_iv_length >= 0);
+ _cleanup_free_ void *zero_iv = NULL;
+ if (iv_size == 0) {
+ zero_iv = malloc0(cipher_iv_length);
+ if (!zero_iv)
+ return log_oom_debug();
+
+ iv = zero_iv;
+ iv_size = (size_t) cipher_iv_length;
+ }
+ if ((size_t) cipher_iv_length > iv_size)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough IV bytes provided, require %d", cipher_iv_length);
+
+ if (!EVP_EncryptInit(ctx, cipher, key, iv))
+ return log_openssl_errors("Failed to initialize EVP_CIPHER_CTX.");
+
+ int cipher_block_size = EVP_CIPHER_CTX_block_size(ctx);
+ assert(cipher_block_size > 0);
+
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t size = 0;
+
+ for (size_t i = 0; i < n_data; i++) {
+ /* Cipher may produce (up to) input length + cipher block size of output. */
+ if (!GREEDY_REALLOC(buf, size + data[i].iov_len + cipher_block_size))
+ return log_oom_debug();
+
+ int update_size;
+ if (!EVP_EncryptUpdate(ctx, &buf[size], &update_size, data[i].iov_base, data[i].iov_len))
+ return log_openssl_errors("Failed to update Cipher.");
+
+ size += update_size;
+ }
+
+ if (!GREEDY_REALLOC(buf, size + cipher_block_size))
+ return log_oom_debug();
+
+ int final_size;
+ if (!EVP_EncryptFinal_ex(ctx, &buf[size], &final_size))
+ return log_openssl_errors("Failed to finalize Cipher.");
+
+ *ret = TAKE_PTR(buf);
+ *ret_size = size + final_size;
+
+ return 0;
+}
+
+/* Perform Single-Step (aka "Concat") KDF. Currently, this only supports using the digest for the auxiliary
+ * function. The derive_size parameter specifies how many bytes are derived.
+ *
+ * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-SS.html */
+int kdf_ss_derive(
+ const char *digest,
+ const void *key,
+ size_t key_size,
+ const void *salt,
+ size_t salt_size,
+ const void *info,
+ size_t info_size,
+ size_t derive_size,
+ void **ret) {
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ assert(digest);
+ assert(key);
+ assert(derive_size > 0);
+ assert(ret);
+
+ _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL);
+ if (!kdf)
+ return log_openssl_errors("Failed to create new EVP_KDF");
+
+ _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_KDF_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");
+
+ _cleanup_free_ void *buf = malloc(derive_size);
+ if (!buf)
+ return log_oom_debug();
+
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0))
+ return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_DIGEST");
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size))
+ return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_KEY");
+
+ if (salt)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size))
+ return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_SALT");
+
+ if (info)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size))
+ return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_INFO");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_openssl_errors("Failed to build KDF-SS OSSL_PARAM");
+
+ if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0)
+ return log_openssl_errors("Openssl KDF-SS derive failed");
+
+ *ret = TAKE_PTR(buf);
+
+ return 0;
+#else
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-SS requires openssl >= 3.");
+#endif
+}
+
+/* Perform Key-Based HMAC KDF. The mode must be "COUNTER" or "FEEDBACK". The parameter naming is from the
+ * Openssl api, and maps to SP800-108 naming as "...key, salt, info, and seed correspond to KI, Label,
+ * Context, and IV (respectively)...". The derive_size parameter specifies how many bytes are derived.
+ *
+ * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-KB.html */
+int kdf_kb_hmac_derive(
+ const char *mode,
+ const char *digest,
+ const void *key,
+ size_t key_size,
+ const void *salt,
+ size_t salt_size,
+ const void *info,
+ size_t info_size,
+ const void *seed,
+ size_t seed_size,
+ size_t derive_size,
+ void **ret) {
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ assert(mode);
+ assert(strcaseeq(mode, "COUNTER") || strcaseeq(mode, "FEEDBACK"));
+ assert(digest);
+ assert(key || key_size == 0);
+ assert(salt || salt_size == 0);
+ assert(info || info_size == 0);
+ assert(seed || seed_size == 0);
+ assert(derive_size > 0);
+ assert(ret);
+
+ _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
+ if (!kdf)
+ return log_openssl_errors("Failed to create new EVP_KDF");
+
+ _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_KDF_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_KDF_PARAM_MAC, (char*) "HMAC", 0))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MAC");
+
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_MODE, (char*) mode, 0))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MODE");
+
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_DIGEST");
+
+ if (key)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_KEY");
+
+ if (salt)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SALT");
+
+ if (info)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_INFO");
+
+ if (seed)
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SEED, (char*) seed, seed_size))
+ return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SEED");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_openssl_errors("Failed to build KDF-KB OSSL_PARAM");
+
+ _cleanup_free_ void *buf = malloc(derive_size);
+ if (!buf)
+ return log_oom_debug();
+
+ if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0)
+ return log_openssl_errors("Openssl KDF-KB derive failed");
+
+ *ret = TAKE_PTR(buf);
+
+ return 0;
+#else
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-KB requires openssl >= 3.");
+#endif
+}
+
int rsa_encrypt_bytes(
EVP_PKEY *pkey,
const void *decrypted_key,
@@ -124,6 +527,73 @@ int rsa_encrypt_bytes(
return 0;
}
+/* Encrypt the key data using RSA-OAEP with the provided label and specified digest algorithm. Returns 0 on
+ * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */
+int rsa_oaep_encrypt_bytes(
+ const EVP_PKEY *pkey,
+ const char *digest_alg,
+ const char *label,
+ const void *decrypted_key,
+ size_t decrypted_key_size,
+ void **ret_encrypt_key,
+ size_t *ret_encrypt_key_size) {
+
+ assert(pkey);
+ assert(digest_alg);
+ assert(label);
+ assert(decrypted_key);
+ assert(decrypted_key_size > 0);
+ assert(ret_encrypt_key);
+ assert(ret_encrypt_key_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_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL);
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
+
+ if (EVP_PKEY_encrypt_init(ctx) <= 0)
+ return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+ return log_openssl_errors("Failed to configure RSA-OAEP padding");
+
+ if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0)
+ return log_openssl_errors("Failed to configure RSA-OAEP MD");
+
+ _cleanup_free_ char *duplabel = strdup(label);
+ if (!duplabel)
+ return log_oom_debug();
+
+ if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, duplabel, strlen(duplabel) + 1) <= 0)
+ return log_openssl_errors("Failed to configure RSA-OAEP label");
+ /* ctx owns this now, don't free */
+ TAKE_PTR(duplabel);
+
+ size_t size = 0;
+ if (EVP_PKEY_encrypt(ctx, NULL, &size, decrypted_key, decrypted_key_size) <= 0)
+ return log_openssl_errors("Failed to determine RSA-OAEP encrypted key size");
+
+ _cleanup_free_ void *buf = malloc(size);
+ if (!buf)
+ return log_oom_debug();
+
+ if (EVP_PKEY_encrypt(ctx, buf, &size, decrypted_key, decrypted_key_size) <= 0)
+ return log_openssl_errors("Failed to RSA-OAEP encrypt");
+
+ *ret_encrypt_key = TAKE_PTR(buf);
+ *ret_encrypt_key_size = size;
+
+ return 0;
+}
+
int rsa_pkey_to_suitable_key_size(
EVP_PKEY *pkey,
size_t *ret_suitable_key_size) {
@@ -490,6 +960,48 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret) {
return 0;
}
+/* Perform ECDH to derive an ECC shared secret between the provided private key and public peer key. For two
+ * keys, this will result in the same shared secret in either direction; ECDH using Alice's private key and
+ * Bob's public (peer) key will result in the same shared secret as ECDH using Bob's private key and Alice's
+ * public (peer) key. On success, this returns 0 and provides the shared secret; otherwise this returns an
+ * error. */
+int ecc_ecdh(const EVP_PKEY *private_pkey,
+ const EVP_PKEY *peer_pkey,
+ void **ret_shared_secret,
+ size_t *ret_shared_secret_size) {
+
+ assert(private_pkey);
+ assert(peer_pkey);
+ assert(ret_shared_secret);
+ assert(ret_shared_secret_size);
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) private_pkey, NULL);
+ if (!ctx)
+ return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
+
+ if (EVP_PKEY_derive_init(ctx) <= 0)
+ return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
+
+ if (EVP_PKEY_derive_set_peer(ctx, (EVP_PKEY*) peer_pkey) <= 0)
+ return log_openssl_errors("Failed to set ECC derive peer");
+
+ size_t shared_secret_size;
+ if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
+ return log_openssl_errors("Failed to get ECC shared secret size");
+
+ _cleanup_free_ void *shared_secret = malloc(shared_secret_size);
+ if (!shared_secret)
+ return log_oom_debug();
+
+ if (EVP_PKEY_derive(ctx, (unsigned char*) shared_secret, &shared_secret_size) <= 0)
+ return log_openssl_errors("Failed to derive ECC shared secret");
+
+ *ret_shared_secret = TAKE_PTR(shared_secret);
+ *ret_shared_secret_size = shared_secret_size;
+
+ return 0;
+}
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) {
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL;
_cleanup_free_ void *d = NULL, *h = NULL;
@@ -592,18 +1104,19 @@ int digest_and_sign(
int string_hashsum(
const char *s,
size_t len,
- const EVP_MD *md_algorithm,
+ const char *md_algorithm,
char **ret) {
- uint8_t hash[EVP_MAX_MD_SIZE];
+ _cleanup_free_ void *hash = NULL;
size_t hash_size;
- char *enc;
+ _cleanup_free_ char *enc;
int r;
- hash_size = EVP_MD_size(md_algorithm);
- assert(hash_size > 0);
+ assert(s || len == 0);
+ assert(md_algorithm);
+ assert(ret);
- r = openssl_hash(md_algorithm, s, len, hash, NULL);
+ r = openssl_digest(md_algorithm, s, len, &hash, &hash_size);
if (r < 0)
return r;
@@ -611,9 +1124,8 @@ int string_hashsum(
if (!enc)
return -ENOMEM;
- *ret = enc;
+ *ret = TAKE_PTR(enc);
return 0;
-
}
# endif
#endif
diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h
index 4ea82b5f27..3f2971375a 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"
@@ -22,6 +23,7 @@
# endif
# if OPENSSL_VERSION_MAJOR >= 3
# include <openssl/core_names.h>
+# include <openssl/kdf.h>
# include <openssl/param_build.h>
# endif
@@ -39,10 +41,17 @@ 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_CIPHER*, EVP_CIPHER_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_KDF*, EVP_KDF_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_KDF_CTX*, EVP_KDF_CTX_free, NULL);
+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
@@ -55,10 +64,34 @@ static inline void sk_X509_free_allp(STACK_OF(X509) **sk) {
int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret);
-int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, uint8_t *ret_hash, size_t *ret_hash_len);
+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 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 openssl_cipher_many(const char *alg, size_t bits, const char *mode, const void *key, size_t key_size, const void *iv, size_t iv_size, const struct iovec data[], size_t n_data, void **ret, size_t *ret_size);
+
+static inline int openssl_cipher(const char *alg, size_t bits, const char *mode, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *buf, size_t len, void **ret, size_t *ret_size) {
+ return openssl_cipher_many(alg, bits, mode, key, key_size, iv, iv_size, &IOVEC_MAKE((void*) buf, len), 1, ret, ret_size);
+}
+
+int kdf_ss_derive(const char *digest, const void *key, size_t key_size, const void *salt, size_t salt_size, const void *info, size_t info_size, size_t derive_size, void **ret);
+
+int kdf_kb_hmac_derive(const char *mode, const char *digest, const void *key, size_t key_size, const void *salt, size_t salt_size, const void *info, size_t info_size, const void *seed, size_t seed_size, size_t derive_size, void **ret);
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_oaep_encrypt_bytes(const EVP_PKEY *pkey, const char *digest_alg, const char *label, 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);
int rsa_pkey_new(size_t bits, EVP_PKEY **ret);
@@ -73,6 +106,8 @@ int ecc_pkey_to_curve_x_y(const EVP_PKEY *pkey, int *ret_curve_id, void **ret_x,
int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
+int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
@@ -120,13 +155,13 @@ typedef gcry_md_hd_t hash_context_t;
#endif
#if PREFER_OPENSSL
-int string_hashsum(const char *s, size_t len, hash_algorithm_t md_algorithm, char **ret);
+int string_hashsum(const char *s, size_t len, const char *md_algorithm, char **ret);
static inline int string_hashsum_sha224(const char *s, size_t len, char **ret) {
- return string_hashsum(s, len, EVP_sha224(), ret);
+ return string_hashsum(s, len, "SHA224", ret);
}
static inline int string_hashsum_sha256(const char *s, size_t len, char **ret) {
- return string_hashsum(s, len, EVP_sha256(), ret);
+ return string_hashsum(s, len, "SHA256", ret);
}
#endif
diff --git a/src/shared/tests.h b/src/shared/tests.h
index 27467fabb6..1baad3e51b 100644
--- a/src/shared/tests.h
+++ b/src/shared/tests.h
@@ -43,7 +43,7 @@ bool can_memlock(void);
#define DEFINE_HEX_PTR(name, hex) \
_cleanup_free_ void *name = NULL; \
size_t name##_len = 0; \
- assert_se(unhexmem(hex, strlen(hex), &name, &name##_len) >= 0);
+ assert_se(unhexmem(hex, strlen_ptr(hex), &name, &name##_len) >= 0);
#define TEST_REQ_RUNNING_SYSTEMD(x) \
if (sd_booted() > 0) { \
diff --git a/src/test/test-cryptolib.c b/src/test/test-cryptolib.c
index ef39bda653..6202a5d6d4 100644
--- a/src/test/test-cryptolib.c
+++ b/src/test/test-cryptolib.c
@@ -11,25 +11,25 @@ TEST(string_hashsum) {
_cleanup_free_ char *out1 = NULL, *out2 = NULL, *out3 = NULL, *out4 = NULL;
assert_se(string_hashsum("asdf", 4,
- OPENSSL_OR_GCRYPT(EVP_sha224(), GCRY_MD_SHA224),
+ OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
&out1) == 0);
/* echo -n 'asdf' | sha224sum - */
assert_se(streq(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a"));
assert_se(string_hashsum("asdf", 4,
- OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256),
+ OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
&out2) == 0);
/* echo -n 'asdf' | sha256sum - */
assert_se(streq(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"));
assert_se(string_hashsum("", 0,
- OPENSSL_OR_GCRYPT(EVP_sha224(), GCRY_MD_SHA224),
+ OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
&out3) == 0);
/* echo -n '' | sha224sum - */
assert_se(streq(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"));
assert_se(string_hashsum("", 0,
- OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256),
+ OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
&out4) == 0);
/* echo -n '' | sha256sum - */
assert_se(streq(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
diff --git a/src/test/test-openssl.c b/src/test/test-openssl.c
index c46ecdcda8..57872221f8 100644
--- a/src/test/test-openssl.c
+++ b/src/test/test-openssl.c
@@ -16,12 +16,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;
@@ -32,12 +30,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) {
@@ -102,4 +98,386 @@ TEST(invalid) {
assert_se(pkey == NULL);
}
+static const struct {
+ const char *alg;
+ size_t size;
+} digest_size_table[] = {
+ /* SHA1 "family" */
+ { "sha1", 20, },
+#if OPENSSL_VERSION_MAJOR >= 3
+ { "sha-1", 20, },
+#endif
+ /* SHA2 family */
+ { "sha224", 28, },
+ { "sha256", 32, },
+ { "sha384", 48, },
+ { "sha512", 64, },
+#if OPENSSL_VERSION_MAJOR >= 3
+ { "sha-224", 28, },
+ { "sha2-224", 28, },
+ { "sha-256", 32, },
+ { "sha2-256", 32, },
+ { "sha-384", 48, },
+ { "sha2-384", 48, },
+ { "sha-512", 64, },
+ { "sha2-512", 64, },
+#endif
+ /* SHA3 family */
+ { "sha3-224", 28, },
+ { "sha3-256", 32, },
+ { "sha3-384", 48, },
+ { "sha3-512", 64, },
+ /* SM3 family */
+ { "sm3", 32, },
+ /* MD5 family */
+ { "md5", 16, },
+};
+
+TEST(digest_size) {
+ size_t size;
+
+ FOREACH_ARRAY(t, digest_size_table, ELEMENTSOF(digest_size_table)) {
+ assert(openssl_digest_size(t->alg, &size) >= 0);
+ assert_se(size == t->size);
+
+ _cleanup_free_ char *uppercase_alg = strdup(t->alg);
+ assert_se(uppercase_alg);
+ assert_se(openssl_digest_size(ascii_strupper(uppercase_alg), &size) >= 0);
+ assert_se(size == t->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);
+}
+
+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);
+}
+
+TEST(kdf_kb_hmac_derive) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_free_ void *derived_key = NULL;
+
+ DEFINE_HEX_PTR(key, "d7ac57124f28371eacaec475b74869d26b4cd64586412a607ce0a9e0c63d468c");
+ const char *salt = "salty chocolate";
+ DEFINE_HEX_PTR(info, "6721a2012d9554f5a64593ed3eaa8fe15e6a21e1c8c8736ea4d234eb55b9e31a");
+ DEFINE_HEX_PTR(expected_derived_key, "A9DA9CEEB9578DBE7DD2862F82898B086E85FF2D10C4E8EC5BD99D0D7F003A2DE1574EB4BD789C03EF5235259BCB3A009DA303EA4DB4CA6BF507DB7C5A063279");
+
+ assert_se(kdf_kb_hmac_derive("COUNTER", "SHA256", key, key_len, salt, strlen(salt), info, info_len, /* seed= */ NULL, /* seed_size= */ 0, 64, &derived_key) >= 0);
+ assert_se(memcmp_nn(derived_key, 64, expected_derived_key, expected_derived_key_len) == 0);
+#else
+ log_tests_skipped("KDF-KB requires Openssl >= 3");
+#endif
+}
+
+#if OPENSSL_VERSION_MAJOR >= 3
+static void check_ss_derive(const char *hex_key, const char *hex_salt, const char *hex_info, const char *hex_expected) {
+ DEFINE_HEX_PTR(key, hex_key);
+ DEFINE_HEX_PTR(salt, hex_salt);
+ DEFINE_HEX_PTR(info, hex_info);
+ DEFINE_HEX_PTR(expected, hex_expected);
+
+ _cleanup_free_ void *derived_key = NULL;
+ assert_se(kdf_ss_derive("SHA256", key, key_len, salt, salt_len, info, info_len, expected_len, &derived_key) >= 0);
+ assert_se(memcmp_nn(derived_key, expected_len, expected, expected_len) == 0);
+}
+#endif
+
+TEST(kdf_ss_derive) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ check_ss_derive(
+ "01166ad6b05d1fad8cdb50d1902170e9",
+ "feea805789dc8d0b57da5d4d61886b1a",
+ "af4cb6d1d0a996e21e3788584165e2ae",
+ "46CECAB4544E11EF986641BA6F843FAFFD111D3974C34E3B9592311E8579C6BD");
+
+ check_ss_derive(
+ "d1c39e37260d79d6e766f1d1412c4b61fc0801db469b97c897b0fbcaebea5178",
+ "b75e3b65d1bb845dee581c7e14cfebc6e882946e90273b77ebe289faaf7de248",
+ "ed25a0043d6c1eb28296da1f9ab138dafee18f4c937bfc43601d4ee6e7634199",
+ "30EB1A1E9DEA7DE4DDB8F3FDF50A01E3");
+ /* Same inputs as above, but derive more bytes */
+ check_ss_derive(
+ "d1c39e37260d79d6e766f1d1412c4b61fc0801db469b97c897b0fbcaebea5178",
+ "b75e3b65d1bb845dee581c7e14cfebc6e882946e90273b77ebe289faaf7de248",
+ "ed25a0043d6c1eb28296da1f9ab138dafee18f4c937bfc43601d4ee6e7634199",
+ "30EB1A1E9DEA7DE4DDB8F3FDF50A01E30581D606C1228D98AFF691DF743AC2EE9D99EFD2AE1946C079AA18C9524877FA65D5065F0DAED058AB3416AF80EB2B73");
+#else
+ log_tests_skipped("KDF-SS requires Openssl >= 3");
+#endif
+}
+
+static void check_cipher(
+ const char *alg,
+ size_t bits,
+ const char *mode,
+ const char *hex_key,
+ const char *hex_iv,
+ const struct iovec data[],
+ size_t n_data,
+ const char *hex_expected) {
+
+ _cleanup_free_ void *enc_buf = NULL;
+ size_t enc_buf_len;
+
+ DEFINE_HEX_PTR(key, hex_key);
+ DEFINE_HEX_PTR(iv, hex_iv);
+ DEFINE_HEX_PTR(expected, hex_expected);
+
+ if (n_data == 0) {
+ assert_se(openssl_cipher(alg, bits, mode, key, key_len, iv, iv_len, NULL, 0, &enc_buf, &enc_buf_len) >= 0);
+ assert_se(memcmp_nn(enc_buf, enc_buf_len, expected, expected_len) == 0);
+ enc_buf = mfree(enc_buf);
+ } else if (n_data == 1) {
+ assert_se(openssl_cipher(alg, bits, mode, key, key_len, iv, iv_len, data[0].iov_base, data[0].iov_len, &enc_buf, &enc_buf_len) >= 0);
+ assert_se(memcmp_nn(enc_buf, enc_buf_len, expected, expected_len) == 0);
+ enc_buf = mfree(enc_buf);
+ }
+
+ assert_se(openssl_cipher_many(alg, bits, mode, key, key_len, iv, iv_len, data, n_data, &enc_buf, &enc_buf_len) >= 0);
+ assert_se(memcmp_nn(enc_buf, enc_buf_len, expected, expected_len) == 0);
+}
+
+TEST(openssl_cipher) {
+ struct iovec data[] = {
+ IOVEC_MAKE_STRING("my"),
+ IOVEC_MAKE_STRING(" "),
+ IOVEC_MAKE_STRING("secret"),
+ IOVEC_MAKE_STRING(" "),
+ IOVEC_MAKE_STRING("text"),
+ IOVEC_MAKE_STRING("!"),
+ };
+
+ check_cipher(
+ "aes", 256, "cfb",
+ "32c62bbaeb0decc5c874b8e0148f86475b5bb10a36f7078a75a6f11704c2f06a",
+ /* hex_iv= */ NULL,
+ data, ELEMENTSOF(data),
+ "bd4a46f8762bf4bef4430514aaec5e");
+
+ check_cipher(
+ "aes", 256, "cfb",
+ "32c62bbaeb0decc5c874b8e0148f86475b5bb10a36f7078a75a6f11704c2f06a",
+ "00000000000000000000000000000000",
+ data, ELEMENTSOF(data),
+ "bd4a46f8762bf4bef4430514aaec5e");
+
+ check_cipher(
+ "aes", 256, "cfb",
+ "32c62bbaeb0decc5c874b8e0148f86475b5bb10a36f7078a75a6f11704c2f06a",
+ "9088fd5c4ad9b9419eced86283021a59",
+ data, ELEMENTSOF(data),
+ "6dfbf8dc972f9a462ad7427a1fa41a");
+
+ check_cipher(
+ "aes", 256, "cfb",
+ "32c62bbaeb0decc5c874b8e0148f86475b5bb10a36f7078a75a6f11704c2f06a",
+ /* hex_iv= */ NULL,
+ &data[2], 1,
+ "a35605f9763c");
+
+ check_cipher(
+ "aes", 256, "cfb",
+ "32c62bbaeb0decc5c874b8e0148f86475b5bb10a36f7078a75a6f11704c2f06a",
+ /* hex_iv= */ NULL,
+ /* data= */ NULL, /* n_data= */ 0,
+ /* expected= */ NULL);
+
+ check_cipher(
+ "aes", 128, "cfb",
+ "b8fe4b89f6f25dd58cadceb68c99d508",
+ /* hex_iv= */ NULL,
+ data, ELEMENTSOF(data),
+ "9c0fe3abb904ab419d950ae00c93a1");
+
+ check_cipher(
+ "aes", 128, "cfb",
+ "b8fe4b89f6f25dd58cadceb68c99d508",
+ "00000000000000000000000000000000",
+ data, ELEMENTSOF(data),
+ "9c0fe3abb904ab419d950ae00c93a1");
+
+ check_cipher(
+ "aes", 128, "cfb",
+ "b8fe4b89f6f25dd58cadceb68c99d508",
+ "9088fd5c4ad9b9419eced86283021a59",
+ data, ELEMENTSOF(data),
+ "e765617aceb1326f5309008c14f4e1");
+
+ check_cipher(
+ "aes", 128, "cfb",
+ "b8fe4b89f6f25dd58cadceb68c99d508",
+ /* hex_iv= */ NULL,
+ /* data= */ NULL, /* n_data= */ 0,
+ /* expected= */ NULL);
+
+ check_cipher(
+ "aes", 128, "cfb",
+ "b8fe4b89f6f25dd58cadceb68c99d508",
+ "00000000000000000000000000000000",
+ /* data= */ NULL, /* n_data= */ 0,
+ /* expected= */ NULL);
+}
+
+TEST(ecc_ecdh) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkeyA = NULL, *pkeyB = NULL, *pkeyC = NULL;
+ _cleanup_free_ void *secretAB = NULL, *secretBA = NULL, *secretAC = NULL, *secretCA = NULL;
+ size_t secretAB_size, secretBA_size, secretAC_size, secretCA_size;
+
+ assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyA) >= 0);
+ assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyB) >= 0);
+ assert_se(ecc_pkey_new(NID_X9_62_prime256v1, &pkeyC) >= 0);
+
+ assert_se(ecc_ecdh(pkeyA, pkeyB, &secretAB, &secretAB_size) >= 0);
+ assert_se(ecc_ecdh(pkeyB, pkeyA, &secretBA, &secretBA_size) >= 0);
+ assert_se(ecc_ecdh(pkeyA, pkeyC, &secretAC, &secretAC_size) >= 0);
+ assert_se(ecc_ecdh(pkeyC, pkeyA, &secretCA, &secretCA_size) >= 0);
+
+ assert_se(memcmp_nn(secretAB, secretAB_size, secretBA, secretBA_size) == 0);
+ assert_se(memcmp_nn(secretAC, secretAC_size, secretCA, secretCA_size) == 0);
+ assert_se(memcmp_nn(secretAC, secretAC_size, secretAB, secretAB_size) != 0);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);