// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/trusted_vault/download_keys_response_handler.h"

#include <vector>

#include "base/strings/string_number_conversions.h"
#include "components/trusted_vault/proto/vault.pb.h"
#include "components/trusted_vault/proto_string_bytes_conversion.h"
#include "components/trusted_vault/securebox.h"
#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
#include "components/trusted_vault/trusted_vault_connection.h"
#include "components/trusted_vault/trusted_vault_crypto.h"
#include "components/trusted_vault/trusted_vault_server_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace trusted_vault {

namespace {

using testing::ElementsAre;
using testing::Eq;
using testing::IsEmpty;

const char kEncodedPrivateKey[] =
    "49e052293c29b5a50b0013eec9d030ac2ad70a42fe093be084264647cb04e16f";

std::unique_ptr<SecureBoxKeyPair> MakeTestKeyPair() {
  std::vector<uint8_t> private_key_bytes;
  bool success = base::HexStringToBytes(kEncodedPrivateKey, &private_key_bytes);
  DCHECK(success);
  return SecureBoxKeyPair::CreateByPrivateKeyImport(private_key_bytes);
}

void AddSecurityDomainMembership(
    const std::string security_domain_path,
    const SecureBoxPublicKey& member_public_key,
    const std::vector<std::vector<uint8_t>>& trusted_vault_keys,
    const std::vector<int>& trusted_vault_keys_versions,
    const std::vector<std::vector<uint8_t>>& signing_keys,
    trusted_vault_pb::SecurityDomainMember* member) {
  DCHECK(member);
  DCHECK_EQ(trusted_vault_keys.size(), trusted_vault_keys_versions.size());
  DCHECK_EQ(trusted_vault_keys.size(), signing_keys.size());

  trusted_vault_pb::SecurityDomainMember::SecurityDomainMembership* membership =
      member->add_memberships();
  membership->set_security_domain(std::move(security_domain_path));
  for (size_t i = 0; i < trusted_vault_keys.size(); ++i) {
    trusted_vault_pb::SharedMemberKey* shared_key = membership->add_keys();
    shared_key->set_epoch(trusted_vault_keys_versions[i]);
    AssignBytesToProtoString(
        ComputeTrustedVaultWrappedKey(member_public_key, trusted_vault_keys[i]),
        shared_key->mutable_wrapped_key());

    if (!signing_keys[i].empty()) {
      trusted_vault_pb::RotationProof* rotation_proof =
          membership->add_rotation_proofs();
      rotation_proof->set_new_epoch(trusted_vault_keys_versions[i]);
      AssignBytesToProtoString(ComputeRotationProof(
                                   /*trusted_vault_key=*/trusted_vault_keys[i],
                                   /*prev_trusted_vault_key=*/signing_keys[i]),
                               rotation_proof->mutable_rotation_proof());
    }
  }
}

std::string CreateGetSecurityDomainMemberResponseWithSyncMembership(
    const std::vector<std::vector<uint8_t>>& trusted_vault_keys,
    const std::vector<int>& trusted_vault_keys_versions,
    const std::vector<std::vector<uint8_t>>& signing_keys) {
  trusted_vault_pb::SecurityDomainMember member;
  AddSecurityDomainMembership(
      GetSecurityDomainPath(SecurityDomainId::kChromeSync),
      MakeTestKeyPair()->public_key(), trusted_vault_keys,
      trusted_vault_keys_versions, signing_keys, &member);
  return member.SerializeAsString();
}

class DownloadKeysResponseHandlerTest : public testing::Test {
 public:
  DownloadKeysResponseHandlerTest()
      : handler_(SecurityDomainId::kChromeSync,
                 TrustedVaultKeyAndVersion(kKnownTrustedVaultKey,
                                           kKnownTrustedVaultKeyVersion),
                 MakeTestKeyPair()) {}

  ~DownloadKeysResponseHandlerTest() override = default;

  const DownloadKeysResponseHandler& handler() const { return handler_; }

  const int kKnownTrustedVaultKeyVersion = 5;
  const std::vector<uint8_t> kKnownTrustedVaultKey = {1, 2, 3, 4};
  const std::vector<uint8_t> kTrustedVaultKey1 = {1, 2, 3, 5};
  const std::vector<uint8_t> kTrustedVaultKey2 = {1, 2, 3, 6};
  const std::vector<uint8_t> kTrustedVaultKey3 = {1, 2, 3, 7};

 private:
  const DownloadKeysResponseHandler handler_;
};

// All HttpStatuses except kSuccess should end up in kOtherError, kNetworkError
// or kMemberNotFound reporting.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleHttpErrors) {
  EXPECT_THAT(
      handler()
          .ProcessResponse(
              /*http_status=*/TrustedVaultRequest::HttpStatus::kNotFound,
              /*response_body=*/std::string())
          .status,
      Eq(TrustedVaultDownloadKeysStatus::kMemberNotFound));
  EXPECT_THAT(
      handler()
          .ProcessResponse(
              /*http_status=*/TrustedVaultRequest::HttpStatus::kBadRequest,
              /*response_body=*/std::string())
          .status,
      Eq(TrustedVaultDownloadKeysStatus::kOtherError));
  EXPECT_THAT(
      handler()
          .ProcessResponse(
              /*http_status=*/TrustedVaultRequest::HttpStatus::kConflict,
              /*response_body=*/std::string())
          .status,
      Eq(TrustedVaultDownloadKeysStatus::kOtherError));
  EXPECT_THAT(
      handler()
          .ProcessResponse(
              /*http_status=*/TrustedVaultRequest::HttpStatus::kNetworkError,
              /*response_body=*/std::string())
          .status,
      Eq(TrustedVaultDownloadKeysStatus::kNetworkError));
  EXPECT_THAT(
      handler()
          .ProcessResponse(
              /*http_status=*/TrustedVaultRequest::HttpStatus::kOtherError,
              /*response_body=*/std::string())
          .status,
      Eq(TrustedVaultDownloadKeysStatus::kOtherError));
}

// Simplest legitimate case of key rotation, server side state corresponds to
// kKnownTrustedVaultKey -> kTrustedVaultKey1 key chain.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleSingleKeyRotation) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
              /*signing_keys=*/{{}, kKnownTrustedVaultKey}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kSuccess));
  EXPECT_THAT(processed_response.downloaded_keys,
              ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1));
  EXPECT_THAT(processed_response.last_key_version,
              Eq(kKnownTrustedVaultKeyVersion + 1));
}

// Multiple key rotations may happen while client is offline, server-side key
// chain is kKnownTrustedVaultKey -> kTrustedVaultKey1 -> kTrustedVaultKey2.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleMultipleKeyRotations) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/
              {kKnownTrustedVaultKey, kTrustedVaultKey1, kTrustedVaultKey2},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1,
               kKnownTrustedVaultKeyVersion + 2},
              /*signing_keys=*/{{}, kKnownTrustedVaultKey, kTrustedVaultKey1}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kSuccess));
  EXPECT_THAT(
      processed_response.downloaded_keys,
      ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1, kTrustedVaultKey2));
  EXPECT_THAT(processed_response.last_key_version,
              Eq(kKnownTrustedVaultKeyVersion + 2));
}

// Server can already clean-up kKnownTrustedVaultKey, but it might still be
// possible to validate the key-chain.
// Full key chain is: kKnownTrustedVaultKey -> kTrustedVaultKey1 ->
// kTrustedVaultKey2.
// Server-side key chain is: kTrustedVaultKey1 -> kTrustedVaultKey2.
TEST_F(DownloadKeysResponseHandlerTest,
       ShouldHandleAbsenseOfKnownKeyWhenKeyChainIsRecoverable) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/
              {kTrustedVaultKey1, kTrustedVaultKey2},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion + 1,
               kKnownTrustedVaultKeyVersion + 2},
              /*signing_keys=*/
              {kKnownTrustedVaultKey, kTrustedVaultKey1}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kSuccess));
  EXPECT_THAT(processed_response.downloaded_keys,
              ElementsAre(kTrustedVaultKey1, kTrustedVaultKey2));
  EXPECT_THAT(processed_response.last_key_version,
              Eq(kKnownTrustedVaultKeyVersion + 2));
}

// Server can already clean-up kKnownTrustedVaultKey and the following key. In
// this case client state is not sufficient to silently download keys and
// kKeyProofsVerificationFailed should be reported.
// Possible full key chain is: kKnownTrustedVaultKey -> kTrustedVaultKey1 ->
// kTrustedVaultKey2 -> kTrustedVaultKey3.
// Server side key chain is: kTrustedVaultKey2 -> kTrustedVaultKey3.
TEST_F(DownloadKeysResponseHandlerTest,
       ShouldHandleAbsenseOfKnownKeyWhenKeyChainIsNotRecoverable) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/
              {kTrustedVaultKey2, kTrustedVaultKey3},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion + 2,
               kKnownTrustedVaultKeyVersion + 3},
              /*signing_keys=*/
              {kTrustedVaultKey1, kTrustedVaultKey2}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed));
  EXPECT_THAT(processed_response.downloaded_keys, IsEmpty());
}

// The test populates undecryptable/corrupted |wrapped_key| field, handler
// should return kMembershipCorrupted to allow client to restore the member by
// re-registration.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleUndecryptableKey) {
  trusted_vault_pb::SecurityDomainMember member;
  AddSecurityDomainMembership(
      GetSecurityDomainPath(SecurityDomainId::kChromeSync),
      MakeTestKeyPair()->public_key(),
      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
      /*trusted_vault_keys_versions=*/
      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
      /*signing_keys=*/{{}, kKnownTrustedVaultKey}, &member);

  // Corrupt wrapped key corresponding to kTrustedVaultKey1.
  member.mutable_memberships(0)->mutable_keys(1)->set_wrapped_key(
      "undecryptable_key");

  EXPECT_THAT(handler()
                  .ProcessResponse(
                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                      /*response_body=*/member.SerializeAsString())
                  .status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipCorrupted));
}

// The test populates invalid |rotation_proof| field for the single key
// rotation. kTrustedVaultKey1 is expected to be signed with
// kKnownTrustedVaultKey, but instead it's signed with kTrustedVaultKey2.
TEST_F(DownloadKeysResponseHandlerTest,
       ShouldHandleInvalidKeyProofOnSingleKeyRotation) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
              /*signing_keys=*/{{}, kTrustedVaultKey2}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed));
  EXPECT_THAT(processed_response.downloaded_keys, IsEmpty());
}

// The test populates invalid |rotation_proof| field for intermediate key when
// multiple key rotations have happened.
// kTrustedVaultKey1 is expected to be signed with kKnownTrustedVaultKey, but
// instead it's signed with kTrustedVaultKey2.
TEST_F(DownloadKeysResponseHandlerTest,
       ShouldHandleInvalidKeyProofOnMultipleKeyRotations) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1,
                                      kTrustedVaultKey2},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1,
               kKnownTrustedVaultKeyVersion + 2},
              /*signing_keys=*/{{}, kTrustedVaultKey2, kTrustedVaultKey1}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed));
  EXPECT_THAT(processed_response.downloaded_keys, IsEmpty());
}

// In this scenario client already has most recent trusted vault key.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfNewKeys) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kKnownTrustedVaultKey},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion},
              /*signing_keys=*/{{}}));
  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kNoNewKeys));
  EXPECT_THAT(processed_response.downloaded_keys,
              ElementsAre(kKnownTrustedVaultKey));
}

// Tests handling the situation, when response isn't a valid serialized
// SecurityDomainMemberProto proto.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleCorruptedResponseProto) {
  EXPECT_THAT(handler()
                  .ProcessResponse(
                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                      /*response_body=*/"corrupted_proto")
                  .status,
              Eq(TrustedVaultDownloadKeysStatus::kOtherError));
}

// Client expects that the sync security domain membership exists, but the
// response indicates it doesn't by having no memberships.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfMemberships) {
  EXPECT_THAT(handler()
                  .ProcessResponse(
                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                      /*response_body=*/trusted_vault_pb::SecurityDomainMember()
                          .SerializeAsString())
                  .status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipNotFound));
}

// Same as above, but there is a different security domain membership.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfSyncMembership) {
  trusted_vault_pb::SecurityDomainMember member;
  AddSecurityDomainMembership(
      "other_domain", MakeTestKeyPair()->public_key(),
      /*trusted_vault_keys=*/{kTrustedVaultKey1},
      /*trusted_vault_keys_versions=*/{kKnownTrustedVaultKeyVersion + 1},
      /*signing_keys=*/{kKnownTrustedVaultKey}, &member);

  EXPECT_THAT(handler()
                  .ProcessResponse(
                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                      /*response_body=*/member.SerializeAsString())
                  .status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipNotFound));
}

TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleEmptyMembership) {
  trusted_vault_pb::SecurityDomainMember member;
  AddSecurityDomainMembership(
      GetSecurityDomainPath(SecurityDomainId::kChromeSync),
      MakeTestKeyPair()->public_key(),
      /*trusted_vault_keys=*/{},
      /*trusted_vault_keys_versions=*/{},
      /*signing_keys=*/{}, &member);

  EXPECT_THAT(handler()
                  .ProcessResponse(
                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                      /*response_body=*/member.SerializeAsString())
                  .status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipEmpty));
}

TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAllSecurityDomains) {
  for (const SecurityDomainId security_domain : kAllSecurityDomainIdValues) {
    trusted_vault_pb::SecurityDomainMember member;
    AddSecurityDomainMembership(
        GetSecurityDomainPath(security_domain), MakeTestKeyPair()->public_key(),
        /*trusted_vault_keys=*/{kTrustedVaultKey1},
        /*trusted_vault_keys_versions=*/{kKnownTrustedVaultKeyVersion + 1},
        /*signing_keys=*/{kKnownTrustedVaultKey}, &member);

    const DownloadKeysResponseHandler::ProcessedResponse processed_response =
        DownloadKeysResponseHandler(
            security_domain,
            TrustedVaultKeyAndVersion(kKnownTrustedVaultKey,
                                      kKnownTrustedVaultKeyVersion),
            MakeTestKeyPair())
            .ProcessResponse(
                /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
                /*response_body=*/member.SerializeAsString());

    EXPECT_THAT(processed_response.status,
                Eq(TrustedVaultDownloadKeysStatus::kSuccess));
    EXPECT_THAT(processed_response.downloaded_keys,
                ElementsAre(kTrustedVaultKey1));
    EXPECT_THAT(processed_response.last_key_version,
                Eq(kKnownTrustedVaultKeyVersion + 1));
  }
}

// Tests handling presence of other security domain memberships.
TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleMultipleSecurityDomains) {
  trusted_vault_pb::SecurityDomainMember member;
  AddSecurityDomainMembership(
      "other_domain", MakeTestKeyPair()->public_key(),
      /*trusted_vault_keys=*/{kTrustedVaultKey1},
      /*trusted_vault_keys_versions=*/{kKnownTrustedVaultKeyVersion + 1},
      /*signing_keys=*/{{}}, &member);

  // Note: sync security domain membership is different by having correct
  // rotation proof.
  AddSecurityDomainMembership(
      GetSecurityDomainPath(SecurityDomainId::kChromeSync),
      MakeTestKeyPair()->public_key(),
      /*trusted_vault_keys=*/{kTrustedVaultKey1},
      /*trusted_vault_keys_versions=*/{kKnownTrustedVaultKeyVersion + 1},
      /*signing_keys=*/{kKnownTrustedVaultKey}, &member);

  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/member.SerializeAsString());

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kSuccess));
  EXPECT_THAT(processed_response.downloaded_keys,
              ElementsAre(kTrustedVaultKey1));
  EXPECT_THAT(processed_response.last_key_version,
              Eq(kKnownTrustedVaultKeyVersion + 1));
}

TEST_F(DownloadKeysResponseHandlerTest,
       ShouldHandleSameVersionWithDifferentKey) {
  // Server sends a different key at same version, with no rotation proof.
  const std::vector<uint8_t> kDifferentKey = {9, 9, 9, 9};
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kDifferentKey},
              /*trusted_vault_keys_versions=*/{kKnownTrustedVaultKeyVersion},
              /*signing_keys=*/{{}}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipCorrupted));
  EXPECT_THAT(processed_response.downloaded_keys, IsEmpty());
}

TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleVersionRegression) {
  // Server sends key with a lower version, no rotation proof.
  const std::vector<uint8_t> kLowerVersionKey = {9, 9, 9, 9};
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/{kLowerVersionKey},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion - 1},
              /*signing_keys=*/{{}}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kMembershipCorrupted));
  EXPECT_THAT(processed_response.downloaded_keys, IsEmpty());
}

TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleRotationsToNewKeys) {
  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
      handler().ProcessResponse(
          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
          /*response_body=*/
          CreateGetSecurityDomainMemberResponseWithSyncMembership(
              /*trusted_vault_keys=*/
              {kTrustedVaultKey1, kTrustedVaultKey2},
              /*trusted_vault_keys_versions=*/
              {kKnownTrustedVaultKeyVersion + 1,
               kKnownTrustedVaultKeyVersion + 2},
              /*signing_keys=*/
              {kKnownTrustedVaultKey, kTrustedVaultKey1}));

  EXPECT_THAT(processed_response.status,
              Eq(TrustedVaultDownloadKeysStatus::kSuccess));
  EXPECT_THAT(processed_response.downloaded_keys,
              ElementsAre(kTrustedVaultKey1, kTrustedVaultKey2));
  EXPECT_THAT(processed_response.last_key_version,
              Eq(kKnownTrustedVaultKeyVersion + 2));
}

}  // namespace

}  // namespace trusted_vault
