mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
Add account_key_filter to see the current device is a saved device
PiperOrigin-RevId: 533197294
This commit is contained in:
committed by
Copybara-Service
parent
2a9af32a44
commit
b7d2ee0c45
@@ -3,6 +3,7 @@ licenses(["notice"])
|
||||
cc_library(
|
||||
name = "common",
|
||||
srcs = [
|
||||
"account_key_filter.cc",
|
||||
"battery_notification.cc",
|
||||
"fast_pair_device.cc",
|
||||
"fast_pair_http_result.cc",
|
||||
@@ -11,6 +12,7 @@ cc_library(
|
||||
],
|
||||
hdrs = [
|
||||
"account_key.h",
|
||||
"account_key_filter.h",
|
||||
"battery_notification.h",
|
||||
"constant.h",
|
||||
"fast_pair_device.h",
|
||||
@@ -31,6 +33,22 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "account_key_filter_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"account_key_filter_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":common",
|
||||
"//internal/platform/implementation/g3", # build_cleaner: keep
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fast_pair_device_test",
|
||||
size = "small",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
@@ -30,6 +31,9 @@ class AccountKey {
|
||||
public:
|
||||
AccountKey() = default;
|
||||
explicit AccountKey(absl::string_view bytes) : bytes_(bytes) {}
|
||||
explicit AccountKey(const std::vector<uint8_t> bytes) {
|
||||
bytes_ = std::string(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
static AccountKey CreateRandomKey() {
|
||||
std::string key(kAccountKeySize, 0);
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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/common/account_key_filter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "fastpair/common/battery_notification.h"
|
||||
#include "fastpair/common/non_discoverable_advertisement.h"
|
||||
#include "internal/crypto/sha2.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
namespace {
|
||||
constexpr int kBitsInByte = 8;
|
||||
|
||||
// SASS enabled peripherals create their Bloom filters with the first byte
|
||||
// equals to either `the account key in use`
|
||||
// or `the most recently used account key`
|
||||
// see this spec:
|
||||
// https://developers.google.com/nearby/fast-pair/early-access/specifications/extensions/sass#SassInUseAccountKey
|
||||
constexpr uint8_t kRecentlyUsedByte = 0x05;
|
||||
constexpr uint8_t kInUseByte = 0x06;
|
||||
constexpr uint8_t kShowUi = 0b00110011;
|
||||
constexpr uint8_t kHideUi = 0b00110100;
|
||||
|
||||
// Helper to AccountKeyFilter::IsAccountKeyInFilter().
|
||||
// Performs the test to see if |data| is in |bit_sets|, a Bloom filter.
|
||||
bool AccountKeyFilterChecker(const std::vector<uint8_t>& data,
|
||||
const std::vector<uint8_t>& bit_sets) {
|
||||
std::array<uint8_t, 32> hashed = crypto::SHA256Hash(data);
|
||||
|
||||
// Iterate over the hashed input in 4 byte increments, combine those 4
|
||||
// bytes into an unsigned int and use it as the index into our
|
||||
// |bit_sets|.
|
||||
for (size_t i = 0; i < hashed.size(); i += 4) {
|
||||
uint32_t hash = uint32_t{hashed[i]} << 24 | uint32_t{hashed[i + 1]} << 16 |
|
||||
uint32_t{hashed[i + 2]} << 8 | hashed[i + 3];
|
||||
|
||||
size_t num_bits = bit_sets.size() * kBitsInByte;
|
||||
size_t n = hash % num_bits;
|
||||
size_t byte_index = floor(n / kBitsInByte);
|
||||
size_t bit_index = n % kBitsInByte;
|
||||
bool is_set = (bit_sets[byte_index] >> bit_index) & 0x01;
|
||||
|
||||
if (!is_set) return false;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__ << " The accountkey is possibly in set.";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AccountKeyFilter::AccountKeyFilter(
|
||||
const NonDiscoverableAdvertisement& advertisement)
|
||||
: bit_sets_(advertisement.account_key_filter) {
|
||||
salt_values_.resize(advertisement.salt.size());
|
||||
std::copy(advertisement.salt.begin(), advertisement.salt.end(),
|
||||
salt_values_.begin());
|
||||
|
||||
// If the advertisement contains battery information, then that information
|
||||
// was also appended to the account keys to generate the filter. We need to
|
||||
// do the same when checking for matches, so save the values in salt_values_
|
||||
// for that purpose later.
|
||||
if (advertisement.battery_notification) {
|
||||
salt_values_.push_back(advertisement.battery_notification->type ==
|
||||
BatteryNotification::Type::kShowUi
|
||||
? kShowUi
|
||||
: kHideUi);
|
||||
for (auto battery_info :
|
||||
advertisement.battery_notification->battery_infos) {
|
||||
salt_values_.push_back(battery_info.ToByte());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccountKeyFilter::AccountKeyFilter(
|
||||
const std::vector<uint8_t>& account_key_filter_bytes,
|
||||
const std::vector<uint8_t>& salt_values)
|
||||
: bit_sets_(account_key_filter_bytes), salt_values_(salt_values) {}
|
||||
|
||||
bool AccountKeyFilter::IsPossiblyInSet(const AccountKey& account_key) {
|
||||
if (!account_key.Ok()) {
|
||||
NEARBY_LOGS(INFO) << __func__ << " Invalid account key.";
|
||||
return false;
|
||||
}
|
||||
if (bit_sets_.empty()) return false;
|
||||
// We first need to append the salt value to the input (see
|
||||
// https://developers.google.com/nearby/fast-pair/spec#AccountKeyFilter).
|
||||
std::vector<uint8_t> data(account_key.GetAsBytes().begin(),
|
||||
account_key.GetAsBytes().end());
|
||||
for (auto& byte : salt_values_) data.push_back(byte);
|
||||
|
||||
// We need to try account keys with different first bytes in case
|
||||
// the peripheral is SASS per
|
||||
// https://developers.google.com/nearby/fast-pair/early-access/specifications/extensions/sass#SassAdvertisingPayload
|
||||
if (AccountKeyFilterChecker(data, bit_sets_)) {
|
||||
return true;
|
||||
}
|
||||
data[0] = kRecentlyUsedByte;
|
||||
if (AccountKeyFilterChecker(data, bit_sets_)) {
|
||||
return true;
|
||||
}
|
||||
data[0] = kInUseByte;
|
||||
return AccountKeyFilterChecker(data, bit_sets_);
|
||||
}
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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_COMMON_ACCOUNT_KEY_FILTER_H_
|
||||
#define THIRD_PARTY_NEARBY_FASTPAIR_COMMON_ACCOUNT_KEY_FILTER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "fastpair/common/account_key.h"
|
||||
#include "fastpair/common/non_discoverable_advertisement.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
// Class which represents a Fast Pair Account Key account_key_filter
|
||||
// https://developers.google.com/nearby/fast-pair/specifications/service/provider#AccountKeyFilter
|
||||
class AccountKeyFilter {
|
||||
public:
|
||||
explicit AccountKeyFilter(const NonDiscoverableAdvertisement& advertisement);
|
||||
AccountKeyFilter(const std::vector<uint8_t>& account_key_filter_bytes,
|
||||
const std::vector<uint8_t>& salt_values);
|
||||
~AccountKeyFilter() = default;
|
||||
|
||||
// Returns true if the `account_key` is possibly in the account key set
|
||||
// defined by the filter.
|
||||
// Return false if `account_key` is definitely not in set.
|
||||
bool IsPossiblyInSet(const AccountKey& account_key);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> bit_sets_;
|
||||
std::vector<uint8_t> salt_values_;
|
||||
};
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_COMMON_ACCOUNT_KEY_FILTER_H_
|
||||
@@ -0,0 +1,135 @@
|
||||
// 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/common/account_key_filter.h"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "fastpair/common/battery_notification.h"
|
||||
#include "fastpair/common/non_discoverable_advertisement.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
namespace {
|
||||
// Test data comes from:
|
||||
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
|
||||
|
||||
class AccountKeyFilterTest : public testing::Test {
|
||||
protected:
|
||||
const std::vector<uint8_t> salt_{0xC7, 0xC8};
|
||||
const std::vector<uint8_t> account_key_1_{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
|
||||
0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
|
||||
0xCC, 0xDD, 0xEE, 0xFF};
|
||||
const std::vector<uint8_t> account_key_2_{0x11, 0x11, 0x22, 0x22, 0x33, 0x33,
|
||||
0x44, 0x44, 0x55, 0x55, 0x66, 0x66,
|
||||
0x77, 0x77, 0x88, 0x88};
|
||||
|
||||
const std::vector<uint8_t> filter_1_{0x02, 0x0C, 0x80, 0x2A};
|
||||
const std::vector<uint8_t> filter_2_{0x84, 0x4A, 0x62, 0x20, 0x8B};
|
||||
const std::vector<uint8_t> filter_1_and_2_{0x84, 0x4A, 0x62, 0x20, 0x8B};
|
||||
|
||||
const std::vector<uint8_t> battery_data_{0b00110011, 0b01000000, 0b01000000,
|
||||
0b01000000};
|
||||
|
||||
const std::vector<uint8_t> filter_1_with_battery_{0x01, 0x01, 0x46, 0x0A};
|
||||
const std::vector<uint8_t> filter_2_with_battery_{0x46, 0x15, 0x24, 0xD0,
|
||||
0x08};
|
||||
};
|
||||
|
||||
TEST_F(AccountKeyFilterTest, ConstrutorWithNonDiscoverableAdvertisement) {
|
||||
NonDiscoverableAdvertisement non_discoverable_advertisement(
|
||||
filter_1_, NonDiscoverableAdvertisement::Type::kShowUi, salt_, {});
|
||||
EXPECT_TRUE(AccountKeyFilter(non_discoverable_advertisement)
|
||||
.IsPossiblyInSet(AccountKey(account_key_1_)));
|
||||
NonDiscoverableAdvertisement non_discoverable_advertisement_with_battery(
|
||||
filter_1_with_battery_, NonDiscoverableAdvertisement::Type::kShowUi,
|
||||
salt_,
|
||||
BatteryNotification::FromBytes({0b01000000, 0b01000000, 0b01000000},
|
||||
BatteryNotification::Type::kShowUi));
|
||||
EXPECT_TRUE(AccountKeyFilter(non_discoverable_advertisement_with_battery)
|
||||
.IsPossiblyInSet(AccountKey(account_key_1_)));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, EmptyAccountKeyFilter) {
|
||||
AccountKeyFilter filter({}, {});
|
||||
|
||||
EXPECT_FALSE(filter.IsPossiblyInSet(AccountKey(account_key_1_)));
|
||||
EXPECT_FALSE(filter.IsPossiblyInSet(AccountKey(account_key_2_)));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, EmptyAccountKey) {
|
||||
EXPECT_FALSE(
|
||||
AccountKeyFilter(filter_1_, salt_).IsPossiblyInSet(AccountKey("")));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, SingleAccountKey) {
|
||||
EXPECT_TRUE(AccountKeyFilter(filter_1_, salt_)
|
||||
.IsPossiblyInSet(AccountKey(AccountKey(account_key_1_))));
|
||||
EXPECT_TRUE(AccountKeyFilter(filter_2_, salt_)
|
||||
.IsPossiblyInSet(AccountKey(account_key_2_)));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, TwoAccountKeys) {
|
||||
AccountKeyFilter filter(filter_1_and_2_, salt_);
|
||||
EXPECT_TRUE(filter.IsPossiblyInSet(AccountKey(account_key_1_)));
|
||||
EXPECT_TRUE(filter.IsPossiblyInSet(AccountKey(account_key_2_)));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, MissingAccountKey) {
|
||||
const std::vector<uint8_t> bytes{0x12, 0x22, 0x33, 0x44, 0x55, 0x66,
|
||||
0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
|
||||
0xCC, 0xDD, 0xEE, 0xFF};
|
||||
AccountKey account_key(bytes);
|
||||
EXPECT_FALSE(AccountKeyFilter(filter_1_, salt_).IsPossiblyInSet(account_key));
|
||||
EXPECT_FALSE(
|
||||
AccountKeyFilter(filter_1_and_2_, salt_).IsPossiblyInSet(account_key));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, AccountKeyWithBatteryData) {
|
||||
std::vector<uint8_t> salt_1 = salt_;
|
||||
for (auto& byte : battery_data_) salt_1.push_back(byte);
|
||||
|
||||
EXPECT_TRUE(AccountKeyFilter(filter_1_with_battery_, salt_1)
|
||||
.IsPossiblyInSet(AccountKey(account_key_1_)));
|
||||
|
||||
std::vector<uint8_t> salt_2 = salt_;
|
||||
for (auto& byte : battery_data_) salt_2.push_back(byte);
|
||||
|
||||
EXPECT_TRUE(AccountKeyFilter(filter_2_with_battery_, salt_2)
|
||||
.IsPossiblyInSet(AccountKey(account_key_2_)));
|
||||
}
|
||||
|
||||
TEST_F(AccountKeyFilterTest, SassEnabledPeripheral) {
|
||||
// Value source: b/243855406#comment24
|
||||
const std::vector<uint8_t> bytes{0x06, 0x3F, 0xC1, 0x8C, 0x63, 0xDC,
|
||||
0x75, 0x1A, 0xE8, 0x1A, 0xCF, 0x65,
|
||||
0x10, 0x15, 0x1D, 0xB0};
|
||||
AccountKey account_key_3(bytes);
|
||||
const std::vector<uint8_t> filter4{0x19, 0x23, 0x50, 0xE8, 0x37,
|
||||
0x68, 0xF0, 0x65, 0x22};
|
||||
const std::vector<uint8_t> salt4{0xD7, 0xDE};
|
||||
const std::vector<uint8_t> batteryData4{0x33, 0xE4, 0xE4, 0x64};
|
||||
std::vector<uint8_t> salt_values{};
|
||||
salt_values.insert(salt_values.end(), salt4.begin(), salt4.end());
|
||||
salt_values.insert(salt_values.end(), batteryData4.begin(),
|
||||
batteryData4.end());
|
||||
EXPECT_TRUE(
|
||||
AccountKeyFilter(filter4, salt_values).IsPossiblyInSet(account_key_3));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
Reference in New Issue
Block a user