// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved. // SPDX-License-Identifier: Apache-2.0 #include #include #include #include #include #include #include #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 &key, const std::vector &msg, const std::vector &tag, const EVP_MD *md) { bssl::UniquePtr pkey_mac( EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, key.data(), key.size())); ASSERT_TRUE(pkey_mac); bssl::UniquePtr 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 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 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 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)); } } } 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 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 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 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 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 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 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)); }