From 60ca7c3cee1ba098ac2c9fd5bbee2df4567717d5 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 12 Sep 2024 10:01:37 -0400 Subject: [PATCH] EVP_PKEY_CTX_ctrl_str w/ HKDF --- crypto/fipsmodule/evp/evp_ctx_test.cc | 103 ++++++++++++++++++++++++++ crypto/fipsmodule/evp/p_hkdf.c | 81 +++++++++++++++++++- 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/crypto/fipsmodule/evp/evp_ctx_test.cc b/crypto/fipsmodule/evp/evp_ctx_test.cc index 0c0b078a30..756d03261b 100644 --- a/crypto/fipsmodule/evp/evp_ctx_test.cc +++ b/crypto/fipsmodule/evp/evp_ctx_test.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "../../internal.h" @@ -236,3 +237,105 @@ TEST_F(EvpPkeyCtxCtrlStrTest, DhPad) { // There is no function to retrieve the DH pad value. } + +static const char *hkdf_hexsalt = "000102030405060708090a0b0c"; +static const char *hkdf_hexinfo = "f0f1f2f3f4f5f6f7f8f9"; +static const char *hkdf_hexkey = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; +static const char *hkdf_hex_expected_okm = + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5" + "b887185865"; +static const char *hkdf_hex_expected_prk = + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"; + +TEST_F(EvpPkeyCtxCtrlStrTest, HkdfHex) { + // Test Cases from RFC 5869. + + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_derive_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "mode", "EXTRACT_AND_EXPAND"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "md", "SHA256"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexsalt", hkdf_hexsalt), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexinfo", hkdf_hexinfo), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexkey", hkdf_hexkey), 1); + + size_t okm_len; + bssl::UniquePtr expected_okm( + OPENSSL_hexstr2buf(hkdf_hex_expected_okm, &okm_len)); + ASSERT_TRUE(expected_okm); + + bssl::UniquePtr actual_okm( + static_cast(OPENSSL_zalloc(okm_len))); + ASSERT_TRUE(actual_okm); + + ASSERT_TRUE(EVP_PKEY_derive(ctx.get(), actual_okm.get(), &okm_len)); + + ASSERT_EQ(OPENSSL_memcmp(actual_okm.get(), expected_okm.get(), okm_len), 0); +} + +TEST_F(EvpPkeyCtxCtrlStrTest, HkdfRaw) { + // Test Cases from RFC 5869. + + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_derive_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "mode", "EXTRACT_AND_EXPAND"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "md", "SHA256"), 1); + + // The salt in the KAT contains a 0-byte so "salt" cannot be used. + ASSERT_EQ( + EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexsalt", "000102030405060708090a0b0c"), + 1); + + size_t _len; + bssl::UniquePtr info(OPENSSL_hexstr2buf(hkdf_hexinfo, &_len)); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "info", + reinterpret_cast(info.get())), + 1); + bssl::UniquePtr key(OPENSSL_hexstr2buf(hkdf_hexkey, &_len)); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "key", + reinterpret_cast(key.get())), + 1); + + size_t okm_len; + bssl::UniquePtr expected_okm( + OPENSSL_hexstr2buf(hkdf_hex_expected_okm, &okm_len)); + ASSERT_TRUE(expected_okm); + + bssl::UniquePtr actual_okm( + static_cast(OPENSSL_zalloc(okm_len))); + ASSERT_TRUE(actual_okm); + + ASSERT_TRUE(EVP_PKEY_derive(ctx.get(), actual_okm.get(), &okm_len)); + + ASSERT_EQ(OPENSSL_memcmp(actual_okm.get(), expected_okm.get(), okm_len), 0); +} + +TEST_F(EvpPkeyCtxCtrlStrTest, HkdfExtract) { + // Test Cases from RFC 5869. + + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_derive_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "mode", "EXTRACT_ONLY"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "md", "SHA256"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexsalt", hkdf_hexsalt), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexinfo", hkdf_hexinfo), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexkey", hkdf_hexkey), 1); + + size_t prk_len; + bssl::UniquePtr expected_prk( + OPENSSL_hexstr2buf(hkdf_hex_expected_prk, &prk_len)); + ASSERT_TRUE(expected_prk); + + bssl::UniquePtr actual_prk( + static_cast(OPENSSL_zalloc(prk_len))); + ASSERT_TRUE(actual_prk); + + ASSERT_TRUE(EVP_PKEY_derive(ctx.get(), actual_prk.get(), &prk_len)); + + ASSERT_EQ(OPENSSL_memcmp(actual_prk.get(), expected_prk.get(), prk_len), 0); +} diff --git a/crypto/fipsmodule/evp/p_hkdf.c b/crypto/fipsmodule/evp/p_hkdf.c index 04316a88bb..d4017ba22b 100644 --- a/crypto/fipsmodule/evp/p_hkdf.c +++ b/crypto/fipsmodule/evp/p_hkdf.c @@ -179,6 +179,85 @@ static int pkey_hkdf_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) { } } +static int pkey_hkdf_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, + const char *value) { + if (strcmp(type, "mode") == 0) { + int mode; + + if (strcmp(value, "EXTRACT_AND_EXPAND") == 0) { + mode = EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND; + } else if (strcmp(value, "EXTRACT_ONLY") == 0) { + mode = EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY; + } else if (strcmp(value, "EXPAND_ONLY") == 0) { + mode = EVP_PKEY_HKDEF_MODE_EXPAND_ONLY; + } else { + return 0; + } + + return EVP_PKEY_CTX_hkdf_mode(ctx, mode); + } + + if (strcmp(type, "md") == 0) { + OPENSSL_BEGIN_ALLOW_DEPRECATED + return EVP_PKEY_CTX_md(ctx, EVP_PKEY_OP_DERIVE, EVP_PKEY_CTRL_HKDF_MD, + value); + OPENSSL_END_ALLOW_DEPRECATED + } + + if (strcmp(type, "salt") == 0) { + // What if the salt contains a 0-byte? + const size_t saltlen = OPENSSL_strnlen(value, INT16_MAX); + return EVP_PKEY_CTX_set1_hkdf_salt(ctx, (const uint8_t *)value, saltlen); + } + + if (strcmp(type, "hexsalt") == 0) { + size_t hex_saltlen = 0; + uint8_t *salt = OPENSSL_hexstr2buf(value, &hex_saltlen); + if (salt == NULL) { + return -2; + } + int result = EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, hex_saltlen); + OPENSSL_free(salt); + return result; + } + + if (strcmp(type, "key") == 0) { + // What if the key contains a 0-byte? + const size_t keylen = OPENSSL_strnlen(value, INT16_MAX); + return EVP_PKEY_CTX_set1_hkdf_key(ctx, (const uint8_t *)value, keylen); + } + + if (strcmp(type, "hexkey") == 0) { + size_t hex_keylen = 0; + uint8_t *key = OPENSSL_hexstr2buf(value, &hex_keylen); + if (key == NULL) { + return -2; + } + int result = EVP_PKEY_CTX_set1_hkdf_key(ctx, key, hex_keylen); + OPENSSL_free(key); + return result; + } + + if (strcmp(type, "info") == 0) { + // What if info contains a 0-byte? + const size_t infolen = OPENSSL_strnlen(value, INT16_MAX); + return EVP_PKEY_CTX_add1_hkdf_info(ctx, (const uint8_t *)value, infolen); + } + + if (strcmp(type, "hexinfo") == 0) { + size_t hex_infolen = 0; + uint8_t *info = OPENSSL_hexstr2buf(value, &hex_infolen); + if (info == NULL) { + return -2; + } + int result = EVP_PKEY_CTX_add1_hkdf_info(ctx, info, hex_infolen); + OPENSSL_free(info); + return result; + } + + return -2; +} + DEFINE_METHOD_FUNCTION(EVP_PKEY_METHOD, EVP_PKEY_hkdf_pkey_meth) { out->pkey_id = EVP_PKEY_HKDF; out->init = pkey_hkdf_init; @@ -197,7 +276,7 @@ DEFINE_METHOD_FUNCTION(EVP_PKEY_METHOD, EVP_PKEY_hkdf_pkey_meth) { out->derive = pkey_hkdf_derive; out->paramgen = NULL; /* paramgen */ out->ctrl = pkey_hkdf_ctrl; - out->ctrl_str = NULL; + out->ctrl_str = pkey_hkdf_ctrl_str; } int EVP_PKEY_CTX_hkdf_mode(EVP_PKEY_CTX *ctx, int mode) {