From 709d214ea60d818acbb6c80e432c18cddf4bfb11 Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Thu, 18 Jan 2024 21:49:13 -0800 Subject: [PATCH] allow HMAC via EVP_PKEY raw privkey functions (#1338) This adds support for consuming `EVP_PKEY_HMAC` through `EVP_PKEY_new_raw_private_key` and `EVP_PKEY_get_raw_private_key`. Logic for `set_priv_raw` and `get_priv_raw` are required for `EVP_PKEY_HMAC`'s ASN1 methods to get things working. New tests setting the key and retrieving it for `EVP_PKEY_HMAC` were also added. --- crypto/evp_extra/p_hmac_asn1.c | 52 ++++++++++++++++++++++++++++++-- crypto/fipsmodule/evp/evp.c | 7 ++++- crypto/fipsmodule/evp/internal.h | 2 +- crypto/hmac_extra/hmac_test.cc | 36 ++++++++++++++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/crypto/evp_extra/p_hmac_asn1.c b/crypto/evp_extra/p_hmac_asn1.c index 9db67f7915..bea66838b3 100644 --- a/crypto/evp_extra/p_hmac_asn1.c +++ b/crypto/evp_extra/p_hmac_asn1.c @@ -57,6 +57,7 @@ #include #include +#include "../internal.h" #include "internal.h" @@ -66,9 +67,56 @@ static int hmac_size(OPENSSL_UNUSED const EVP_PKEY *pkey) { static void hmac_key_free(EVP_PKEY *pkey) { HMAC_KEY *key = pkey->pkey.ptr; + if (key != NULL) { + OPENSSL_free(key->key); + } OPENSSL_free(key); } +static int hmac_set_key(EVP_PKEY *pkey, const uint8_t *priv, size_t len, + OPENSSL_UNUSED const uint8_t *pubkey, + OPENSSL_UNUSED size_t pubkey_len) { + if (pkey->pkey.ptr != NULL) { + return 0; + } + + HMAC_KEY *key = HMAC_KEY_new(); + if (key == NULL) { + return 0; + } + + key->key = OPENSSL_memdup(priv, len); + if (key->key == NULL && len > 0) { + OPENSSL_free(key); + return 0; + } + key->key_len = len; + pkey->pkey.ptr = key; + return 1; +} + +static int hmac_get_key(const EVP_PKEY *pkey, uint8_t *priv, size_t *len) { + HMAC_KEY *key = pkey->pkey.ptr; + if (key == NULL || len == NULL) { + return 0; + } + + // The semantics of the EVP APIs are to return the length, if |priv| is NULL. + if (priv == NULL) { + *len = key->key_len; + return 1; + } + + // Retrieve the key, if |*len| has a large enough length. + if (*len < key->key_len) { + return 0; + } + *len = key->key_len; + OPENSSL_memcpy(priv, key->key, key->key_len); + return 1; +} + + const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = { EVP_PKEY_HMAC, {0xff} /* placeholder oid */, @@ -79,9 +127,9 @@ const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = { NULL /*priv_decode */, NULL /* priv_encode */, NULL /* priv_encode_v2 */, - NULL /* set_priv_raw */, + hmac_set_key /* set_priv_raw */, NULL /* set_pub_raw */, - NULL /* get_priv_raw */, + hmac_get_key /* get_priv_raw */, NULL /* get_pub_raw */, NULL /* pkey_opaque */, hmac_size /* pkey_size */, diff --git a/crypto/fipsmodule/evp/evp.c b/crypto/fipsmodule/evp/evp.c index 8415dc85c6..517f23c44a 100644 --- a/crypto/fipsmodule/evp/evp.c +++ b/crypto/fipsmodule/evp/evp.c @@ -290,8 +290,13 @@ EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *engine, const uint8_t *mac_key, if(key == NULL) { goto err; } - key->key = mac_key; + key->key = OPENSSL_memdup(mac_key, mac_key_len); + if (key->key == NULL && mac_key_len > 0) { + OPENSSL_free(key); + goto err; + } key->key_len = mac_key_len; + if(!EVP_PKEY_assign(ret, EVP_PKEY_HMAC, key)) { OPENSSL_free(key); goto err; diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index feb2486e1d..5080be1f4c 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -300,7 +300,7 @@ typedef struct { } HMAC_PKEY_CTX; typedef struct { - const uint8_t *key; + uint8_t *key; size_t key_len; } HMAC_KEY; diff --git a/crypto/hmac_extra/hmac_test.cc b/crypto/hmac_extra/hmac_test.cc index a4d91eda3b..96d00a9e86 100644 --- a/crypto/hmac_extra/hmac_test.cc +++ b/crypto/hmac_extra/hmac_test.cc @@ -157,6 +157,42 @@ static void RunHMACTestEVP(const std::vector &key, } ASSERT_TRUE(EVP_DigestSignFinal(mctx.get(), actual.data(), &len)); EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size())); + + + // Test |EVP_PKEY| key creation with |EVP_PKEY_new_raw_private_key|. + bssl::UniquePtr raw_pkey(EVP_PKEY_new_raw_private_key( + EVP_PKEY_HMAC, nullptr, key.data(), key.size())); + mctx.Reset(); + len = 0; + actual.clear(); + EXPECT_TRUE( + EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, raw_pkey.get())); + EXPECT_TRUE(EVP_DigestSignUpdate(mctx.get(), msg.data(), msg.size())); + EXPECT_TRUE(EVP_DigestSignFinal(mctx.get(), nullptr, &len)); + actual.resize(len); + EXPECT_TRUE(EVP_DigestSignFinal(mctx.get(), actual.data(), &len)); + actual.resize(len); + EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size())); + + // Test retrieving key passed into |raw_pkey| with + // |EVP_PKEY_get_raw_private_key|. + std::vector retrieved_key; + size_t retrieved_key_len; + EXPECT_TRUE(EVP_PKEY_get_raw_private_key(raw_pkey.get(), nullptr, + &retrieved_key_len)); + EXPECT_EQ(key.size(), retrieved_key_len); + retrieved_key.resize(retrieved_key_len); + EXPECT_TRUE(EVP_PKEY_get_raw_private_key(raw_pkey.get(), retrieved_key.data(), + &retrieved_key_len)); + retrieved_key.resize(retrieved_key_len); + EXPECT_EQ(Bytes(retrieved_key), Bytes(key)); + + // Test retrieving key with a buffer length that's too small. This should fail + if (!key.empty()) { + size_t short_key_len = retrieved_key_len - 1; + EXPECT_FALSE(EVP_PKEY_get_raw_private_key( + raw_pkey.get(), retrieved_key.data(), &short_key_len)); + } }