blob: 2ea213e2a9043860e3dcb9fe45b67dbaa13ff05c [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
#include "tink/subtle/subtle_util_boringssl.h"
#include <algorithm>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "openssl/curve25519.h"
#include "openssl/digest.h"
#include "openssl/ec.h"
#include "openssl/evp.h"
#include "openssl/x509.h"
#include "include/rapidjson/document.h"
#include "tink/subtle/common_enums.h"
#include "tink/subtle/ec_util.h"
#include "tink/subtle/wycheproof_util.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
#include "tink/util/test_util.h"
namespace crypto {
namespace tink {
namespace subtle {
namespace {
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::StrEq;
struct EncodingTestVector {
EcPointFormat format;
std::string x_hex;
std::string y_hex;
std::string encoded_hex;
EllipticCurveType curve;
};
static const std::vector<EncodingTestVector> encoding_test_vector(
{{EcPointFormat::UNCOMPRESSED,
"00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66"
"19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
"00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac"
"aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
"0400093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec"
"6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a"
"00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac"
"aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
EllipticCurveType::NIST_P521},
{EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
"00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66"
"19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
"00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac"
"aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
"00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec"
"6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a"
"00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac"
"aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
EllipticCurveType::NIST_P521},
{EcPointFormat::COMPRESSED,
"00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66"
"19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
"00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac"
"aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
"0300093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec"
"6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
EllipticCurveType::NIST_P521}});
TEST(SubtleUtilBoringSSLTest, EcPointEncode) {
for (const EncodingTestVector& test : encoding_test_vector) {
std::string x_str = test::HexDecodeOrDie(test.x_hex);
std::string y_str = test::HexDecodeOrDie(test.y_hex);
bssl::UniquePtr<BIGNUM> x(
BN_bin2bn(reinterpret_cast<const unsigned char*>(x_str.data()),
x_str.length(), nullptr));
bssl::UniquePtr<BIGNUM> y(
BN_bin2bn(reinterpret_cast<const unsigned char*>(y_str.data()),
y_str.length(), nullptr));
auto status_or_group = SubtleUtilBoringSSL::GetEcGroup(test.curve);
bssl::UniquePtr<EC_POINT> point(EC_POINT_new(status_or_group.ValueOrDie()));
EXPECT_EQ(1, EC_POINT_set_affine_coordinates_GFp(
status_or_group.ValueOrDie(), point.get(), x.get(),
y.get(), nullptr));
auto status_or_string = SubtleUtilBoringSSL::EcPointEncode(
test.curve, test.format, point.get());
EXPECT_TRUE(status_or_string.ok());
EXPECT_EQ(test.encoded_hex, test::HexEncode(status_or_string.ValueOrDie()));
}
}
TEST(SubtleUtilBoringSSLTest, EcPointDecode) {
for (const EncodingTestVector& test : encoding_test_vector) {
std::string x_str = test::HexDecodeOrDie(test.x_hex);
std::string y_str = test::HexDecodeOrDie(test.y_hex);
std::string encoded_str = test::HexDecodeOrDie(test.encoded_hex);
bssl::UniquePtr<BIGNUM> x(
BN_bin2bn(reinterpret_cast<const unsigned char*>(x_str.data()),
x_str.length(), nullptr));
bssl::UniquePtr<BIGNUM> y(
BN_bin2bn(reinterpret_cast<const unsigned char*>(y_str.data()),
y_str.length(), nullptr));
auto status_or_group = SubtleUtilBoringSSL::GetEcGroup(test.curve);
bssl::UniquePtr<EC_POINT> point(EC_POINT_new(status_or_group.ValueOrDie()));
EXPECT_EQ(1, EC_POINT_set_affine_coordinates_GFp(
status_or_group.ValueOrDie(), point.get(), x.get(),
y.get(), nullptr));
auto status_or_ec_point = SubtleUtilBoringSSL::EcPointDecode(
test.curve, test.format, encoded_str);
EXPECT_TRUE(status_or_ec_point.ok());
EXPECT_EQ(0, EC_POINT_cmp(status_or_group.ValueOrDie(), point.get(),
status_or_ec_point.ValueOrDie(), nullptr));
// Modify the 1st byte.
encoded_str = std::string("0") + encoded_str.substr(1);
status_or_ec_point = SubtleUtilBoringSSL::EcPointDecode(
test.curve, test.format, encoded_str);
EXPECT_FALSE(status_or_ec_point.ok());
EXPECT_LE(0, status_or_ec_point.status().error_message().find(
"point should start with"));
}
}
TEST(SubtleUtilBoringSSLTest, Bn2strAndStr2bn) {
int len = 8;
std::string bn_str[6] = {"0000000000000000", "0000000000000001",
"1000000000000000", "ffffffffffffffff",
"0fffffffffffffff", "00ffffffffffffff"};
for (const std::string& s : bn_str) {
auto status_or_bn = SubtleUtilBoringSSL::str2bn(test::HexDecodeOrDie(s));
EXPECT_TRUE(status_or_bn.ok());
auto status_or_str =
SubtleUtilBoringSSL::bn2str(status_or_bn.ValueOrDie().get(), len);
EXPECT_TRUE(status_or_str.ok());
EXPECT_EQ(test::HexDecodeOrDie(s), status_or_str.ValueOrDie());
}
}
TEST(SubtleUtilBoringSSLTest, ValidateSignatureHash) {
EXPECT_TRUE(
SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA256).ok());
EXPECT_TRUE(
SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA512).ok());
EXPECT_FALSE(SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA1).ok());
EXPECT_FALSE(
SubtleUtilBoringSSL::ValidateSignatureHash(HashType::UNKNOWN_HASH).ok());
}
static std::string GetError() {
auto err = ERR_peek_last_error();
// Sometimes there is no error message on the stack.
if (err == 0) {
return "";
}
std::string lib(ERR_lib_error_string(err));
std::string func(ERR_func_error_string(err));
std::string reason(ERR_reason_error_string(err));
return lib + ":" + func + ":" + reason;
}
// Test with test vectors from Wycheproof project.
bool WycheproofTest(const rapidjson::Value& root) {
int errors = 0;
for (const rapidjson::Value& test_group : root["testGroups"].GetArray()) {
std::string curve_str = test_group["curve"].GetString();
// Tink only supports secp256r1, secp384r1 or secp521r1.
if (!(curve_str == "secp256r1" || curve_str == "secp384r1" ||
curve_str == "secp521r1")) {
continue;
}
EllipticCurveType curve =
WycheproofUtil::GetEllipticCurveType(test_group["curve"]);
for (const rapidjson::Value& test : test_group["tests"].GetArray()) {
std::string id = absl::StrCat(test["tcId"].GetInt());
std::string comment = test["comment"].GetString();
std::string pub_bytes = WycheproofUtil::GetBytes(test["public"]);
std::string priv_bytes = WycheproofUtil::GetBytes(test["private"]);
std::string expected_shared_bytes = WycheproofUtil::GetBytes(test["shared"]);
std::string result = test["result"].GetString();
EcPointFormat format = EcPointFormat::UNCOMPRESSED;
for (const rapidjson::Value& flag : test["flags"].GetArray()) {
if (std::string(flag.GetString()) == "CompressedPoint") {
format = EcPointFormat::COMPRESSED;
}
}
// Wycheproof's ECDH public key uses ASN encoding while Tink uses X9.62
// format point encoding. For the purpose of testing, we note the
// followings:
// + The prefix of ASN encoding contains curve name, so we can skip test
// vector with "UnnamedCurve".
// + The suffix of ASN encoding is X9.62 format point encoding.
// TODO(quannguyen): Use X9.62 test vectors once it's available.
bool skip = false;
for (const rapidjson::Value& flag : test["flags"].GetArray()) {
if (std::string(flag.GetString()) == "UnnamedCurve") {
skip = true;
break;
}
}
if (skip) {
continue;
}
auto status_or_point_size = EcUtil::EncodingSizeInBytes(curve, format);
if (!status_or_point_size.ok()) {
continue;
}
size_t point_size = status_or_point_size.ValueOrDie();
if (point_size > pub_bytes.size()) {
continue;
}
pub_bytes = pub_bytes.substr(pub_bytes.size() - point_size, point_size);
auto status_or_ec_point =
SubtleUtilBoringSSL ::EcPointDecode(curve, format, pub_bytes);
if (!status_or_ec_point.ok()) {
if (result == "valid") {
ADD_FAILURE() << "Could not decode public key with tcId:" << id
<< " error:" << GetError()
<< status_or_ec_point.status();
}
continue;
}
bssl::UniquePtr<EC_POINT> pub_key(status_or_ec_point.ValueOrDie());
bssl::UniquePtr<BIGNUM> priv_key(
BN_bin2bn(reinterpret_cast<const unsigned char*>(priv_bytes.data()),
priv_bytes.size(), nullptr));
auto status_or_shared = SubtleUtilBoringSSL ::ComputeEcdhSharedSecret(
curve, priv_key.get(), pub_key.get());
if (status_or_shared.ok()) {
std::string shared = status_or_shared.ValueOrDie();
if (result == "invalid") {
ADD_FAILURE() << "Computed shared secret with invalid test vector"
<< ", tcId= " << id;
errors++;
} else if (shared != expected_shared_bytes) {
ADD_FAILURE() << "Computed wrong shared secret with tcId: " << id;
errors++;
}
} else {
if (result == "valid" || result == "acceptable") {
ADD_FAILURE() << "Could not compute shared secret with tcId:" << id;
errors++;
}
}
}
}
return errors == 0;
}
TEST(SubtleUtilBoringSSLTest, ComputeEcdhSharedSecretWithWycheproofTest) {
// placeholder_disabled_subtle_test, please ignore
ASSERT_TRUE(WycheproofTest(
*WycheproofUtil ::ReadTestVectors("ecdh_test.json")));
ASSERT_TRUE(WycheproofTest(
*WycheproofUtil ::ReadTestVectors("ecdh_secp256r1_test.json")));
ASSERT_TRUE(WycheproofTest(
*WycheproofUtil ::ReadTestVectors("ecdh_secp384r1_test.json")));
ASSERT_TRUE(WycheproofTest(
*WycheproofUtil ::ReadTestVectors("ecdh_secp521r1_test.json")));
}
TEST(CreatesNewRsaKeyPairTest, BasicSanityChecks) {
SubtleUtilBoringSSL::RsaPublicKey public_key;
SubtleUtilBoringSSL::RsaPrivateKey private_key;
bssl::UniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), RSA_F4);
ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key,
&public_key),
IsOk());
EXPECT_THAT(private_key.n, Not(IsEmpty()));
EXPECT_THAT(private_key.e, Not(IsEmpty()));
EXPECT_THAT(private_key.d, Not(IsEmpty()));
EXPECT_THAT(private_key.p, Not(IsEmpty()));
EXPECT_THAT(private_key.q, Not(IsEmpty()));
EXPECT_THAT(private_key.dp, Not(IsEmpty()));
EXPECT_THAT(private_key.dq, Not(IsEmpty()));
EXPECT_THAT(private_key.crt, Not(IsEmpty()));
EXPECT_THAT(public_key.n, Not(IsEmpty()));
EXPECT_THAT(public_key.e, Not(IsEmpty()));
EXPECT_EQ(public_key.n, private_key.n);
EXPECT_EQ(public_key.e, private_key.e);
}
TEST(CreatesNewRsaKeyPairTest, FailsOnLargeE) {
// OpenSSL requires the "e" value to be at most 32 bits.
SubtleUtilBoringSSL::RsaPublicKey public_key;
SubtleUtilBoringSSL::RsaPrivateKey private_key;
bssl::UniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), 1L << 33);
ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key,
&public_key),
StatusIs(util::error::INTERNAL));
}
TEST(CreatesNewRsaKeyPairTest, KeyIsWellFormed) {
SubtleUtilBoringSSL::RsaPublicKey public_key;
SubtleUtilBoringSSL::RsaPrivateKey private_key;
bssl::UniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), RSA_F4);
ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key,
&public_key),
IsOk());
auto n = std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie());
auto d = std::move(SubtleUtilBoringSSL::str2bn(private_key.d).ValueOrDie());
auto p = std::move(SubtleUtilBoringSSL::str2bn(private_key.p).ValueOrDie());
auto q = std::move(SubtleUtilBoringSSL::str2bn(private_key.q).ValueOrDie());
auto dp = std::move(SubtleUtilBoringSSL::str2bn(private_key.dp).ValueOrDie());
auto dq = std::move(SubtleUtilBoringSSL::str2bn(private_key.dq).ValueOrDie());
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
// Check n = p * q.
{
auto n_calc = bssl::UniquePtr<BIGNUM>(BN_new());
ASSERT_TRUE(BN_mul(n_calc.get(), p.get(), q.get(), ctx.get()));
ASSERT_TRUE(BN_equal_consttime(n_calc.get(), n.get()));
}
// Check n size >= 2048 bit.
EXPECT_GE(BN_num_bits(n.get()), 2048);
// dp = d mod (p - 1)
{
auto pm1 = bssl::UniquePtr<BIGNUM>(BN_dup(p.get()));
ASSERT_TRUE(BN_sub_word(pm1.get(), 1));
auto dp_calc = bssl::UniquePtr<BIGNUM>(BN_new());
ASSERT_TRUE(BN_mod(dp_calc.get(), d.get(), pm1.get(), ctx.get()));
ASSERT_TRUE(BN_equal_consttime(dp_calc.get(), dp.get()));
}
// dq = d mod (q - 1)
{
auto qm1 = bssl::UniquePtr<BIGNUM>(BN_dup(q.get()));
ASSERT_TRUE(BN_sub_word(qm1.get(), 1));
auto dq_calc = bssl::UniquePtr<BIGNUM>(BN_new());
ASSERT_TRUE(BN_mod(dq_calc.get(), d.get(), qm1.get(), ctx.get()));
ASSERT_TRUE(BN_equal_consttime(dq_calc.get(), dq.get()));
}
}
TEST(CreatesNewRsaKeyPairTest, GeneratesDifferentKeysEveryTime) {
SubtleUtilBoringSSL::RsaPublicKey public_key;
bssl::UniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), RSA_F4);
std::vector<SubtleUtilBoringSSL::RsaPrivateKey> generated_keys;
std::generate_n(std::back_inserter(generated_keys), 4, [&]() {
SubtleUtilBoringSSL::RsaPrivateKey private_key;
EXPECT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(
2048, e.get(), &private_key, &public_key),
IsOk());
return private_key;
});
// Iterate through a two-element sliding windows, comparing two consecutive
// elements in the list.
for (std::size_t i = 0; i + 1 < generated_keys.size(); ++i) {
const auto& left = generated_keys[i];
const auto& right = generated_keys[i + 1];
// The only fieldthat should be equal.
ASSERT_EQ(left.e, right.e);
ASSERT_NE(left.n, right.n);
ASSERT_NE(left.d, right.d);
ASSERT_NE(left.p, right.p);
ASSERT_NE(left.q, right.q);
ASSERT_NE(left.dp, right.dp);
ASSERT_NE(left.dq, right.dq);
ASSERT_NE(left.crt, right.crt);
}
}
TEST(CreatesNewEd25519KeyPairTest, BoringSSLPrivateKeySuffix) {
// Generate a new key pair.
uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN];
uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN];
ED25519_keypair(out_public_key, out_private_key);
std::string pk = std::string(reinterpret_cast<const char*>(out_public_key),
ED25519_PUBLIC_KEY_LEN);
std::string sk = std::string(reinterpret_cast<const char*>(out_private_key),
ED25519_PRIVATE_KEY_LEN);
ASSERT_EQ(pk.length(), 32);
ASSERT_EQ(sk.length(), 64);
// BoringSSL's ED25519_keypair returns a private key with the last 32-bytes
// equal to the public key. If this changes you must update
// SubtleUtilBoringSSL::GetNewEd25519Key().
ASSERT_EQ(sk.substr(32, std::string::npos), pk);
}
TEST(CreatesNewEd25519KeyPairTest, KeyIsWellFormed) {
auto keypair = SubtleUtilBoringSSL::GetNewEd25519Key();
ASSERT_EQ(keypair->public_key.length(), 32);
ASSERT_EQ(keypair->private_key.length(), 32);
ASSERT_TRUE(keypair->public_key != keypair->private_key);
}
TEST(CreatesNewEd25519KeyPairTest, GeneratesDifferentKeysEveryTime) {
auto keypair1 = SubtleUtilBoringSSL::GetNewEd25519Key();
auto keypair2 = SubtleUtilBoringSSL::GetNewEd25519Key();
ASSERT_NE(keypair1->public_key, keypair2->public_key);
ASSERT_NE(keypair1->private_key, keypair2->private_key);
ASSERT_NE(keypair1->public_key, keypair1->private_key);
}
TEST(SubtleUtilBoringSSLTest, ValidateRsaModulusSize) {
SubtleUtilBoringSSL::RsaPublicKey public_key;
SubtleUtilBoringSSL::RsaPrivateKey private_key;
bssl::UniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), RSA_F4);
ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key,
&public_key),
IsOk());
auto n_2048 =
std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie());
ASSERT_THAT(
SubtleUtilBoringSSL::ValidateRsaModulusSize(BN_num_bits(n_2048.get())),
IsOk());
ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(1024, e.get(), &private_key,
&public_key),
IsOk());
auto n_1024 =
std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie());
ASSERT_THAT(
SubtleUtilBoringSSL::ValidateRsaModulusSize(BN_num_bits(n_1024.get())),
Not(IsOk()));
}
TEST(ComputeHashTest, AcceptsNullStringView) {
auto null_hash =
boringssl::ComputeHash(absl::string_view(nullptr, 0), *EVP_sha512());
auto empty_hash = boringssl::ComputeHash("", *EVP_sha512());
std::string str;
auto empty_str_hash = boringssl::ComputeHash(str, *EVP_sha512());
ASSERT_THAT(null_hash.status(), IsOk());
ASSERT_THAT(empty_hash.status(), IsOk());
ASSERT_THAT(empty_str_hash.status(), IsOk());
EXPECT_EQ(null_hash.ValueOrDie(), empty_hash.ValueOrDie());
EXPECT_EQ(null_hash.ValueOrDie(), empty_str_hash.ValueOrDie());
}
using ComputeHashSamplesTest = ::testing::TestWithParam<
std::tuple<HashType, absl::string_view, absl::string_view>>;
INSTANTIATE_TEST_SUITE_P(
NistSampleCases, ComputeHashSamplesTest,
::testing::Values(
std::make_tuple(
HashType::SHA256, "af397a8b8dd73ab702ce8e53aa9f",
"d189498a3463b18e846b8ab1b41583b0b7efc789dad8a7fb885bbf8fb5b45c5c"),
std::make_tuple(
HashType::SHA256, "59eb45bbbeb054b0b97334d53580ce03f699",
"32c38c54189f2357e96bd77eb00c2b9c341ebebacc2945f97804f59a93238288"),
std::make_tuple(
HashType::SHA512,
"16b17074d3e3d97557f9ed77d920b4b1bff4e845b345a922",
"6884134582a760046433abcbd53db8ff1a89995862f305b887020f6da6c7b903a3"
"14721e972bf438483f452a8b09596298a576c903c91df4a414c7bd20fd1d07"),
std::make_tuple(
HashType::SHA512,
"7651ab491b8fa86f969d42977d09df5f8bee3e5899180b52c968b0db057a6f02a8"
"86ad617a84915a",
"f35e50e2e02b8781345f8ceb2198f068ba103476f715cfb487a452882c9f0de0c7"
"20b2a088a39d06a8a6b64ce4d6470dfeadc4f65ae06672c057e29f14c4daf9")));
TEST_P(ComputeHashSamplesTest, ComputesHash) {
const EVP_MD* hasher =
SubtleUtilBoringSSL::EvpHash(std::get<0>(GetParam())).ValueOrDie();
std::string data = absl::HexStringToBytes(std::get<1>(GetParam()));
std::string expected_hash = absl::HexStringToBytes(std::get<2>(GetParam()));
auto hash_or = boringssl::ComputeHash(data, *hasher);
ASSERT_THAT(hash_or.status(), IsOk());
std::string hash(reinterpret_cast<char*>(hash_or.ValueOrDie().data()),
hash_or.ValueOrDie().size());
EXPECT_THAT(hash, StrEq(expected_hash));
}
} // namespace
} // namespace subtle
} // namespace tink
} // namespace crypto