Files
cli/vendor/aws-lc-sys/aws-lc/crypto/hmac_extra/hmac_test.cc

630 lines
28 KiB
C++

// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <memory>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include <openssl/digest.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include "../../../crypto/fipsmodule/hmac/internal.h"
#include "../test/file_test.h"
#include "../test/test_util.h"
#include "../test/wycheproof_util.h"
static const EVP_MD *GetDigest(const std::string &name) {
if (name == "MD5") {
return EVP_md5();
} else if (name == "SHA1") {
return EVP_sha1();
} else if (name == "SHA224") {
return EVP_sha224();
} else if (name == "SHA256") {
return EVP_sha256();
} else if (name == "SHA384") {
return EVP_sha384();
} else if (name == "SHA512") {
return EVP_sha512();
} else if (name == "SHA512/224") {
return EVP_sha512_224();
} else if (name == "SHA512/256") {
return EVP_sha512_256();
} else if (name == "SHA3-224") {
return EVP_sha3_224();
} else if (name == "SHA3-256") {
return EVP_sha3_256();
} else if (name == "SHA3-384") {
return EVP_sha3_384();
} else if (name == "SHA3-512") {
return EVP_sha3_512();
}
return nullptr;
}
static size_t GetPrecomputedKeySize(const std::string &name) {
if (name == "MD5") {
return HMAC_MD5_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA1") {
return HMAC_SHA1_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA224") {
return HMAC_SHA224_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA256") {
return HMAC_SHA256_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA384") {
return HMAC_SHA384_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA512") {
return HMAC_SHA512_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA512/224") {
return HMAC_SHA512_224_PRECOMPUTED_KEY_SIZE;
} else if (name == "SHA512/256") {
return HMAC_SHA512_256_PRECOMPUTED_KEY_SIZE;
}
return 0;
}
static void RunHMACTestEVP(const std::vector<uint8_t> &key,
const std::vector<uint8_t> &msg,
const std::vector<uint8_t> &tag, const EVP_MD *md) {
bssl::UniquePtr<EVP_PKEY> pkey_mac(
EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, key.data(), key.size()));
ASSERT_TRUE(pkey_mac);
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_HMAC, NULL));
ASSERT_TRUE(ctx);
ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get()));
auto hexkey = EncodeHex(key);
ASSERT_TRUE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "hexkey", hexkey.data()));
EVP_PKEY* my_pkey = NULL;
ASSERT_TRUE(EVP_PKEY_keygen(ctx.get(), &my_pkey));
bssl::UniquePtr<EVP_PKEY> pkey_gen(my_pkey);
ASSERT_TRUE(pkey_gen);
for (const auto pkey : {pkey_mac.get(), pkey_gen.get()}) {
bssl::ScopedEVP_MD_CTX copy, mctx;
size_t len;
std::vector<uint8_t> actual;
ASSERT_TRUE(EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, pkey));
// Make a copy we can test against later.
ASSERT_TRUE(EVP_MD_CTX_copy_ex(copy.get(), mctx.get()));
ASSERT_TRUE(EVP_DigestSignUpdate(mctx.get(), msg.data(), msg.size()));
ASSERT_TRUE(EVP_DigestSignFinal(mctx.get(), nullptr, &len));
actual.resize(len);
ASSERT_TRUE(EVP_DigestSignFinal(mctx.get(), actual.data(), &len));
actual.resize(len);
// Wycheproof tests truncate the tags down to |tagSize|. Expected outputs in
// hmac_tests.txt have the length of the entire tag.
EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size()));
// Repeat the test with |copy|, to check |EVP_MD_CTX_copy_ex| duplicated
// everything.
len = 0;
actual.clear();
ASSERT_TRUE(EVP_DigestSignUpdate(copy.get(), msg.data(), msg.size()));
ASSERT_TRUE(EVP_DigestSignFinal(copy.get(), nullptr, &len));
actual.resize(len);
ASSERT_TRUE(EVP_DigestSignFinal(copy.get(), actual.data(), &len));
actual.resize(len);
EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size()));
// Test using the one-shot API.
mctx.Reset();
copy.Reset();
len = 0;
actual.clear();
ASSERT_TRUE(EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, pkey));
ASSERT_TRUE(EVP_MD_CTX_copy_ex(copy.get(), mctx.get()));
ASSERT_TRUE(
EVP_DigestSign(mctx.get(), nullptr, &len, msg.data(), msg.size()));
actual.resize(len);
ASSERT_TRUE(
EVP_DigestSign(mctx.get(), actual.data(), &len, msg.data(), msg.size()));
actual.resize(len);
EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size()));
// Repeat the test with |copy|, to check |EVP_MD_CTX_copy_ex| duplicated
// everything.
len = 0;
actual.clear();
ASSERT_TRUE(EVP_DigestSignUpdate(copy.get(), msg.data(), msg.size()));
ASSERT_TRUE(EVP_DigestSignFinal(copy.get(), nullptr, &len));
actual.resize(len);
ASSERT_TRUE(EVP_DigestSignFinal(copy.get(), actual.data(), &len));
actual.resize(len);
EXPECT_EQ(Bytes(tag), Bytes(actual.data(), tag.size()));
// Test feeding the input in byte by byte.
mctx.Reset();
ASSERT_TRUE(EVP_DigestSignInit(mctx.get(), nullptr, md, nullptr, pkey));
for (const unsigned char &i : msg) {
ASSERT_TRUE(EVP_DigestSignUpdate(mctx.get(), &i, 1));
}
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<EVP_PKEY> 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<uint8_t> 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));
}
}
}
TEST(HMACTest, TestVectors) {
FileTestGTest("crypto/hmac_extra/hmac_tests.txt", [](FileTest *t) {
std::string digest_str;
ASSERT_TRUE(t->GetAttribute(&digest_str, "HMAC"));
const EVP_MD *digest = GetDigest(digest_str);
ASSERT_TRUE(digest) << "Unknown digest: " << digest_str;
std::vector<uint8_t> key, input, output;
ASSERT_TRUE(t->GetBytes(&key, "Key"));
ASSERT_TRUE(t->GetBytes(&input, "Input"));
ASSERT_TRUE(t->GetBytes(&output, "Output"));
ASSERT_EQ(EVP_MD_size(digest), output.size());
// Test using the one-shot API.
const unsigned expected_mac_len = EVP_MD_size(digest);
std::unique_ptr<uint8_t[]> mac(new uint8_t[expected_mac_len]);
unsigned mac_len;
ASSERT_TRUE(HMAC(digest, key.data(), key.size(), input.data(), input.size(),
mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test using HMAC_CTX.
bssl::ScopedHMAC_CTX ctx;
ASSERT_TRUE(
HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that an HMAC_CTX may be reset with the same key.
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that an HMAC_CTX may be reset with the same key and a null md
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Some callers will call init multiple times and we need to ensure that doesn't break anything
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test feeding the input in byte by byte.
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
for (size_t i = 0; i < input.size(); i++) {
ASSERT_TRUE(HMAC_Update(ctx.get(), &input[i], 1));
}
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
// Test consuming HMAC through the |EVP_PKEY_HMAC| interface.
RunHMACTestEVP(key, input, output, digest);
});
}
TEST(HMACTest, TestVectorsPrecomputedKey) {
FileTestGTest("crypto/hmac_extra/hmac_tests.txt", [](FileTest *t) {
std::string digest_str;
ASSERT_TRUE(t->GetAttribute(&digest_str, "HMAC"));
const EVP_MD *digest = GetDigest(digest_str);
ASSERT_TRUE(digest) << "Unknown digest: " << digest_str;
std::vector<uint8_t> key, input, output;
ASSERT_TRUE(t->GetBytes(&key, "Key"));
ASSERT_TRUE(t->GetBytes(&input, "Input"));
ASSERT_TRUE(t->GetBytes(&output, "Output"));
ASSERT_EQ(EVP_MD_size(digest), output.size());
// Test using the one-shot API.
const unsigned expected_mac_len = EVP_MD_size(digest);
std::unique_ptr<uint8_t[]> mac(new uint8_t[expected_mac_len]);
unsigned mac_len;
ASSERT_TRUE(HMAC(digest, key.data(), key.size(), input.data(), input.size(),
mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Digests that do not support pre-computed keys will have a non-positive
// pre-computed key size. In this case, assert that we can't successfully
// call precomputed-key functions.
bssl::ScopedHMAC_CTX ctx;
if (GetPrecomputedKeySize(digest_str) <= 0) {
ASSERT_TRUE(
HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_FALSE(HMAC_set_precomputed_key_export(ctx.get()));
size_t len;
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), key.data(), &len));
ASSERT_FALSE(HMAC_Init_from_precomputed_key(ctx.get(), key.data(), key.size(), digest));
return;
}
// Test using the one-shot API with precompute
ASSERT_TRUE(HMAC_with_precompute(digest, key.data(), key.size(),
input.data(), input.size(), mac.get(),
&mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test using HMAC_CTX.
ASSERT_TRUE(
HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
uint8_t precomputed_key[HMAC_MAX_PRECOMPUTED_KEY_SIZE];
// Test that the precomputed key cannot be exported without calling
// HMAC_set_precomputed_key_export
size_t precomputed_key_len_out = HMAC_MAX_PRECOMPUTED_KEY_SIZE;
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), precomputed_key, &precomputed_key_len_out));
// Test that the precomputed key cannot be exported if ctx not initialized
// and the precomputed_key_export flag cannot be set
bssl::ScopedHMAC_CTX ctx2;
ASSERT_FALSE(HMAC_set_precomputed_key_export(ctx2.get()));
precomputed_key_len_out = HMAC_MAX_PRECOMPUTED_KEY_SIZE;
ASSERT_FALSE(HMAC_get_precomputed_key(ctx2.get(), precomputed_key, &precomputed_key_len_out));
// Get the precomputed key length for later use
// And test the precomputed key size is at most HMAC_MAX_PRECOMPUTED_KEY_SIZE
// and is equal to HMAC_xxx_PRECOMPUTED_KEY_SIZE, where xxx is the digest name
ASSERT_TRUE(HMAC_set_precomputed_key_export(ctx.get()));
size_t precomputed_key_len;
HMAC_get_precomputed_key(ctx.get(), nullptr, &precomputed_key_len);
ASSERT_LE(precomputed_key_len, (size_t) HMAC_MAX_PRECOMPUTED_KEY_SIZE);
ASSERT_EQ(GetPrecomputedKeySize(digest_str), precomputed_key_len);
// Test that at this point, the context cannot be used with HMAC_Update
ASSERT_FALSE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_FALSE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
// Export the precomputed key for later use
precomputed_key_len_out = HMAC_MAX_PRECOMPUTED_KEY_SIZE;
ASSERT_TRUE(HMAC_get_precomputed_key(ctx.get(), precomputed_key, &precomputed_key_len_out));
ASSERT_EQ(precomputed_key_len, precomputed_key_len_out);
// Test that at this point, the context can be used with HMAC_Update
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that an HMAC_CTX may be reset with the same key but with HMAC_Init_from_precomputed_key
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 0, digest));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that an HMAC_CTX may be reset with the same key and a null md but using the Init_from_precomputed_key instead
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 0, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Some callers will call init multiple times and we need to ensure that doesn't break anything but using a mix of Init_ex and Init_from_precomputed_key
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 0, nullptr));
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 0, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that the HMAC_CTX can be reset using the precomputed key
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), precomputed_key, precomputed_key_len, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Same test but starting from an empty context
ctx.Reset();
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), precomputed_key, precomputed_key_len, digest));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Some callers will call init_from_precomputed_key multiple times and we need to ensure that doesn't break anything
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), precomputed_key, precomputed_key_len, nullptr));
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 0, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), input.data(), input.size()));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer
// Test that we get an error if the out_len is not large enough or is null
uint8_t precomputed_key2[HMAC_MAX_PRECOMPUTED_KEY_SIZE];
size_t precomputed_key_len_out2;
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr));
ASSERT_TRUE(HMAC_set_precomputed_key_export(ctx.get()));
ASSERT_TRUE(HMAC_set_precomputed_key_export(ctx.get())); // testing we can set it twice
precomputed_key_len_out2 = precomputed_key_len - 1; // slightly too short
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, &precomputed_key_len_out2));
precomputed_key_len_out2 = 0; // 0-size should also fail
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, &precomputed_key_len_out2));
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, nullptr));
// Test that we get the same precompute_key the second time we correctly call HMAC_get_precomputed_key
precomputed_key_len_out2 = precomputed_key_len; // testing with the out_len is the exact value
ASSERT_TRUE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, &precomputed_key_len_out2));
ASSERT_EQ(precomputed_key_len, precomputed_key_len_out2);
ASSERT_EQ(Bytes(precomputed_key, precomputed_key_len), Bytes(precomputed_key2, precomputed_key_len));
OPENSSL_memset(precomputed_key2, 0, HMAC_MAX_PRECOMPUTED_KEY_SIZE); // Clear the prior correct answer
// Test that at this point, the context cannot be used to re-export the precomputed key
precomputed_key_len_out2 = HMAC_MAX_PRECOMPUTED_KEY_SIZE;
ASSERT_FALSE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, &precomputed_key_len_out2));
// Check that precomputed_key_len_out2 and precomputed_key2 were not modified and are still their original value
uint8_t zero_precomputed_key[HMAC_MAX_PRECOMPUTED_KEY_SIZE];
OPENSSL_memset(zero_precomputed_key, 0, HMAC_MAX_PRECOMPUTED_KEY_SIZE);
ASSERT_EQ((size_t)HMAC_MAX_PRECOMPUTED_KEY_SIZE, precomputed_key_len_out2);
ASSERT_EQ(Bytes(zero_precomputed_key, HMAC_MAX_PRECOMPUTED_KEY_SIZE), Bytes(precomputed_key2, HMAC_MAX_PRECOMPUTED_KEY_SIZE));
// Same but initializing the ctx using the precompute key in the first place
ctx.Reset();
ASSERT_TRUE(HMAC_Init_from_precomputed_key(ctx.get(), precomputed_key, precomputed_key_len, digest));
ASSERT_TRUE(HMAC_set_precomputed_key_export(ctx.get()));
ASSERT_TRUE(HMAC_get_precomputed_key(ctx.get(), precomputed_key2, &precomputed_key_len_out2));
ASSERT_EQ(precomputed_key_len, precomputed_key_len_out2);
ASSERT_EQ(Bytes(precomputed_key, precomputed_key_len), Bytes(precomputed_key2, precomputed_key_len));
// Test feeding the input in byte by byte after initializing from precomputed key
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
for (size_t i = 0; i < input.size(); i++) {
ASSERT_TRUE(HMAC_Update(ctx.get(), &input[i], 1));
}
ASSERT_TRUE(HMAC_Final(ctx.get(), mac.get(), &mac_len));
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len));
// Test consuming HMAC through the |EVP_PKEY_HMAC| interface.
RunHMACTestEVP(key, input, output, digest);
// Test that initializing without the precomputed_key does not work
ctx.Reset();
ASSERT_FALSE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, precomputed_key_len, digest));
// Test that initializing with the wrong precomputed_key_len does not work
ctx.Reset();
ASSERT_FALSE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, 1, digest));
ASSERT_FALSE(HMAC_Init_from_precomputed_key(ctx.get(), nullptr, precomputed_key_len+1, digest));
});
}
static void RunWycheproofTest(const char *path, const EVP_MD *md) {
SCOPED_TRACE(path);
FileTestGTest(path, [&](FileTest *t) {
t->IgnoreInstruction("keySize");
t->IgnoreInstruction("tagSize");
std::vector<uint8_t> key, msg, tag;
ASSERT_TRUE(t->GetBytes(&key, "key"));
ASSERT_TRUE(t->GetBytes(&msg, "msg"));
ASSERT_TRUE(t->GetBytes(&tag, "tag"));
WycheproofResult result;
ASSERT_TRUE(GetWycheproofResult(t, &result));
if (!result.IsValid()) {
// Wycheproof tests assume the HMAC implementation checks the MAC. Ours
// simply computes the HMAC, so skip the tests with invalid outputs.
return;
}
uint8_t out[EVP_MAX_MD_SIZE];
unsigned out_len;
ASSERT_TRUE(HMAC(md, key.data(), key.size(), msg.data(), msg.size(), out,
&out_len));
// Wycheproof tests truncate the tags down to |tagSize|.
ASSERT_LE(tag.size(), out_len);
EXPECT_EQ(Bytes(out, tag.size()), Bytes(tag));
// Run Wycheproof tests through the |EVP_PKEY_HMAC| interface.
RunHMACTestEVP(key, msg, tag, md);
});
}
TEST(HMACTest, WycheproofSHA1) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha1_test.txt",
EVP_sha1());
}
TEST(HMACTest, WycheproofSHA224) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha224_test.txt",
EVP_sha224());
}
TEST(HMACTest, WycheproofSHA256) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha256_test.txt",
EVP_sha256());
}
TEST(HMACTest, WycheproofSHA384) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha384_test.txt",
EVP_sha384());
}
TEST(HMACTest, WycheproofSHA512) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha512_test.txt",
EVP_sha512());
}
TEST(HMACTest, WycheproofSHA512_224) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha512_224_test.txt",
EVP_sha512_224());
}
TEST(HMACTest, WycheproofSHA512_256) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha512_256_test.txt",
EVP_sha512_256());
}
TEST(HMACTest, WycheproofSHA3_224) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha3_224_test.txt",
EVP_sha3_224());
}
TEST(HMACTest, WycheproofSHA3_256) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha3_256_test.txt",
EVP_sha3_256());
}
TEST(HMACTest, WycheproofSHA3_384) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha3_384_test.txt",
EVP_sha3_384());
}
TEST(HMACTest, WycheproofSHA3_512) {
RunWycheproofTest("third_party/wycheproof_testvectors/hmac_sha3_512_test.txt",
EVP_sha3_512());
}
TEST(HMACTest, EVP_DigestVerify) {
bssl::UniquePtr<EVP_PKEY> pkey(
EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, nullptr, 0));
ASSERT_TRUE(pkey);
bssl::ScopedEVP_MD_CTX mctx;
EXPECT_FALSE(EVP_DigestVerifyInit(mctx.get(), nullptr, EVP_sha256(), nullptr,
pkey.get()));
EXPECT_EQ(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
ERR_GET_REASON(ERR_get_error()));
EXPECT_FALSE(EVP_DigestVerifyUpdate(mctx.get(), nullptr, 0));
EXPECT_EQ(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
ERR_GET_REASON(ERR_get_error()));
EXPECT_FALSE(EVP_DigestVerifyFinal(mctx.get(), nullptr, 0));
EXPECT_EQ(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
ERR_GET_REASON(ERR_get_error()));
EXPECT_FALSE(EVP_DigestVerify(mctx.get(), nullptr, 0, nullptr, 0));
EXPECT_EQ(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
ERR_GET_REASON(ERR_get_error()));
}
TEST(HMACTest, HandlesNullOutputParameters) {
bssl::ScopedHMAC_CTX ctx;
const EVP_MD *digest = EVP_sha256();
// make key and input valid
const uint8_t key[32] = {0};
const uint8_t input[16] = {0};
// Test one-shot API with out and out_len as NULL
ASSERT_FALSE(HMAC(digest, &key[0], sizeof(key), &input[0], sizeof(input),
nullptr,nullptr));
unsigned mac_len;
// Test one-shot API with only out as NULL
ASSERT_FALSE(HMAC(digest, &key[0], sizeof(key), &input[0], sizeof(input),
nullptr, &mac_len));
// Test HMAC_ctx
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), &key[0], sizeof(key), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), &input[0], sizeof(input)));
ASSERT_FALSE(HMAC_Final(ctx.get(), nullptr, nullptr));
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), &key[0], sizeof(key), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), &input[0], sizeof(input)));
ASSERT_FALSE(HMAC_Final(ctx.get(), nullptr, &mac_len));
}
TEST(HMACTest, InitExResetsContext) {
bssl::ScopedHMAC_CTX ctx;
bssl::ScopedHMAC_CTX ctx_copy;
const EVP_MD *digest = EVP_sha256();
const uint8_t key1[] = "test_key_1";
const uint8_t data1[] = "first_message";
uint8_t mac1[EVP_MAX_MD_SIZE];
uint8_t mac2[EVP_MAX_MD_SIZE];
uint8_t mac3[EVP_MAX_MD_SIZE];
unsigned mac_len;
// First HMAC computation with |key1| and |data1|.
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), key1, sizeof(key1), digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), data1, sizeof(data1)));
// Copy to |ctx_copy| and finalize that to preserve |HMAC_STATE_IN_PROGRESS|
// in |ctx|.
ASSERT_TRUE(HMAC_CTX_copy(ctx_copy.get(), ctx.get()));
ASSERT_TRUE(HMAC_Final(ctx_copy.get(), mac1, &mac_len));
// Reset context with NULL |key|/|digest| - should reuse previous key and
// reset state. This tests that |HMAC_Init_ex| properly resets even after
// Update/Final.
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, nullptr, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), data1, sizeof(data1)));
ASSERT_TRUE(HMAC_Final(ctx.get(), mac2, &mac_len));
// |mac1| and |mac2| should be the same (same key reused, context reset).
EXPECT_EQ(Bytes(mac1, mac_len), Bytes(mac2, mac_len));
// Reset |ctx| with NULL |key| but explicit digest - should reuse previous
// key and reset state.
ASSERT_TRUE(HMAC_Init_ex(ctx.get(), nullptr, 0, digest, nullptr));
ASSERT_TRUE(HMAC_Update(ctx.get(), data1, sizeof(data1)));
// Copy to |ctx_copy| and finalize that to preserve |HMAC_STATE_IN_PROGRESS|
// in |ctx|.
ASSERT_TRUE(HMAC_CTX_copy(ctx_copy.get(), ctx.get()));
ASSERT_TRUE(HMAC_Final(ctx_copy.get(), mac3, &mac_len));
// |mac1| and |mac3| should be the same (same key reused, context reset).
EXPECT_EQ(Bytes(mac1, mac_len), Bytes(mac3, mac_len));
}