mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
Implement fast pair handshake
PiperOrigin-RevId: 521872662
This commit is contained in:
committed by
Copybara-Service
parent
f8f9c4fd4e
commit
d01bbcc2cd
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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 <optional>
|
||||
#include <utility>
|
||||
|
||||
#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<void(std::optional<PairFailure>)>
|
||||
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<PairFailure> failure = absl::nullopt) {
|
||||
std::move(on_initialized_callback_)(failure);
|
||||
}
|
||||
|
||||
void RunWriteResponseCallback(
|
||||
absl::string_view value,
|
||||
std::optional<PairFailure> failure = absl::nullopt) {
|
||||
std::move(key_based_write_response_callback_)(value, failure);
|
||||
}
|
||||
|
||||
void RunWritePasskeyCallback(
|
||||
absl::string_view value,
|
||||
std::optional<PairFailure> failure = absl::nullopt) {
|
||||
std::move(passkey_write_response_callback_)(value, failure);
|
||||
}
|
||||
|
||||
private:
|
||||
absl::AnyInvocable<void(std::optional<PairFailure>)> 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_
|
||||
@@ -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 <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#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<void(
|
||||
FastPairDevice& device, std::optional<PairFailure> failure)>;
|
||||
|
||||
FastPairHandshake(
|
||||
FastPairDevice& device, OnCompleteCallback on_complete_cb,
|
||||
std::unique_ptr<FastPairDataEncryptor> data_encryptor,
|
||||
std::unique_ptr<FastPairGattServiceClient> 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<FastPairDataEncryptor> fast_pair_data_encryptor_;
|
||||
std::unique_ptr<FastPairGattServiceClient> fast_pair_gatt_service_client_;
|
||||
};
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_H_
|
||||
@@ -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 <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<PairFailure> failure) {
|
||||
OnGattClientInitializedCallback(failure);
|
||||
});
|
||||
}
|
||||
|
||||
void FastPairHandshakeImpl::OnGattClientInitializedCallback(
|
||||
std::optional<PairFailure> 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<FastPairDataEncryptor> 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<PairFailure> failure) {
|
||||
OnWriteResponse(response, failure);
|
||||
});
|
||||
}
|
||||
|
||||
void FastPairHandshakeImpl::OnWriteResponse(
|
||||
absl::string_view response, std::optional<PairFailure> 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<uint8_t> response_bytes(response.begin(), response.end());
|
||||
fast_pair_data_encryptor_->ParseDecryptResponse(
|
||||
response_bytes, [this](std::optional<DecryptedResponse> response) {
|
||||
OnParseDecryptedResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
void FastPairHandshakeImpl::OnParseDecryptedResponse(
|
||||
std::optional<DecryptedResponse>& 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
|
||||
@@ -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 <memory>
|
||||
#include <optional>
|
||||
|
||||
#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<PairFailure> failure);
|
||||
void OnDataEncryptorCreateAsync(
|
||||
std::unique_ptr<FastPairDataEncryptor> fast_pair_data_encryptor);
|
||||
void OnWriteResponse(absl::string_view response,
|
||||
std::optional<PairFailure> failure);
|
||||
void OnParseDecryptedResponse(std::optional<DecryptedResponse>& response);
|
||||
};
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_IMPL_H_
|
||||
@@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#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<uint8_t, kAesBlockByteSize> 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<FakeFastPairRepository>();
|
||||
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<uint8_t, kAesBlockByteSize> encryptedResponse =
|
||||
fast_pair_data_encryptor_->EncryptBytes(kRawResponseBytes);
|
||||
std::array<char, kAesBlockByteSize> 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<FastPairHandshake> handshake_;
|
||||
|
||||
private:
|
||||
MediumEnvironment& env_{MediumEnvironment::Instance()};
|
||||
std::unique_ptr<GattServer> gatt_server_;
|
||||
std::optional<GattCharacteristic> key_based_characteristic_;
|
||||
std::optional<GattCharacteristic> passkey_characteristic_;
|
||||
std::unique_ptr<FakeFastPairRepository> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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<FastPairHandshakeImpl>(
|
||||
device,
|
||||
[&](FastPairDevice& callback_device, std::optional<PairFailure> 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
|
||||
Reference in New Issue
Block a user