Add account_key_filter to see the current device is a saved device

PiperOrigin-RevId: 533197294
This commit is contained in:
Qin Wang
2023-05-18 11:39:05 -07:00
committed by Copybara-Service
parent 2a9af32a44
commit b7d2ee0c45
5 changed files with 331 additions and 0 deletions
+18
View File
@@ -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",
+4
View File
@@ -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);
+125
View File
@@ -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
+49
View File
@@ -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_
+135
View File
@@ -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