// Copyright 2019 Google LLC
//
// 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/prf/hkdf_streaming_prf.h"

#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <utility>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "tink/config/tink_fips.h"
#include "tink/subtle/common_enums.h"
#include "tink/subtle/hkdf.h"
#include "tink/subtle/random.h"
#include "tink/util/input_stream_util.h"
#include "tink/util/secret_data.h"
#include "tink/util/test_matchers.h"
#include "tink/util/test_util.h"

namespace crypto {
namespace tink {
namespace subtle {

namespace {

using ::crypto::tink::test::HexDecodeOrDie;
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Ne;
using ::testing::Not;
using ::testing::SizeIs;

// GENERIC TESTS ===============================================================
//
// These should be satisfied for any streaming prf which generates enough
// output.
TEST(HkdfStreamingPrf, Basic) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA512, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");
  auto result_or = ReadBytesFromStream(10, stream.get());
  ASSERT_THAT(result_or, IsOk());

  EXPECT_THAT(result_or.value(), SizeIs(10));
}

TEST(HkdfStreamingPrf, EmptySalt) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }

  crypto::tink::subtle::HashType hash_type = SHA512;
  const int hash_length = 64;
  util::SecretData secret = util::SecretDataFromStringView("key0123456");
  absl::string_view input = "input";
  int num_bytes = 10;

  std::string prf_empty_salt;
  std::string prf_zeroed_salt;
  {
    auto streaming_prf = HkdfStreamingPrf::New(hash_type, secret, "");
    ASSERT_THAT(streaming_prf, IsOk());
    std::unique_ptr<InputStream> stream = (*streaming_prf)->ComputePrf(input);
    auto result = ReadBytesFromStream(num_bytes, stream.get());
    ASSERT_THAT(result, IsOk());
    prf_empty_salt = *result;
  }
  {
    uint8_t zeroedSalt[hash_length];
    std::fill(std::begin(zeroedSalt), std::end(zeroedSalt), 0);
    auto streaming_prf = HkdfStreamingPrf::New(hash_type, secret,
                                               std::string((char*)zeroedSalt));
    ASSERT_THAT(streaming_prf, IsOk());
    std::unique_ptr<InputStream> stream = (*streaming_prf)->ComputePrf(input);
    auto result = ReadBytesFromStream(num_bytes, stream.get());
    ASSERT_THAT(result, IsOk());
    prf_zeroed_salt = *result;
  }
  EXPECT_THAT(prf_empty_salt, SizeIs(num_bytes));
  EXPECT_THAT(prf_zeroed_salt, SizeIs(num_bytes));
  ASSERT_EQ(prf_empty_salt, prf_zeroed_salt);
}

TEST(HkdfStreamingPrf, DifferentInputsGiveDifferentvalues) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA512, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");
  auto result_or = ReadBytesFromStream(10, stream.get());
  ASSERT_THAT(result_or, IsOk());

  // Different input.
  std::unique_ptr<InputStream> stream2 =
      streaming_prf_or.value()->ComputePrf("input2");
  auto result_or2 = ReadBytesFromStream(10, stream2.get());
  ASSERT_THAT(result_or2, IsOk());
  EXPECT_THAT(result_or2.value(), Ne(result_or.value()));
}

TEST(HkdfStreamingPrf, SameInputTwice) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA512, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");
  auto result_or = ReadBytesFromStream(10, stream.get());
  ASSERT_THAT(result_or, IsOk());

  // Same input.
  std::unique_ptr<InputStream> stream2 =
      streaming_prf_or.value()->ComputePrf("input");
  auto result_or2 = ReadBytesFromStream(10, stream2.get());
  ASSERT_THAT(result_or2, IsOk());
  EXPECT_THAT(result_or2.value(), Eq(result_or.value()));
}

// STREAM HANDLING TESTS =======================================================
//
// These check that the buffer handling of the implementation is correct. They
// should be satisfied with any input stream.

// Tests that after Backing up the full stream, we get back the same data.
TEST(HkdfStreamingPrf, BackupFullStream) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  int bytes_read = result.value();
  std::string first_read =
      std::string(static_cast<const char*>(data), bytes_read);

  stream->BackUp(bytes_read);

  result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  // We typically read at least as many bytes the second time -- strictly
  // speaking this might not be satisfied by every InputStream, but it usually
  // will be.
  ASSERT_THAT(result.value(), Ge(bytes_read));

  std::string second_read =
      std::string(static_cast<const char*>(data), bytes_read);
  EXPECT_THAT(first_read, Eq(second_read));
}

// Tests that after Backing up half the stream, we get back the same data.
TEST(HkdfStreamingPrf, BackupHalf) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  int bytes_read = result.value();
  int backup_amount = bytes_read / 2;
  std::string first_read =
      std::string(static_cast<const char*>(data) + bytes_read - backup_amount,
                  backup_amount);

  stream->BackUp(backup_amount);

  result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  // We typically read at least as many bytes the second time -- strictly
  // speaking this might not be satisfied by every InputStream, but it usually
  // will be.
  ASSERT_THAT(result.value(), Ge(backup_amount));

  std::string second_read =
      std::string(static_cast<const char*>(data), backup_amount);
  EXPECT_THAT(first_read, Eq(second_read));
}

// Tests that after Position is correct initially (i.e., 0).
TEST(HkdfStreamingPrf, PositionOneRead) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  EXPECT_THAT(stream->Position(), Eq(0));
}

// Tests that after Position is correct after a single read.
TEST(HkdfStreamingPrf, PositionSingleRead) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  EXPECT_THAT(stream->Position(), Eq(result.value()));
}

// Tests that after Position is correct after a two reads.
TEST(HkdfStreamingPrf, PositionTwoReads) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());

  crypto::tink::util::StatusOr<int> result2 = stream->Next(&data);
  ASSERT_THAT(result, IsOk());

  EXPECT_THAT(stream->Position(), Eq(result.value() + result2.value()));
}

// Tests that we can backup the first read completely.
TEST(HkdfStreamingPrf, BackupSingleRead) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());
  stream->BackUp(result.value());
  EXPECT_THAT(stream->Position(), Eq(0));
}

// Tests that we can backup the second read completely.
TEST(HkdfStreamingPrf, BackupSecondRead) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());

  crypto::tink::util::StatusOr<int> result2 = stream->Next(&data);
  ASSERT_THAT(result, IsOk());

  stream->BackUp(result2.value());

  EXPECT_THAT(stream->Position(), Eq(result.value()));
}

// Tests that we can partially backup and position is correct.
TEST(HkdfStreamingPrf, PartialBackup) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA256, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");

  const void* data;
  crypto::tink::util::StatusOr<int> result = stream->Next(&data);
  ASSERT_THAT(result, IsOk());

  stream->BackUp(result.value() / 2);

  EXPECT_THAT(stream->Position(), Eq(result.value() - result.value() / 2));
}

// HKDF Specific tests =========================================================
// Tests which only apply for Hkdf.
TEST(HkdfStreamingPrf, ExhaustInput) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  auto streaming_prf_or = HkdfStreamingPrf::New(
      SHA512, util::SecretDataFromStringView("key0123456"), "salt");
  ASSERT_THAT(streaming_prf_or, IsOk());

  const int max_output_length = 255 * (512 / 8);
  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf("input");
  auto result_or = ReadBytesFromStream(max_output_length, stream.get());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), SizeIs(max_output_length));
  result_or = ReadBytesFromStream(50, stream.get());
  ASSERT_THAT(result_or, Not(IsOk()));
}

// TEST VECTORS AND COMPARISON =================================================
// These test are Hkdf specific. We test with the test vectors from RFC 5869 and
// compare with our implementation.
TEST(HkdfStreamingPrf, TestVector1) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.1
  HashType hash = SHA256;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"));
  std::string salt = HexDecodeOrDie("000102030405060708090a0b0c");
  std::string info = HexDecodeOrDie("f0f1f2f3f4f5f6f7f8f9");
  std::string expected_result = HexDecodeOrDie(
      "3cb25f25faacd57a90434f64d0362f2a"
      "2d2d0a90cf1a5a4c5db02d56ecc4c5bf"
      "34007208d5b887185865");

  auto streaming_prf_or = HkdfStreamingPrf::New(hash, ikm, salt);
  ASSERT_THAT(streaming_prf_or, IsOk());
  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf(info);
  auto result_or = ReadBytesFromStream(expected_result.size(), stream.get());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

crypto::tink::util::StatusOr<std::string> ComputeWithHkdfStreamingPrf(
    HashType hash, util::SecretData ikm, std::string salt, std::string info,
    int length) {
  auto streaming_prf_or = HkdfStreamingPrf::New(hash, std::move(ikm), salt);
  if (!streaming_prf_or.status().ok()) {
    return streaming_prf_or.status();
  }
  std::unique_ptr<InputStream> stream =
      streaming_prf_or.value()->ComputePrf(info);
  return ReadBytesFromStream(length, stream.get());
}

TEST(HkdfStreamingPrf, TestVector2) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.2
  HashType hash = SHA256;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("000102030405060708090a0b0c0d0e0f"
                     "101112131415161718191a1b1c1d1e1f"
                     "202122232425262728292a2b2c2d2e2f"
                     "303132333435363738393a3b3c3d3e3f"
                     "404142434445464748494a4b4c4d4e4f"));
  std::string salt = HexDecodeOrDie(
      "606162636465666768696a6b6c6d6e6f"
      "707172737475767778797a7b7c7d7e7f"
      "808182838485868788898a8b8c8d8e8f"
      "909192939495969798999a9b9c9d9e9f"
      "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
  std::string info = HexDecodeOrDie(
      "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
      "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
      "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
      "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
  std::string expected_result = HexDecodeOrDie(
      "b11e398dc80327a1c8e7f78c596a4934"
      "4f012eda2d4efad8a050cc4c19afa97c"
      "59045a99cac7827271cb41c65e590e09"
      "da3275600c2f09b8367793a9aca3db71"
      "cc30c58179ec3e87c14c01d5c1f3434f"
      "1d87");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestVector3) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.3
  HashType hash = SHA256;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"));
  std::string salt = HexDecodeOrDie("");
  std::string info = HexDecodeOrDie("");
  std::string expected_result = HexDecodeOrDie(
      "8da4e775a563c18f715f802a063c5a31"
      "b8a11f5c5ee1879ec3454e5f3c738d2d"
      "9d201395faa4b61a96c8");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestVector4) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.4
  HashType hash = SHA1;
  util::SecretData ikm =
      util::SecretDataFromStringView(HexDecodeOrDie("0b0b0b0b0b0b0b0b0b0b0b"));
  std::string salt = HexDecodeOrDie("000102030405060708090a0b0c");
  std::string info = HexDecodeOrDie("f0f1f2f3f4f5f6f7f8f9");
  std::string expected_result = HexDecodeOrDie(
      "085a01ea1b10f36933068b56efa5ad81"
      "a4f14b822f5b091568a9cdd4f155fda2"
      "c22e422478d305f3f896");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestVector5) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.5
  HashType hash = SHA1;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("000102030405060708090a0b0c0d0e0f"
                     "101112131415161718191a1b1c1d1e1f"
                     "202122232425262728292a2b2c2d2e2f"
                     "303132333435363738393a3b3c3d3e3f"
                     "404142434445464748494a4b4c4d4e4f"));
  std::string salt = HexDecodeOrDie(
      "606162636465666768696a6b6c6d6e6f"
      "707172737475767778797a7b7c7d7e7f"
      "808182838485868788898a8b8c8d8e8f"
      "909192939495969798999a9b9c9d9e9f"
      "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
  std::string info = HexDecodeOrDie(
      "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
      "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
      "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
      "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
  std::string expected_result = HexDecodeOrDie(
      "0bd770a74d1160f7c9f12cd5912a06eb"
      "ff6adcae899d92191fe4305673ba2ffe"
      "8fa3f1a4e5ad79f3f334b3b202b2173c"
      "486ea37ce3d397ed034c7f9dfeb15c5e"
      "927336d0441f4c4300e2cff0d0900b52"
      "d3b4");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestVector6) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.6
  HashType hash = SHA1;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"));
  std::string salt = HexDecodeOrDie("");
  std::string info = HexDecodeOrDie("");
  std::string expected_result = HexDecodeOrDie(
      "0ac1af7002b3d761d1e55298da9d0506"
      "b9ae52057220a306e07b6b87e8df21d0"
      "ea00033de03984d34918");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestVector7) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  // https://tools.ietf.org/html/rfc5869#appendix-A.7
  HashType hash = SHA1;
  util::SecretData ikm = util::SecretDataFromStringView(
      HexDecodeOrDie("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"));
  // Since HMAC anyhow pads, this is the same as an absent salt.
  std::string salt = HexDecodeOrDie("");
  std::string info = HexDecodeOrDie("");
  std::string expected_result = HexDecodeOrDie(
      "0ac1af7002b3d761d1e55298da9d0506"
      "b9ae52057220a306e07b6b87e8df21d0"
      "ea00033de03984d34918");

  auto result_or = ComputeWithHkdfStreamingPrf(hash, std::move(ikm), salt, info,
                                               expected_result.size());
  ASSERT_THAT(result_or, IsOk());
  EXPECT_THAT(result_or.value(), Eq(expected_result));
}

TEST(HkdfStreamingPrf, TestAgainstHkdfUtil) {
  if (IsFipsModeEnabled()) {
    GTEST_SKIP() << "Not supported in FIPS-only mode";
  }
  HashType hash = SHA1;
  util::SecretData ikm = Random::GetRandomKeyBytes(123);
  std::string salt = Random::GetRandomBytes(234);
  std::string info = Random::GetRandomBytes(345);

  auto streaming_result_or =
      ComputeWithHkdfStreamingPrf(hash, ikm, salt, info, 456);
  ASSERT_THAT(streaming_result_or, IsOk());

  auto compute_hkdf_result_or = Hkdf::ComputeHkdf(hash, ikm, salt, info, 456);
  ASSERT_THAT(compute_hkdf_result_or, IsOk());

  util::SecretData compute_hkdf_result =
      std::move(compute_hkdf_result_or).value();
  EXPECT_THAT(streaming_result_or.value(),
              Eq(util::SecretDataAsStringView(compute_hkdf_result)));
}

TEST(HkdfStreamingPrf, TestFipsOnly) {
  if (!IsFipsModeEnabled()) {
    GTEST_SKIP() << "Only supported in FIPS-only mode";
  }

  HashType hash = SHA1;
  util::SecretData ikm = Random::GetRandomKeyBytes(123);
  std::string salt = Random::GetRandomBytes(234);
  std::string info = Random::GetRandomBytes(345);

  EXPECT_THAT(HkdfStreamingPrf::New(hash, std::move(ikm), salt).status(),
              StatusIs(absl::StatusCode::kInternal));
}
}  // namespace
}  // namespace subtle
}  // namespace tink
}  // namespace crypto
