diff --git a/fastpair/handshake/BUILD b/fastpair/handshake/BUILD index 488bf176..f099e941 100644 --- a/fastpair/handshake/BUILD +++ b/fastpair/handshake/BUILD @@ -19,12 +19,15 @@ cc_library( srcs = [ "fast_pair_data_encryptor_impl.cc", "fast_pair_gatt_service_client_impl.cc", + "fast_pair_handshake_impl.cc", ], hdrs = [ "fast_pair_data_encryptor.h", "fast_pair_data_encryptor_impl.h", "fast_pair_gatt_service_client.h", "fast_pair_gatt_service_client_impl.h", + "fast_pair_handshake.h", + "fast_pair_handshake_impl.h", ], visibility = [ "//:__subpackages__", @@ -54,6 +57,7 @@ cc_library( name = "test_support", hdrs = [ "fake_fast_pair_data_encryptor.h", + "fake_fast_pair_gatt_service_client.h", ], visibility = [ "//fastpair:__subpackages__", @@ -117,3 +121,24 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "fast_pair_handshake_impl_test", + size = "small", + srcs = [ + "fast_pair_handshake_impl_test.cc", + ], + shard_count = 16, + deps = [ + ":handshake", + "//fastpair/common", + "//fastpair/server_access:test_support", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/handshake/fake_fast_pair_gatt_service_client.h b/fastpair/handshake/fake_fast_pair_gatt_service_client.h new file mode 100644 index 00000000..2274034e --- /dev/null +++ b/fastpair/handshake/fake_fast_pair_gatt_service_client.h @@ -0,0 +1,78 @@ +// Copyright 2023 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 +// +// https://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. +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_ + +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/handshake/fast_pair_gatt_service_client.h" + +namespace nearby { +namespace fastpair { + +// This class fakes FastPairGattServiceClient and permits setting which +// PairFailure, if any, is run with the callback. +class FakeFastPairGattServiceClient : public FastPairGattServiceClient { + public: + void InitializeGattConnection( + absl::AnyInvocable)> + on_gatt_initialized_callback) override { + on_initialized_callback_ = std::move(on_gatt_initialized_callback); + } + + void WriteRequestAsync( + uint8_t message_type, uint8_t flags, absl::string_view provider_address, + absl::string_view seekers_address, + const FastPairDataEncryptor& fast_pair_data_encryptor, + WriteResponseCallback write_response_callback) override { + key_based_write_response_callback_ = std::move(write_response_callback); + } + void WritePasskeyAsync( + uint8_t message_type, uint32_t passkey, + const FastPairDataEncryptor& fast_pair_data_encryptor, + WriteResponseCallback write_response_callback) override { + passkey_write_response_callback_ = std::move(write_response_callback); + } + void RunOnGattClientInitializedCallback( + std::optional failure = absl::nullopt) { + std::move(on_initialized_callback_)(failure); + } + + void RunWriteResponseCallback( + absl::string_view value, + std::optional failure = absl::nullopt) { + std::move(key_based_write_response_callback_)(value, failure); + } + + void RunWritePasskeyCallback( + absl::string_view value, + std::optional failure = absl::nullopt) { + std::move(passkey_write_response_callback_)(value, failure); + } + + private: + absl::AnyInvocable)> on_initialized_callback_; + WriteResponseCallback key_based_write_response_callback_; + WriteResponseCallback passkey_write_response_callback_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_ diff --git a/fastpair/handshake/fast_pair_handshake.h b/fastpair/handshake/fast_pair_handshake.h new file mode 100644 index 00000000..c42da161 --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake.h @@ -0,0 +1,85 @@ +// Copyright 2023 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 +// +// https://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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_H_ + +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/handshake/fast_pair_data_encryptor.h" +#include "fastpair/handshake/fast_pair_gatt_service_client.h" + +namespace nearby { +namespace fastpair { + +// This class performs the Fast Pair handshake procedure upon creation and +// calls |on_complete| once finished. It also exposes the +// |FastPairDataEncryptor| and |FastPairGattServiceClient| instances that were +// used during the handshake. +// +// The procedure steps are as follows: +// 1. Create a GATT connection to the device. +// 2. Create a data encryptor instance with the generated keys. +// 3. Write the Key-Based Pairing Request to the characteristic +// (https://developers.google.com/nearby/fast-pair/spec#table1.1) +// 4. Decrypt the response. +// 5. Validate response. +// 6. Set classic address field on |FastPairDevice| instance. +// 7. Complete. + +class FastPairHandshake { + public: + using OnCompleteCallback = absl::AnyInvocable failure)>; + + FastPairHandshake( + FastPairDevice& device, OnCompleteCallback on_complete_cb, + std::unique_ptr data_encryptor, + std::unique_ptr gatt_service_client) + : device_(&device), + on_complete_callback_(std::move(on_complete_cb)), + fast_pair_data_encryptor_(std::move(data_encryptor)), + fast_pair_gatt_service_client_(std::move(gatt_service_client)) {} + + FastPairHandshake(const FastPairHandshake&) = delete; + FastPairHandshake& operator=(const FastPairHandshake&) = delete; + virtual ~FastPairHandshake() = default; + + bool completed_successfully() { return completed_successfully_; } + + FastPairDataEncryptor* fast_pair_data_encryptor() { + return fast_pair_data_encryptor_.get(); + } + + FastPairGattServiceClient* fast_pair_gatt_service_client() { + return fast_pair_gatt_service_client_.get(); + } + + protected: + bool completed_successfully_ = false; + FastPairDevice* device_; + OnCompleteCallback on_complete_callback_; + std::unique_ptr fast_pair_data_encryptor_; + std::unique_ptr fast_pair_gatt_service_client_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_H_ diff --git a/fastpair/handshake/fast_pair_handshake_impl.cc b/fastpair/handshake/fast_pair_handshake_impl.cc new file mode 100644 index 00000000..a2d6a2dc --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_impl.cc @@ -0,0 +1,131 @@ +// Copyright 2023 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 +// +// https://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 "fastpair/handshake/fast_pair_handshake_impl.h" + +#include +#include +#include +#include + +#include "absl/functional/bind_front.h" +#include "fastpair/common/constant.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/handshake/fast_pair_data_encryptor_impl.h" +#include "fastpair/handshake/fast_pair_gatt_service_client_impl.h" +#include "internal/base/bluetooth_address.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace fastpair { + +FastPairHandshakeImpl::FastPairHandshakeImpl(FastPairDevice& device, + OnCompleteCallback on_complete) + : FastPairHandshake(device, std::move(on_complete), nullptr, nullptr) { + fast_pair_gatt_service_client_ = + FastPairGattServiceClientImpl::Factory::Create(device); + fast_pair_gatt_service_client_->InitializeGattConnection( + [this](std::optional failure) { + OnGattClientInitializedCallback(failure); + }); +} + +void FastPairHandshakeImpl::OnGattClientInitializedCallback( + std::optional failure) { + if (failure.has_value()) { + NEARBY_LOGS(WARNING) << __func__ + << ": Failed to init gatt client with failure = " + << failure.value(); + std::move(on_complete_callback_)(*device_, failure.value()); + return; + } + + NEARBY_LOGS(INFO) + << __func__ + << ": Fast Pair GATT service client initialization successful."; + FastPairDataEncryptorImpl::Factory::CreateAsync( + *device_, absl::bind_front( + &FastPairHandshakeImpl::OnDataEncryptorCreateAsync, this)); +} + +void FastPairHandshakeImpl::OnDataEncryptorCreateAsync( + std::unique_ptr fast_pair_data_encryptor) { + if (!fast_pair_data_encryptor) { + NEARBY_LOGS(WARNING) << __func__ + << ": Failed to create Fast Pair Data Encryptor."; + std::move(on_complete_callback_)(*device_, + PairFailure::kDataEncryptorRetrieval); + return; + } + + fast_pair_data_encryptor_ = std::move(fast_pair_data_encryptor); + NEARBY_LOGS(INFO) << __func__ << ": Beginning key-based pairing protocol"; + fast_pair_gatt_service_client_->WriteRequestAsync( + /*message_type=*/kKeyBasedPairingType, + /*flags=*/kInitialOrSubsequentFlags, + /*provider_address=*/device_->GetBleAddress(), + /*seekers_address=*/"", *fast_pair_data_encryptor_, + [this](absl::string_view response, std::optional failure) { + OnWriteResponse(response, failure); + }); +} + +void FastPairHandshakeImpl::OnWriteResponse( + absl::string_view response, std::optional failure) { + if (failure.has_value()) { + NEARBY_LOGS(WARNING) + << __func__ + << ": Failed during key-based pairing protocol with failure = " + << failure.value(); + std::move(on_complete_callback_)(*device_, failure.value()); + return; + } + + NEARBY_LOGS(INFO) << __func__ << ": Successfully wrote response."; + + if (response.size() != kAesBlockByteSize) { + NEARBY_LOGS(WARNING) + << __func__ << ": Handshake failed because of incorrect response size."; + std::move(on_complete_callback_)( + *device_, PairFailure::kKeybasedPairingResponseDecryptFailure); + return; + } + + std::vector response_bytes(response.begin(), response.end()); + fast_pair_data_encryptor_->ParseDecryptResponse( + response_bytes, [this](std::optional response) { + OnParseDecryptedResponse(response); + }); +} + +void FastPairHandshakeImpl::OnParseDecryptedResponse( + std::optional& response) { + if (!response.has_value()) { + NEARBY_LOGS(WARNING) << __func__ + << ": Missing decrypted response from parse."; + std::move(on_complete_callback_)( + *device_, PairFailure::kKeybasedPairingResponseDecryptFailure); + return; + } + NEARBY_LOGS(INFO) << __func__ + << ": Successfully decrypted and parsed response."; + + device_->set_public_address( + device::CanonicalizeBluetoothAddress(response->address_bytes)); + completed_successfully_ = true; + std::move(on_complete_callback_)(*device_, absl::nullopt); +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/handshake/fast_pair_handshake_impl.h b/fastpair/handshake/fast_pair_handshake_impl.h new file mode 100644 index 00000000..c41bbc30 --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_impl.h @@ -0,0 +1,47 @@ +// Copyright 2023 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 +// +// https://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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_IMPL_H_ + +#include +#include + +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/crypto/decrypted_response.h" +#include "fastpair/handshake/fast_pair_handshake.h" + +namespace nearby { +namespace fastpair { + +class FastPairHandshakeImpl : public FastPairHandshake { + public: + FastPairHandshakeImpl(FastPairDevice& device, OnCompleteCallback on_complete); + FastPairHandshakeImpl(const FastPairHandshakeImpl&) = delete; + FastPairHandshakeImpl& operator=(const FastPairHandshakeImpl&) = delete; + + private: + void OnGattClientInitializedCallback(std::optional failure); + void OnDataEncryptorCreateAsync( + std::unique_ptr fast_pair_data_encryptor); + void OnWriteResponse(absl::string_view response, + std::optional failure); + void OnParseDecryptedResponse(std::optional& response); +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_IMPL_H_ diff --git a/fastpair/handshake/fast_pair_handshake_impl_test.cc b/fastpair/handshake/fast_pair_handshake_impl_test.cc new file mode 100644 index 00000000..47dac67c --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_impl_test.cc @@ -0,0 +1,268 @@ +// Copyright 2023 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 +// +// https://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 "fastpair/handshake/fast_pair_handshake_impl.h" + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/escaping.h" +#include "fastpair/common/constant.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/common/protocol.h" +#include "fastpair/handshake/fast_pair_gatt_service_client_impl.h" +#include "fastpair/server_access/fake_fast_pair_repository.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/medium_environment.h" + +namespace nearby { +namespace fastpair { + +namespace { +using Property = nearby::api::ble_v2::GattCharacteristic::Property; +using Permission = nearby::api::ble_v2::GattCharacteristic::Permission; +using ::nearby::api::ble_v2::GattCharacteristic; + +constexpr absl::string_view kMetadataId("718c17"); +constexpr absl::string_view kProviderAddress("11:22:33:44:55:66"); +constexpr absl::string_view kPublicAddress("5E:3F:45:61:C3:32"); +constexpr absl::string_view kKeyBasedResponse("keybasedresponse"); +constexpr absl::string_view kWrongResponse("wrongresponse"); +constexpr absl::string_view kPublicAntiSpoof = + "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" + "0wVcljfT3XPoiy1fntlneziyLD5knDVAJSE+RM/zlPRP/Jg=="; +constexpr char kInvalidPublicAntiSpoof[] = "InvalidPublicAntiSpoof"; +constexpr std::array kRawResponseBytes = { + 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, + 0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6}; +constexpr Uuid kFastPairServiceUuid(0x0000FE2C00001000, 0x800000805F9B34FB); +constexpr Uuid kKeyBasedCharacteristicUuidV2(0xFE2C123483664814, + 0x8EB001DE32100BEA); +constexpr Uuid kPasskeyCharacteristicUuidV2(0xFE2C123583664814, + 0x8EB001DE32100BEA); +// Length of advertisement byte should be 16 +constexpr absl::string_view kKeyBasedCharacteristicAdvertisementByte = + "keyBasedCharacte"; +constexpr absl::string_view kPasskeyharacteristicAdvertisementByte = + "passkeyCharacter"; +constexpr absl::Duration kGattOperationTimeout = absl::Seconds(15); +} // namespace + +class FastPairHandshakeImplTest : public testing::Test { + public: + void SetUp() override { + repository_ = std::make_unique(); + env_.Start({.use_simulated_clock = true}); + BluetoothAdapter adapter; + BleV2Medium ble(adapter); + gatt_server_ = ble.StartGattServer(/*ServerGattConnectionCallback=*/{}); + } + + void TearDown() override { + repository_.reset(); + key_based_characteristic_ = std::nullopt; + passkey_characteristic_ = std::nullopt; + gatt_server_->Stop(); + gatt_server_.reset(); + handshake_.reset(); + env_.Stop(); + } + + void InsertCorrectGattCharacteristics() { + key_based_characteristic_ = gatt_server_->CreateCharacteristic( + kFastPairServiceUuid, kKeyBasedCharacteristicUuidV2, permissions_, + properties_); + gatt_server_->UpdateCharacteristic( + key_based_characteristic_.value(), + ByteArray(std::string(kKeyBasedCharacteristicAdvertisementByte))); + + passkey_characteristic_ = gatt_server_->CreateCharacteristic( + kFastPairServiceUuid, kPasskeyCharacteristicUuidV2, permissions_, + properties_); + gatt_server_->UpdateCharacteristic( + passkey_characteristic_.value(), + ByteArray(std::string(kPasskeyharacteristicAdvertisementByte))); + } + + void SetUpFastPairRepository() { + proto::Device metadata; + std::string decoded_key; + absl::Base64Unescape(kPublicAntiSpoof, &decoded_key); + metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key); + repository_->SetFakeMetadata(kMetadataId, metadata); + } + + void FailedFastPairRepository() { + proto::Device metadata; + std::string decoded_key; + absl::Base64Unescape(kInvalidPublicAntiSpoof, &decoded_key); + metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key); + repository_->SetFakeMetadata(kMetadataId, metadata); + } + + absl::Status TriggerKeyBasedGattChanged() { + FastPairDataEncryptor* fast_pair_data_encryptor_ = + handshake_->fast_pair_data_encryptor(); + std::array encryptedResponse = + fast_pair_data_encryptor_->EncryptBytes(kRawResponseBytes); + std::array response; + std::copy(encryptedResponse.begin(), encryptedResponse.end(), + response.begin()); + return gatt_server_->NotifyCharacteristicChanged( + key_based_characteristic_.value(), false, ByteArray(response)); + } + + absl::Status TriggerKeyBasedGattChangedWithWrongResponse() { + return gatt_server_->NotifyCharacteristicChanged( + key_based_characteristic_.value(), false, + ByteArray(std::string(kKeyBasedResponse))); + } + + absl::Status TriggerKeyBasedGattChangedWithWrongSizeResponse() { + return gatt_server_->NotifyCharacteristicChanged( + key_based_characteristic_.value(), false, + ByteArray(std::string(kWrongResponse))); + } + + protected: + std::unique_ptr handshake_; + + private: + MediumEnvironment& env_{MediumEnvironment::Instance()}; + std::unique_ptr gatt_server_; + std::optional key_based_characteristic_; + std::optional passkey_characteristic_; + std::unique_ptr repository_; + Property properties_ = Property::kWrite | Property::kNotify; + Permission permissions_ = Permission::kWrite; +}; + +TEST_F(FastPairHandshakeImplTest, Success) { + SetUpFastPairRepository(); + InsertCorrectGattCharacteristics(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(device.public_address(), kPublicAddress); + EXPECT_FALSE(failure.has_value()); + latch.CountDown(); + }); + EXPECT_OK(TriggerKeyBasedGattChanged()); + latch.Await(); + EXPECT_TRUE(handshake_->completed_successfully()); +} + +TEST_F(FastPairHandshakeImplTest, GattError) { + SetUpFastPairRepository(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(failure.value(), PairFailure::kCreateGattConnection); + latch.CountDown(); + }); + latch.Await(); + EXPECT_FALSE(handshake_->completed_successfully()); +} + +TEST_F(FastPairHandshakeImplTest, DataEncryptorCreateError) { + FailedFastPairRepository(); + InsertCorrectGattCharacteristics(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(failure.value(), PairFailure::kDataEncryptorRetrieval); + latch.CountDown(); + }); + latch.Await(); + EXPECT_FALSE(handshake_->completed_successfully()); +} + +TEST_F(FastPairHandshakeImplTest, WriteResponseError) { + SetUpFastPairRepository(); + InsertCorrectGattCharacteristics(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(failure.value(), + PairFailure::kKeyBasedPairingResponseTimeout); + latch.CountDown(); + }); + SystemClock::Sleep(kGattOperationTimeout); + latch.Await(); + EXPECT_FALSE(handshake_->completed_successfully()); +} + +TEST_F(FastPairHandshakeImplTest, WriteResponseWrongSize) { + SetUpFastPairRepository(); + InsertCorrectGattCharacteristics(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(failure.value(), + PairFailure::kKeybasedPairingResponseDecryptFailure); + latch.CountDown(); + }); + EXPECT_OK(TriggerKeyBasedGattChangedWithWrongSizeResponse()); + latch.Await(); + EXPECT_FALSE(handshake_->completed_successfully()); +} + +TEST_F(FastPairHandshakeImplTest, ParseResponseError) { + SetUpFastPairRepository(); + InsertCorrectGattCharacteristics(); + FastPairDevice device(kMetadataId, kProviderAddress, + Protocol::kFastPairInitialPairing); + CountDownLatch latch(1); + handshake_ = std::make_unique( + device, + [&](FastPairDevice& callback_device, std::optional failure) { + EXPECT_EQ(&device, &callback_device); + EXPECT_EQ(failure.value(), + PairFailure::kKeybasedPairingResponseDecryptFailure); + latch.CountDown(); + }); + EXPECT_OK(TriggerKeyBasedGattChangedWithWrongResponse()); + latch.Await(); + EXPECT_FALSE(handshake_->completed_successfully()); +} +} // namespace fastpair +} // namespace nearby