]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
openssl: add kdf_ss_derive() 29183/head
authorDan Streetman <ddstreet@ieee.org>
Fri, 7 Jul 2023 14:13:27 +0000 (10:13 -0400)
committerDan Streetman <ddstreet@ieee.org>
Thu, 28 Sep 2023 20:44:42 +0000 (16:44 -0400)
Add function to perform KDF-SS ("concat" KDF).

While Openssl allows a digest, HMAC, or KMAC for the auxiliary function H, this
currently only allows using a digest for H.

src/shared/openssl-util.c
src/shared/openssl-util.h
src/test/test-openssl.c

index 0015aa5b98cf50a413d3c4cc50d632e9638e60a1..9ea6284afb2f79a162d95034786d2b4c3bbe071f 100644 (file)
@@ -338,6 +338,72 @@ int openssl_cipher_many(
         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.
index a06ad5b388b94dc6887b33e566e69e83fa101615..3f2971375acdd3990dbf0f51483e1694a816e409 100644 (file)
@@ -84,6 +84,8 @@ static inline int openssl_cipher(const char *alg, size_t bits, const char *mode,
         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);
index 586c090c03057fcf0c583f61ca1298786d24e5c5..57872221f8b6828e70ed2904667324d8457ba120 100644 (file)
@@ -312,6 +312,43 @@ TEST(kdf_kb_hmac_derive) {
 #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,