chromium/ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor_impl_unittest.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor_impl.h"

#include <stddef.h>
#include <stdint.h>

#include <array>

#include "ash/quick_pair/common/protocol.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor.h"
#include "ash/quick_pair/repository/fake_fast_pair_repository.h"
#include "base/base64.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/services/quick_pair/fast_pair_data_parser.h"
#include "chromeos/ash/services/quick_pair/mock_quick_pair_process_manager.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_manager.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_manager_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const std::array<uint8_t, kBlockSizeBytes> kResponseBytes = {
    0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D,
    0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6};

const std::array<uint8_t, kBlockSizeBytes> kPasskeyBytes = {
    0x02, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D,
    0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6};

const std::vector<uint8_t> kAccountKey = {0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F,
                                          0xF7, 0xB6, 0xCF, 0x5E, 0x3F, 0x45,
                                          0x61, 0xC3, 0x32, 0x1D};

const char kPublicAntiSpoof[] =
    "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+"
    "0wVcljfT3XPoiy1fntlneziyLD5knDVAJSE+RM/zlPRP/Jg==";
const char kInvalidPublicAntiSpoof[] = "";

constexpr char kValidModelId[] = "718c17";
constexpr char kTestAddress[] = "test_address";

}  // namespace

namespace ash {
namespace quick_pair {

// This controls the protocol we use to test.
using TestParam = bool;

class FastPairDataEncryptorImplTest : public testing::TestWithParam<TestParam> {
 public:
  void SetUp() override {
    data_parser_ = std::make_unique<ash::quick_pair::FastPairDataParser>(
        fast_pair_data_parser_.InitWithNewPipeAndPassReceiver());

    data_parser_remote_.Bind(std::move(fast_pair_data_parser_),
                             task_environment_.GetMainThreadTaskRunner());

    process_manager_ = std::make_unique<MockQuickPairProcessManager>();
    quick_pair_process::SetProcessManager(process_manager_.get());
  }

  void TearDown() override { data_encryptor_.reset(); }

  void FailedSetUpNoMetadata() {
    repository_ = std::make_unique<FakeFastPairRepository>();
    // Not using the param here to control the type of device because only
    // the kFastPairInitial protocol can fail.
    device_ = base::MakeRefCounted<Device>(kValidModelId, kTestAddress,
                                           Protocol::kFastPairInitial);
    FastPairDataEncryptorImpl::Factory::CreateAsync(
        device_, base::BindOnce(
                     &FastPairDataEncryptorImplTest::OnDataEncryptorCreateAsync,
                     weak_ptr_factory_.GetWeakPtr()));
  }

  void SuccessfulSetUp(const std::vector<uint8_t> account_key) {
    repository_ = std::make_unique<FakeFastPairRepository>();
    nearby::fastpair::Device metadata;

    std::string decoded_key;
    base::Base64Decode(kPublicAntiSpoof, &decoded_key);
    metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key);
    repository_->SetFakeMetadata(kValidModelId, metadata);

    // The param controls which protocol we use.
    if (GetParam()) {
      device_ = base::MakeRefCounted<Device>(kValidModelId, kTestAddress,
                                             Protocol::kFastPairInitial);
    } else {
      device_ = base::MakeRefCounted<Device>(kValidModelId, kTestAddress,
                                             Protocol::kFastPairSubsequent);
    }
    device_->set_account_key(account_key);

    FastPairDataEncryptorImpl::Factory::CreateAsync(
        device_, base::BindOnce(
                     &FastPairDataEncryptorImplTest::OnDataEncryptorCreateAsync,
                     weak_ptr_factory_.GetWeakPtr()));
  }

  void SuccessfulSetUpToTestPublicKey() {
    repository_ = std::make_unique<FakeFastPairRepository>();
    nearby::fastpair::Device metadata;

    std::string decoded_key;
    base::Base64Decode(kPublicAntiSpoof, &decoded_key);
    metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key);
    repository_->SetFakeMetadata(kValidModelId, metadata);

    // Not using the param here to control the type of device because the
    // public key expectations differ for protocols
    device_ = base::MakeRefCounted<Device>(kValidModelId, kTestAddress,
                                           Protocol::kFastPairInitial);

    FastPairDataEncryptorImpl::Factory::CreateAsync(
        device_, base::BindOnce(
                     &FastPairDataEncryptorImplTest::OnDataEncryptorCreateAsync,
                     weak_ptr_factory_.GetWeakPtr()));
  }

  void FailedSetUpNoKeyPair() {
    repository_ = std::make_unique<FakeFastPairRepository>();
    nearby::fastpair::Device metadata;

    std::string decoded_key;
    base::Base64Decode(kInvalidPublicAntiSpoof, &decoded_key);
    metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key);
    repository_->SetFakeMetadata(kValidModelId, metadata);

    // Not using the param here to control the type of device because only
    // the kFastPairInitial protocol can fail..
    device_ = base::MakeRefCounted<Device>(kValidModelId, kTestAddress,
                                           Protocol::kFastPairInitial);
    FastPairDataEncryptorImpl::Factory::CreateAsync(
        device_, base::BindOnce(
                     &FastPairDataEncryptorImplTest::OnDataEncryptorCreateAsync,
                     weak_ptr_factory_.GetWeakPtr()));
  }

  void OnDataEncryptorCreateAsync(
      std::unique_ptr<FastPairDataEncryptor> fast_pair_data_encryptor) {
    data_encryptor_ = std::move(fast_pair_data_encryptor);
  }

  const std::array<uint8_t, kBlockSizeBytes> EncryptBytes() {
    return data_encryptor_->EncryptBytes(kResponseBytes);
  }

  void ParseDecryptedResponse() {
    const std::array<uint8_t, kBlockSizeBytes> bytes =
        data_encryptor_->EncryptBytes(kResponseBytes);

    data_encryptor_->ParseDecryptedResponse(
        std::vector<uint8_t>(bytes.begin(), bytes.end()),
        base::BindOnce(
            &FastPairDataEncryptorImplTest::ParseDecryptedResponseCallback,
            weak_ptr_factory_.GetWeakPtr()));
  }

  void ParseDecryptedResponseInvalidBytes() {
    const std::array<uint8_t, kBlockSizeBytes> bytes =
        data_encryptor_->EncryptBytes(kResponseBytes);

    data_encryptor_->ParseDecryptedResponse(
        std::vector<uint8_t>(bytes.begin() + 3, bytes.end()),
        base::BindOnce(
            &FastPairDataEncryptorImplTest::ParseDecryptedResponseCallback,
            weak_ptr_factory_.GetWeakPtr()));
  }

  void ParseDecryptedResponseCallback(
      const std::optional<DecryptedResponse>& response) {
    response_ = response;
  }

  void ParseDecryptedPasskey() {
    const std::array<uint8_t, kBlockSizeBytes> bytes =
        data_encryptor_->EncryptBytes(kPasskeyBytes);

    data_encryptor_->ParseDecryptedPasskey(
        std::vector<uint8_t>(bytes.begin(), bytes.end()),
        base::BindOnce(
            &FastPairDataEncryptorImplTest::ParseDecryptedPasskeyCallback,
            weak_ptr_factory_.GetWeakPtr()));
  }

  void ParseDecryptedPasskeyInvalidBytes() {
    const std::array<uint8_t, kBlockSizeBytes> bytes =
        data_encryptor_->EncryptBytes(kPasskeyBytes);

    data_encryptor_->ParseDecryptedPasskey(
        std::vector<uint8_t>(bytes.begin() + 3, bytes.end()),
        base::BindOnce(
            &FastPairDataEncryptorImplTest::ParseDecryptedPasskeyCallback,
            weak_ptr_factory_.GetWeakPtr()));
  }

  void ParseDecryptedPasskeyCallback(
      const std::optional<DecryptedPasskey>& passkey) {
    passkey_ = passkey;
  }

 protected:
  std::unique_ptr<FastPairDataEncryptor> data_encryptor_;
  std::optional<DecryptedResponse> response_ = std::nullopt;
  std::optional<DecryptedPasskey> passkey_ = std::nullopt;
  std::unique_ptr<MockQuickPairProcessManager> process_manager_;
  mojo::SharedRemote<ash::quick_pair::mojom::FastPairDataParser>
      data_parser_remote_;
  mojo::PendingRemote<ash::quick_pair::mojom::FastPairDataParser>
      fast_pair_data_parser_;
  std::unique_ptr<ash::quick_pair::FastPairDataParser> data_parser_;

 private:
  scoped_refptr<Device> device_;
  std::unique_ptr<FakeFastPairRepository> repository_;
  base::test::TaskEnvironment task_environment_;
  base::WeakPtrFactory<FastPairDataEncryptorImplTest> weak_ptr_factory_{this};
};

TEST_P(FastPairDataEncryptorImplTest, FailedSetUpNoMetadata) {
  EXPECT_FALSE(data_encryptor_);
  FailedSetUpNoMetadata();
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(data_encryptor_);
}

TEST_P(FastPairDataEncryptorImplTest, SuccessfulSetUp) {
  EXPECT_FALSE(data_encryptor_);
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
}

TEST_P(FastPairDataEncryptorImplTest, EncryptBytes) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_FALSE(EncryptBytes().empty());
}

TEST_P(FastPairDataEncryptorImplTest, ParseDecryptedResponse) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference);
  ParseDecryptedResponse();
  base::RunLoop().RunUntilIdle();
}

TEST_P(FastPairDataEncryptorImplTest, ParseDecryptedPasskey) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference);
  ParseDecryptedPasskey();
  base::RunLoop().RunUntilIdle();
}

TEST_P(FastPairDataEncryptorImplTest, ParseDecryptedPasskey_InvalidInputSize) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference).Times(0);
  ParseDecryptedPasskeyInvalidBytes();
  base::RunLoop().RunUntilIdle();
}

TEST_P(FastPairDataEncryptorImplTest, ParseDecryptedResponse_InvalidInputSize) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference).Times(0);
  ParseDecryptedResponseInvalidBytes();
  base::RunLoop().RunUntilIdle();
}

TEST_P(FastPairDataEncryptorImplTest, NoKeyPair) {
  FailedSetUpNoKeyPair();
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(data_encryptor_);
}

// TODO(crbug.com/40822900) flaky on ASan + LSan bots
#if defined(ADDRESS_SANITIZER) && defined(LEAK_SANITIZER)
#define MAYBE_ParseDecryptedPasskey_ProcessStopped \
  DISABLED_ParseDecryptedPasskey_ProcessStopped
#else
#define MAYBE_ParseDecryptedPasskey_ProcessStopped \
  ParseDecryptedPasskey_ProcessStopped
#endif
TEST_P(FastPairDataEncryptorImplTest,
       MAYBE_ParseDecryptedPasskey_ProcessStopped) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference)
      .WillRepeatedly(
          [&](QuickPairProcessManager::ProcessStoppedCallback callback) {
            std::move(callback).Run(
                QuickPairProcessManager::ShutdownReason::kCrash);
            return std::make_unique<
                QuickPairProcessManagerImpl::ProcessReferenceImpl>(
                data_parser_remote_, base::DoNothing());
          });
  ParseDecryptedPasskey();
  base::RunLoop().RunUntilIdle();
}

// TODO(crbug.com/40822900) flaky on ASan + LSan bots
#if defined(ADDRESS_SANITIZER) && defined(LEAK_SANITIZER)
#define MAYBE_ParseDecryptedResponse_ProcessStopped \
  DISABLED_ParseDecryptedResponse_ProcessStopped
#else
#define MAYBE_ParseDecryptedResponse_ProcessStopped \
  ParseDecryptedResponse_ProcessStopped
#endif
TEST_P(FastPairDataEncryptorImplTest,
       MAYBE_ParseDecryptedResponse_ProcessStopped) {
  SuccessfulSetUp(kAccountKey);
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference)
      .WillRepeatedly(
          [&](QuickPairProcessManager::ProcessStoppedCallback callback) {
            std::move(callback).Run(
                QuickPairProcessManager::ShutdownReason::kCrash);
            return std::make_unique<
                QuickPairProcessManagerImpl::ProcessReferenceImpl>(
                data_parser_remote_, base::DoNothing());
          });
  ParseDecryptedResponse();
  base::RunLoop().RunUntilIdle();
}

TEST_P(FastPairDataEncryptorImplTest, GetPublicKey) {
  SuccessfulSetUpToTestPublicKey();
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_encryptor_);
  EXPECT_CALL(*process_manager_, GetProcessReference);
  ParseDecryptedPasskey();
  base::RunLoop().RunUntilIdle();
  EXPECT_NE(data_encryptor_->GetPublicKey(), std::nullopt);
}

TEST_P(FastPairDataEncryptorImplTest, CreateAdditionalDataPacket_Success) {
  // // Values from Fast Pair Spec successful test:
  // //
  // https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#encode_personalized_name_to_additional_data_packet
  const std::string input_string = "Someone's Google Headphone";
  std::vector<uint8_t> input{input_string.begin(), input_string.end()};

  std::array<uint8_t, kPrivateKeyByteSize> secret_key = {
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};

  std::array<uint8_t, kNonceSizeBytes> nonce = {0x00, 0x01, 0x02, 0x03,
                                                0x04, 0x05, 0x06, 0x07};

  std::vector<uint8_t> expected = {
      0x55, 0xEC, 0x5E, 0x60, 0x55, 0xAF, 0x6E, 0x92, 0x00, 0x01, 0x02,
      0x03, 0x04, 0x05, 0x06, 0x07, 0xEE, 0x4A, 0x24, 0x83, 0x73, 0x80,
      0x52, 0xE4, 0x4E, 0x9B, 0x2A, 0x14, 0x5E, 0x5D, 0xDF, 0xAA, 0x44,
      0xB9, 0xE5, 0x53, 0x6A, 0xF4, 0x38, 0xE1, 0xE5, 0xC6};

  // Set up
  std::vector<uint8_t> secret_key_vec(secret_key.data(),
                                      secret_key.data() + secret_key.size());
  SuccessfulSetUp(secret_key_vec);

  // Test only if pairing protocol is Subsequent, which occurs in
  // SuccessfulSetUp() when GetParam() == 0, so that the device's account key is
  // used as the secret key in `data_encryptor_`.
  if (!GetParam()) {
    EXPECT_EQ(data_encryptor_->CreateAdditionalDataPacket(nonce, input),
              expected);
  }
}

TEST_P(FastPairDataEncryptorImplTest,
       CreateAdditionalDataPacket_EmptyData_NoCrash) {
  std::vector<uint8_t> input = {0x53, 0x6F, 0x6D, 0x65, 0x6F, 0x6E, 0x65,
                                0x27, 0x73, 0x20, 0x47, 0x6F, 0x6F, 0x67,
                                0x6C, 0x65, 0x20, 0x48, 0x65, 0x61, 0x64,
                                0x70, 0x68, 0x6F, 0x6E, 0x65};

  std::array<uint8_t, kPrivateKeyByteSize> secret_key = {
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};

  std::array<uint8_t, kNonceSizeBytes> nonce = {0x00, 0x01, 0x02, 0x03,
                                                0x04, 0x05, 0x06, 0x07};

  // Set up
  std::vector<uint8_t> secret_key_vec(secret_key.data(),
                                      secret_key.data() + secret_key.size());
  SuccessfulSetUp(secret_key_vec);

  // Test only if pairing protocol is Subsequent, which occurs in
  // SuccessfulSetUp() when GetParam() == 0, so that the device's account key is
  // used as the secret key in `data_encryptor_`.
  if (!GetParam()) {
    data_encryptor_->CreateAdditionalDataPacket(nonce, input);
  }
}

TEST_P(FastPairDataEncryptorImplTest, VerifyEncryptedAdditionalData_Success) {
  // Values from Fast Pair Spec successful test:
  // https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#hmac-sha256
  std::vector<uint8_t> encrypted_additional_data{
      0xEE, 0x4A, 0x24, 0x83, 0x73, 0x80, 0x52, 0xE4, 0x4E,
      0x9B, 0x2A, 0x14, 0x5E, 0x5D, 0xDF, 0xAA, 0x44, 0xB9,
      0xE5, 0x53, 0x6A, 0xF4, 0x38, 0xE1, 0xE5, 0xC6};

  std::array<uint8_t, kPrivateKeyByteSize> secret_key = {
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};

  std::array<uint8_t, kNonceSizeBytes> nonce = {0x00, 0x01, 0x02, 0x03,
                                                0x04, 0x05, 0x06, 0x07};

  std::array<uint8_t, kHmacVerifyLenBytes> expected = {0x55, 0xEC, 0x5E, 0x60,
                                                       0x55, 0xAF, 0x6E, 0x92};

  // Set up
  std::vector<uint8_t> secret_key_vec(secret_key.data(),
                                      secret_key.data() + secret_key.size());
  SuccessfulSetUp(secret_key_vec);

  // Test only if pairing protocol is Subsequent, which occurs in
  // SuccessfulSetUp() when GetParam() == 0, so that the device's account key is
  // used as the secret key in `data_encryptor_`.
  if (!GetParam()) {
    EXPECT_TRUE(data_encryptor_->VerifyEncryptedAdditionalData(
        expected, nonce, encrypted_additional_data));
  }
}

TEST_P(FastPairDataEncryptorImplTest, VerifyEncryptedAdditionalData_Failure) {
  // Values from Fast Pair Spec successful test:
  // https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#hmac-sha256
  std::vector<uint8_t> encrypted_additional_data{
      0xEE, 0x4A, 0x24, 0x83, 0x73, 0x80, 0x52, 0xE4, 0x4E,
      0x9B, 0x2A, 0x14, 0x5E, 0x5D, 0xDF, 0xAA, 0x44, 0xB9,
      0xE5, 0x53, 0x6A, 0xF4, 0x38, 0xE1, 0xE5, 0xC6};

  std::array<uint8_t, kPrivateKeyByteSize> secret_key = {
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
      0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};

  std::array<uint8_t, kNonceSizeBytes> nonce = {0x00, 0x01, 0x02, 0x03,
                                                0x04, 0x05, 0x06, 0x07};

  std::array<uint8_t, kHmacVerifyLenBytes> expected = {0x00, 0x01, 0x02, 0x03,
                                                       0x04, 0x05, 0x06, 0x07};

  // Set up
  std::vector<uint8_t> secret_key_vec(secret_key.data(),
                                      secret_key.data() + secret_key.size());
  SuccessfulSetUp(secret_key_vec);

  // Test only if pairing protocol is Subsequent, which occurs in
  // SuccessfulSetUp() when GetParam() == 0, so that the device's account key is
  // used as the secret key in `data_encryptor_`.
  if (!GetParam()) {
    EXPECT_FALSE(data_encryptor_->VerifyEncryptedAdditionalData(
        expected, nonce, encrypted_additional_data));
  }
}

INSTANTIATE_TEST_SUITE_P(FastPairDataEncryptorImplTest,
                         FastPairDataEncryptorImplTest,
                         testing::Bool());

}  // namespace quick_pair
}  // namespace ash