Update advertisement encoder/decoder

1. Update the API to accept the credentials on input instead of credential manager.
2. Update the test with values using the LDT encryption.

These changes allow us to have a more asynchronous implementaion.
When broadcasting we can:
1. Fetch the private credentials asynchronously.
2. Create the advertisement and start broadcasting when the credentials have been fetched.

When scanning we can:
1. Fetch public credentials asynchronously.
2. Start scanning for advertisements.
3. Update the decoder when new credentials are fetched.

PiperOrigin-RevId: 493998041
This commit is contained in:
Janusz Sobczak
2022-12-08 14:36:44 -08:00
committed by Copybara-Service
parent a8ca18f477
commit a92abac83a
21 changed files with 610 additions and 578 deletions
@@ -20,7 +20,7 @@
#include <vector>
#include "internal/platform/logging.h"
#include "internal/proto/credential.proto.h"
#include "internal/proto/credential.pb.h"
namespace location {
namespace nearby {
+5
View File
@@ -140,7 +140,9 @@ cc_test(
srcs = ["advertisement_decoder_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/platform/implementation/g3", # build_cleaner: keep
"//internal/proto:credential_cc_proto",
"//presence:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
@@ -154,7 +156,9 @@ cc_test(
srcs = ["advertisement_factory_test.cc"],
deps = [
":internal",
"//internal/platform:base",
"//internal/platform/implementation/g3", # build_cleaner: keep
"//internal/proto:credential_cc_proto",
"//presence:types",
"//presence/implementation/mediums",
"@com_github_protobuf_matchers//protobuf-matchers",
@@ -202,6 +206,7 @@ cc_test(
"//internal/platform:base",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
+117 -47
View File
@@ -26,9 +26,11 @@
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "internal/platform/logging.h"
#include "internal/proto/credential.proto.h"
#include "presence/data_element.h"
#include "presence/implementation/action_factory.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/ldt.h"
namespace nearby {
namespace presence {
@@ -87,6 +89,25 @@ bool IsEncryptedIdentity(int data_type) {
data_type == DataElement::kProvisionedIdentityFieldType;
}
bool IsIdentity(int data_type) {
return data_type == DataElement::kPublicIdentityFieldType ||
IsEncryptedIdentity(data_type);
}
internal::IdentityType GetIdentityType(int data_type) {
switch (data_type) {
case DataElement::kPrivateIdentityFieldType:
return internal::IDENTITY_TYPE_PRIVATE;
case DataElement::kTrustedIdentityFieldType:
return internal::IDENTITY_TYPE_TRUSTED;
case DataElement::kProvisionedIdentityFieldType:
return internal::IDENTITY_TYPE_PROVISIONED;
case DataElement::kPublicIdentityFieldType:
return internal::IDENTITY_TYPE_PUBLIC;
}
return internal::IDENTITY_TYPE_UNSPECIFIED;
}
// Returns the real length of a DE in v0 advertisement, which may be larger than
// the value in the header.
size_t GetDataElementTrueLength(uint8_t header) {
@@ -133,31 +154,6 @@ absl::StatusOr<DataElement> ParseDataElement(const absl::string_view input,
return DataElement(data_type, input.substr(start, length));
}
void DecodeBaseTxAndAction(absl::string_view serialized_action,
std::vector<DataElement>& output) {
if (serialized_action.size() < sizeof(uint8_t) ||
serialized_action.size() > sizeof(uint8_t) + sizeof(uint32_t)) {
NEARBY_LOGS(WARNING) << "Base NP action \'"
<< absl::BytesToHexString(serialized_action)
<< "\' has wrong length " << serialized_action.size()
<< " , expected size in range [1 - 5]";
return;
}
// TX power.
uint8_t tx_power = serialized_action[0];
output.emplace_back(DataElement::kTxPowerFieldType, tx_power);
// Action, 0-4 bytes in Big Endian order.
Action action = {.action = 0};
constexpr int kActionOffset = sizeof(uint8_t);
for (int i = 0; i < serialized_action.size() - kActionOffset; ++i) {
int offset = (sizeof(uint32_t) - 1 - i) * 8;
action.action |= serialized_action[i + kActionOffset] << offset;
}
ActionFactory::DecodeAction(action, output);
}
bool Contains(const std::vector<DataElement>& data_elements,
const DataElement& data_element) {
return std::find(data_elements.begin(), data_elements.end(), data_element) !=
@@ -189,15 +185,67 @@ bool ContainsAny(const std::vector<DataElement>& data_elements,
} // namespace
void AdvertisementDecoder::DecodeBaseTxAndAction(
absl::string_view serialized_action) {
if (serialized_action.size() < sizeof(uint8_t) ||
serialized_action.size() > sizeof(uint8_t) + sizeof(uint32_t)) {
NEARBY_LOGS(WARNING) << "Base NP action \'"
<< absl::BytesToHexString(serialized_action)
<< "\' has wrong length " << serialized_action.size()
<< " , expected size in range [1 - 5]";
return;
}
// TX power.
uint8_t tx_power = serialized_action[0];
decoded_advertisement_.data_elements.emplace_back(
DataElement::kTxPowerFieldType, tx_power);
// Action, 0-4 bytes in Big Endian order.
Action action = {.action = 0};
constexpr int kActionOffset = sizeof(uint8_t);
for (int i = 0; i < serialized_action.size() - kActionOffset; ++i) {
int offset = (sizeof(uint32_t) - 1 - i) * 8;
action.action |= serialized_action[i + kActionOffset] << offset;
}
ActionFactory::DecodeAction(action, decoded_advertisement_.data_elements);
}
absl::StatusOr<std::string> AdvertisementDecoder::DecryptLdt(
const std::vector<internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements) {
if (credentials.empty()) {
return absl::UnavailableError("No credentials");
}
for (const auto& credential : credentials) {
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(credential.authenticity_key(),
credential.metadata_encryption_key_tag());
if (encryptor.ok()) {
absl::StatusOr<std::string> result =
encryptor->DecryptAndVerify(data_elements, salt);
if (result.ok() && result->size() > kBaseMetadataSize) {
decoded_advertisement_.public_credential = credential;
decoded_advertisement_.metadata_key =
result->substr(0, kBaseMetadataSize);
return result->substr(kBaseMetadataSize);
}
}
}
return absl::UnavailableError(
"Couldn't decrypt the message with any credentials");
}
absl::Status AdvertisementDecoder::DecryptDataElements(
const DataElement& elem, std::vector<DataElement>& result) {
const DataElement& elem) {
if (elem.GetValue().size() <= kEncryptedIdentityAdditionalLength) {
return absl::OutOfRangeError(absl::StrFormat(
"Encrypted identity data element is too short - %d bytes",
elem.GetValue().size()));
}
absl::string_view salt = elem.GetValue().substr(0, kSaltSize);
result.emplace_back(DataElement::kSaltFieldType, salt);
decoded_advertisement_.data_elements.emplace_back(DataElement::kSaltFieldType,
salt);
absl::string_view encrypted = elem.GetValue().substr(kSaltSize);
absl::StatusOr<std::string> decrypted = Decrypt(salt, encrypted);
if (!decrypted.ok()) {
@@ -205,13 +253,7 @@ absl::Status AdvertisementDecoder::DecryptDataElements(
<< decrypted.status();
return decrypted.status();
}
if (decrypted->size() <= kBaseMetadataSize) {
return absl::OutOfRangeError(absl::StrFormat(
"Decrypted identity DE is too short - %d bytes. Expected more than %d",
decrypted->size(), kBaseMetadataSize));
}
result.emplace_back(elem.GetType(), decrypted->substr(0, kBaseMetadataSize));
size_t index = kBaseMetadataSize;
size_t index = 0;
while (index < decrypted->size()) {
absl::StatusOr<DataElement> internal_elem =
ParseDataElement(*decrypted, index);
@@ -222,9 +264,9 @@ absl::Status AdvertisementDecoder::DecryptDataElements(
}
if (internal_elem->GetType() == DataElement::kActionFieldType) {
// In v0 OTA format, this is a combined TX and Action DE.
DecodeBaseTxAndAction(internal_elem->GetValue(), result);
DecodeBaseTxAndAction(internal_elem->GetValue());
} else {
result.push_back(*std::move(internal_elem));
decoded_advertisement_.data_elements.push_back(*std::move(internal_elem));
}
}
return absl::OkStatus();
@@ -243,13 +285,17 @@ absl::StatusOr<std::string> AdvertisementDecoder::Decrypt(
continue;
}
absl::StatusOr<std::string> decrypted =
credential_manager_.DecryptDataElements(credentials, salt, encrypted);
DecryptLdt(credentials, salt, encrypted);
if (decrypted.ok()) {
return decrypted;
}
}
return credential_manager_.DecryptDataElements(scan_request_.account_name,
salt, encrypted);
if (credentials_ == nullptr) {
return absl::FailedPreconditionError("Missing credentials");
}
return DecryptLdt((*credentials_)[decoded_advertisement_.identity_type], salt,
encrypted);
}
void AdvertisementDecoder::AddBannedDataTypes() {
@@ -281,11 +327,14 @@ void AdvertisementDecoder::AddBannedDataTypes() {
}
}
absl::StatusOr<std::vector<DataElement>>
AdvertisementDecoder::DecodeAdvertisement(absl::string_view advertisement) {
absl::StatusOr<Advertisement> AdvertisementDecoder::DecodeAdvertisement(
absl::string_view advertisement) {
// Let's keep the result advertisement in a member variable to avoid passing
// it around all the time.
decoded_advertisement_ = Advertisement{};
std::vector<DataElement> result;
NEARBY_LOGS(VERBOSE) << "Advertisement: "
<< absl::BytesToHexString(advertisement);
NEARBY_LOGS(INFO) << "Advertisement: "
<< absl::BytesToHexString(advertisement);
if (advertisement.empty()) {
return absl::OutOfRangeError("Empty advertisement");
}
@@ -295,6 +344,7 @@ AdvertisementDecoder::DecodeAdvertisement(absl::string_view advertisement) {
return absl::UnimplementedError(absl::StrFormat(
"Advertisement version (%d) is not supported", version));
}
decoded_advertisement_.version = version;
size_t index = 1;
absl::StatusOr<std::string> decrypted;
while (index < advertisement.size()) {
@@ -312,21 +362,24 @@ AdvertisementDecoder::DecodeAdvertisement(absl::string_view advertisement) {
absl::StrFormat("Ignoring advertisement with data element type: %d",
elem->GetType()));
}
if (IsIdentity(elem->GetType())) {
decoded_advertisement_.identity_type = GetIdentityType(elem->GetType());
}
if (IsEncryptedIdentity(elem->GetType())) {
absl::Status status = DecryptDataElements(*elem, result);
absl::Status status = DecryptDataElements(*elem);
if (!status.ok()) {
return status;
}
} else {
if (elem->GetType() == DataElement::kActionFieldType) {
// In v0 OTA format, this is a combined TX and Action DE.
DecodeBaseTxAndAction(elem->GetValue(), result);
DecodeBaseTxAndAction(elem->GetValue());
} else {
result.push_back(*std::move(elem));
decoded_advertisement_.data_elements.push_back(*std::move(elem));
}
}
}
return result;
return std::move(decoded_advertisement_);
}
bool AdvertisementDecoder::MatchesScanFilter(
@@ -369,5 +422,22 @@ bool AdvertisementDecoder::MatchesScanFilter(
ContainsAll(data_elements, filter.extended_properties);
}
std::vector<CredentialSelector> AdvertisementDecoder::GetCredentialSelectors(
const ScanRequest& scan_request) {
std::vector<internal::IdentityType> all_types = {
internal::IDENTITY_TYPE_PRIVATE, internal::IDENTITY_TYPE_TRUSTED,
internal::IDENTITY_TYPE_PROVISIONED, internal::IDENTITY_TYPE_PUBLIC};
std::vector<CredentialSelector> selectors(all_types.size());
for (auto identity_type :
(scan_request.identity_types.empty() ? all_types
: scan_request.identity_types)) {
selectors.push_back(
CredentialSelector{.manager_app_id = scan_request.manager_app_id,
.account_name = scan_request.account_name,
.identity_type = identity_type});
}
return selectors;
}
} // namespace presence
} // namespace nearby
+36 -11
View File
@@ -18,31 +18,50 @@
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/die_if_null.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/credential_manager.h"
#include "presence/scan_request.h"
namespace nearby {
namespace presence {
struct Advertisement {
uint8_t version = 0;
std::vector<DataElement> data_elements;
absl::StatusOr<internal::PublicCredential> public_credential =
absl::NotFoundError("");
internal::IdentityType identity_type = internal::IDENTITY_TYPE_UNSPECIFIED;
std::string metadata_key;
};
// Decodes BLE NP advertisements
class AdvertisementDecoder {
public:
AdvertisementDecoder(CredentialManager* credential_manager,
ScanRequest scan_request)
: credential_manager_(*ABSL_DIE_IF_NULL(credential_manager)),
scan_request_(scan_request) {
AdvertisementDecoder(
ScanRequest scan_request,
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>* credentials)
: scan_request_(scan_request), credentials_(credentials) {
AddBannedDataTypes();
}
explicit AdvertisementDecoder(ScanRequest scan_request)
: scan_request_(scan_request) {
AddBannedDataTypes();
}
static std::vector<CredentialSelector> GetCredentialSelectors(
const ScanRequest& scan_request);
// Returns a list of Data Elements decoded from the advertisement.
// Returns an error if the advertisement is misformatted or if it couldn't be
// decrypted.
absl::StatusOr<std::vector<DataElement>> DecodeAdvertisement(
absl::StatusOr<Advertisement> DecodeAdvertisement(
absl::string_view advertisement);
// Returns true if the decoded advertisement in `data_elements` matches the
@@ -51,20 +70,26 @@ class AdvertisementDecoder {
private:
// Decrypts data elements stored inside encrypted `elem` and appends them to
// `result`.
absl::Status DecryptDataElements(const DataElement& elem,
std::vector<DataElement>& result);
// `decoded_advertisement_`.
absl::Status DecryptDataElements(const DataElement& elem);
absl::StatusOr<std::string> Decrypt(absl::string_view salt,
absl::string_view encrypted);
void DecodeBaseTxAndAction(absl::string_view serialized_action);
absl::StatusOr<std::string> DecryptLdt(
const std::vector<internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements);
void AddBannedDataTypes();
bool MatchesScanFilter(const std::vector<DataElement>& data_elements,
const PresenceScanFilter& filter);
bool MatchesScanFilter(const std::vector<DataElement>& data_elements,
const LegacyPresenceScanFilter& filter);
CredentialManager& credential_manager_;
ScanRequest scan_request_;
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>* credentials_ =
nullptr;
absl::flat_hash_set<int> banned_data_types_;
Advertisement decoded_advertisement_;
};
} // namespace presence
@@ -14,6 +14,7 @@
#include "presence/implementation/advertisement_decoder.h"
#include <array>
#include <string>
#include <vector>
@@ -21,9 +22,11 @@
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/credential_manager_impl.h"
#include "presence/scan_request.h"
#include "presence/scan_request_builder.h"
@@ -31,6 +34,8 @@ namespace nearby {
namespace presence {
namespace {
using ::location::nearby::ByteArray; // NOLINT
using ::nearby::internal::PublicCredential; // NOLINT
using ::testing::ElementsAre;
using ::protobuf_matchers::EqualsProto;
using ::testing::Matcher;
@@ -49,8 +54,8 @@ ScanRequest GetScanRequest() {
internal::IDENTITY_TYPE_PROVISIONED}};
}
ScanRequest GetScanRequest(
std::vector<nearby::internal::PublicCredential> credentials) {
#if USE_RUST_LDT == 1
ScanRequest GetScanRequest(std::vector<PublicCredential> credentials) {
LegacyPresenceScanFilter scan_filter = {.remote_public_credentials =
credentials};
return ScanRequestBuilder()
@@ -62,165 +67,152 @@ ScanRequest GetScanRequest(
.AddScanFilter(scan_filter)
.Build();
}
nearby::internal::PublicCredential GetPublicCredential() {
nearby::internal::PublicCredential public_credential;
public_credential.set_authenticity_key("authenticity key");
public_credential.set_metadata_encryption_key_tag(
"metadata encryption key tag");
PublicCredential GetPublicCredential() {
// Values copied from LDT tests
ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72,
184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152,
200, 193, 94, 107, 28, 194, 182, 32, 205, 57});
ByteArray known_mac({223, 185, 10, 31, 155, 31, 226, 141, 24, 187, 204,
165, 34, 64, 181, 204, 44, 203, 95, 141, 82, 137,
163, 203, 100, 235, 53, 65, 202, 97, 75, 180});
PublicCredential public_credential;
public_credential.set_authenticity_key(seed.AsStringView());
public_credential.set_metadata_encryption_key_tag(known_mac.AsStringView());
return public_credential;
}
class MockCredentialManager : public CredentialManagerImpl {
public:
MOCK_METHOD(absl::StatusOr<std::string>, DecryptDataElements,
(absl::string_view account_name, absl::string_view salt,
absl::string_view data_elements),
(override));
MOCK_METHOD(
absl::StatusOr<std::string>, DecryptDataElements,
(const std::vector<nearby::internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements),
(override));
};
TEST(AdvertisementDecoder, DecodeBaseNpPrivateAdvertisement) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
MockCredentialManager credential_manager;
EXPECT_CALL(credential_manager,
DecryptDataElements(
kAccountName, salt,
encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("37C1C2C31BEE")));
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>
credentials;
credentials[internal::IDENTITY_TYPE_PRIVATE].push_back(GetPublicCredential());
AdvertisementDecoder decoder(GetScanRequest(), &credentials);
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00614142F01112131415161718192021222F505152535455"));
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00414142ceb073b0e34f58d7dc6dea370783ac943fa5"));
ASSERT_OK(result);
EXPECT_THAT(
*result,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPrivateIdentityFieldType, metadata),
DataElement(DataElement::kModelIdFieldType,
absl::HexStringToBytes("C1C2C3")),
DataElement(DataElement::kBatteryFieldType,
absl::HexStringToBytes("EE"))));
EXPECT_EQ(result->metadata_key, metadata_key.AsStringView());
EXPECT_EQ(result->identity_type, internal::IDENTITY_TYPE_PRIVATE);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("08"))));
}
TEST(AdvertisementDecoder,
DecodeBaseNpPrivateAdvertisementWithPublicCredentialFromScanRequest) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
std::vector<nearby::internal::PublicCredential> credentials = {
GetPublicCredential()};
MockCredentialManager credential_manager;
EXPECT_CALL(
credential_manager,
DecryptDataElements(
Matcher<const std::vector<nearby::internal::PublicCredential>&>(
Pointwise(EqualsProto(), credentials)),
salt, encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("37C1C2C31BEE")));
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
std::vector<PublicCredential> credentials = {GetPublicCredential()};
AdvertisementDecoder decoder(&credential_manager,
GetScanRequest(credentials));
AdvertisementDecoder decoder(GetScanRequest(credentials));
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00614142F01112131415161718192021222F505152535455"));
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00414142ceb073b0e34f58d7dc6dea370783ac943fa5"));
ASSERT_OK(result);
EXPECT_THAT(
*result,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPrivateIdentityFieldType, metadata),
DataElement(DataElement::kModelIdFieldType,
absl::HexStringToBytes("C1C2C3")),
DataElement(DataElement::kBatteryFieldType,
absl::HexStringToBytes("EE"))));
EXPECT_EQ(result->metadata_key, metadata_key.AsStringView());
EXPECT_EQ(result->identity_type, internal::IDENTITY_TYPE_PRIVATE);
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("08"))));
}
TEST(AdvertisementDecoder, DecodeBaseNpTrustedAdvertisement) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
MockCredentialManager credential_manager;
EXPECT_CALL(credential_manager,
DecryptDataElements(
kAccountName, salt,
encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("3AC1C2C31BEE")));
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>
credentials;
credentials[internal::IDENTITY_TYPE_TRUSTED].push_back(GetPublicCredential());
AdvertisementDecoder decoder(GetScanRequest(), &credentials);
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00624142F01112131415161718192021222F505152535455"));
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00424142253536ac63191a96894d95f0ffa38b57cf9b"));
ASSERT_OK(result);
EXPECT_EQ(result->metadata_key, metadata_key.AsStringView());
EXPECT_EQ(result->identity_type, internal::IDENTITY_TYPE_TRUSTED);
EXPECT_THAT(
*result,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTrustedIdentityFieldType, metadata),
DataElement(DataElement::kConnectionStatusFieldType,
absl::HexStringToBytes("C1C2C3")),
DataElement(DataElement::kBatteryFieldType,
absl::HexStringToBytes("EE"))));
result->data_elements,
UnorderedElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("08")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("0A"))));
}
TEST(AdvertisementDecoder, DecodeBaseNpProvisionedAdvertisement) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
MockCredentialManager credential_manager;
EXPECT_CALL(credential_manager,
DecryptDataElements(
kAccountName, salt,
encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("59C1C2C3C4C5")));
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>
credentials;
credentials[internal::IDENTITY_TYPE_PROVISIONED].push_back(
GetPublicCredential());
AdvertisementDecoder decoder(GetScanRequest(), &credentials);
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00644142F01112131415161718192021222F505152535455"));
absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00444142253536ac63191a96894d95f0ffa38b57cf9b"));
ASSERT_OK(result);
EXPECT_EQ(result->metadata_key, metadata_key.AsStringView());
EXPECT_EQ(result->identity_type, internal::IDENTITY_TYPE_PROVISIONED);
EXPECT_THAT(
*result,
ElementsAre(
DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kProvisionedIdentityFieldType, metadata),
DataElement(DataElement::kAccountKeyDataFieldType,
absl::HexStringToBytes("C1C2C3C4C5"))));
result->data_elements,
UnorderedElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("05")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("08")),
DataElement(DataElement::kActionFieldType,
absl::HexStringToBytes("0A"))));
}
TEST(AdvertisementDecoder, InvalidEncryptedContent) {
std::string salt = "AB";
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
absl::flat_hash_map<internal::IdentityType,
std::vector<internal::PublicCredential>>
credentials;
credentials[internal::IDENTITY_TYPE_PRIVATE].push_back(GetPublicCredential());
AdvertisementDecoder decoder(GetScanRequest(), &credentials);
EXPECT_THAT(decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00414142f085d661ac8cb110e792e7faeb736294")),
StatusIs(absl::StatusCode::kOutOfRange));
}
#endif /*USE_RUST_LDT*/
TEST(AdvertisementDecoder, DecodeBaseNpPublicAdvertisement) {
const std::string salt = "AB";
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(
absl::HexStringToBytes("002041420337C1C2C31BEE"));
const absl::StatusOr<Advertisement> result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("002041420337C1C2C31BEE"));
ASSERT_OK(result);
EXPECT_EQ(result->identity_type, internal::IDENTITY_TYPE_PUBLIC);
EXPECT_EQ(result->version, 0);
EXPECT_THAT(
*result,
result->data_elements,
ElementsAre(DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPublicIdentityFieldType, ""),
DataElement(DataElement::kModelIdFieldType,
@@ -229,49 +221,15 @@ TEST(AdvertisementDecoder, DecodeBaseNpPublicAdvertisement) {
absl::HexStringToBytes("EE"))));
}
TEST(AdvertisementDecoder, DecodeBaseNpPrivateAdvertisementWithTxActionField) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
MockCredentialManager credential_manager;
EXPECT_CALL(credential_manager,
DecryptDataElements(
kAccountName, salt,
encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("4650B04180")));
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
const absl::StatusOr<std::vector<DataElement>> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00614142F01112131415161718192021222F505152535455"));
ASSERT_OK(result);
EXPECT_THAT(*result,
UnorderedElementsAre(
DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPrivateIdentityFieldType, metadata),
DataElement(DataElement::kTxPowerFieldType,
absl::HexStringToBytes("50")),
DataElement(DataElement::kContextTimestampFieldType,
absl::HexStringToBytes("0B")),
DataElement(DataElement(ActionBit::kEddystoneAction)),
DataElement(DataElement(ActionBit::kTapToTransferAction)),
DataElement(DataElement(ActionBit::kNearbyShareAction))));
}
TEST(AdvertisementDecoder, DecodeBaseNpWithTxActionField) {
std::string salt = "AB";
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
auto result = decoder.DecodeAdvertisement(
absl::HexStringToBytes("00204142034650B04180"));
EXPECT_OK(result);
EXPECT_THAT(*result,
EXPECT_THAT(result->data_elements,
UnorderedElementsAre(
DataElement(DataElement::kSaltFieldType, salt),
DataElement(DataElement::kPublicIdentityFieldType, ""),
@@ -286,10 +244,7 @@ TEST(AdvertisementDecoder, DecodeBaseNpWithTxActionField) {
TEST(AdvertisementDecoder,
ScanForEncryptedIdentityIgnoresPublicIdentityAdvertisement) {
std::string salt = "AB";
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(
&credential_manager,
{.account_name = std::string(kAccountName),
.identity_types = {internal::IDENTITY_TYPE_PRIVATE,
internal::IDENTITY_TYPE_TRUSTED,
@@ -301,8 +256,7 @@ TEST(AdvertisementDecoder,
}
TEST(AdvertisementDecoder, DecodeEddystone) {
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
std::string eddystone_id =
absl::HexStringToBytes("A0A1A2A3A4A5A6A7A8A9B0B1B2B3B4B5B6B7B8B9");
@@ -310,15 +264,15 @@ TEST(AdvertisementDecoder, DecodeEddystone) {
eddystone_id);
EXPECT_OK(result);
EXPECT_THAT(*result, ElementsAre(DataElement(
DataElement::kEddystoneIdFieldType, eddystone_id)));
EXPECT_THAT(result->data_elements,
ElementsAre(DataElement(DataElement::kEddystoneIdFieldType,
eddystone_id)));
}
// TODO(b/238214467): Add more negative tests
TEST(AdvertisementDecoder, UnsupportedDataElement) {
std::string valid_header_and_salt = absl::HexStringToBytes("00204142");
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
EXPECT_THAT(decoder.DecodeAdvertisement(valid_header_and_salt +
absl::HexStringToBytes("0D")),
@@ -326,8 +280,7 @@ TEST(AdvertisementDecoder, UnsupportedDataElement) {
}
TEST(AdvertisementDecoder, InvalidAdvertisementFieldTooShort) {
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
// 0x59 header means 5 bytes long Account Key Data but only 4 bytes follow.
EXPECT_THAT(
@@ -336,49 +289,25 @@ TEST(AdvertisementDecoder, InvalidAdvertisementFieldTooShort) {
}
TEST(AdvertisementDecoder, ZeroLengthPayload) {
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
// A action with type 0xA and no payload
const absl::StatusOr<std::vector<DataElement>> result =
const absl::StatusOr<Advertisement> result =
decoder.DecodeAdvertisement(absl::HexStringToBytes("000A"));
ASSERT_OK(result);
EXPECT_THAT(*result, ElementsAre(DataElement(0xA, "")));
EXPECT_THAT(result->data_elements, ElementsAre(DataElement(0xA, "")));
}
TEST(AdvertisementDecoder, EmptyAdvertisement) {
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
EXPECT_THAT(decoder.DecodeAdvertisement(""),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementDecoder, InvalidEncryptedContent) {
const std::string salt = "AB";
const std::string metadata =
absl::HexStringToBytes("1011121314151617181920212223");
const std::string encrypted_metadata =
absl::HexStringToBytes("F01112131415161718192021222F");
MockCredentialManager credential_manager;
// 0x37CD is an invalid DE, 0x37 means a 3 byte payload with type 7 alas
// only one byte is given (0xCD)
EXPECT_CALL(credential_manager,
DecryptDataElements(
kAccountName, salt,
encrypted_metadata + absl::HexStringToBytes("505152535455")))
.WillOnce(Return(metadata + absl::HexStringToBytes("37CD")));
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
EXPECT_THAT(decoder.DecodeAdvertisement(absl::HexStringToBytes(
"00614142F01112131415161718192021222F505152535455")),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementDecoder, UnsupportedAdvertisementVersion) {
MockCredentialManager credential_manager;
AdvertisementDecoder decoder(&credential_manager, GetScanRequest());
AdvertisementDecoder decoder(GetScanRequest());
EXPECT_THAT(decoder.DecodeAdvertisement(
absl::HexStringToBytes("012041420318CD29EEFF")),
@@ -388,9 +317,8 @@ TEST(AdvertisementDecoder, UnsupportedAdvertisementVersion) {
TEST(AdvertisementDecoder, MatchesScanFilterNoFilterPasses) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateIdentityFieldType, "payload")};
MockCredentialManager credential_manager;
ScanRequest empty_scan_request = {};
AdvertisementDecoder decoder(&credential_manager, empty_scan_request);
AdvertisementDecoder decoder(empty_scan_request);
// A scan request without scan filters matches any advertisement
EXPECT_TRUE(decoder.MatchesScanFilter(
@@ -401,7 +329,6 @@ TEST(AdvertisementDecoder, MatchesScanFilterNoFilterPasses) {
TEST(AdvertisementDecoder, MatchesPresenceScanFilter) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateIdentityFieldType, "payload")};
MockCredentialManager credential_manager;
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
@@ -409,7 +336,8 @@ TEST(AdvertisementDecoder, MatchesPresenceScanFilter) {
PresenceScanFilter filter = {.extended_properties = {model_id, salt}};
AdvertisementDecoder decoder(
&credential_manager, ScanRequestBuilder().AddScanFilter(filter).Build());
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(decoder.MatchesScanFilter({}));
EXPECT_FALSE(decoder.MatchesScanFilter({salt}));
@@ -421,7 +349,6 @@ TEST(AdvertisementDecoder, MatchesPresenceScanFilter) {
TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilter) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateIdentityFieldType, "payload")};
MockCredentialManager credential_manager;
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
@@ -429,7 +356,8 @@ TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilter) {
LegacyPresenceScanFilter filter = {.extended_properties = {model_id, salt}};
AdvertisementDecoder decoder(
&credential_manager, ScanRequestBuilder().AddScanFilter(filter).Build());
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(decoder.MatchesScanFilter({}));
EXPECT_FALSE(decoder.MatchesScanFilter({salt}));
@@ -441,7 +369,6 @@ TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilter) {
TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilterWithActions) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateIdentityFieldType, "payload")};
MockCredentialManager credential_manager;
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
@@ -452,7 +379,8 @@ TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilterWithActions) {
.extended_properties = {model_id, salt}};
AdvertisementDecoder decoder(
&credential_manager, ScanRequestBuilder().AddScanFilter(filter).Build());
ScanRequestBuilder().AddScanFilter(filter).Build());
EXPECT_FALSE(decoder.MatchesScanFilter({salt, model_id}));
EXPECT_TRUE(decoder.MatchesScanFilter({salt, eddystone_action, model_id}));
@@ -461,7 +389,6 @@ TEST(AdvertisementDecoder, MatchesLegacyPresenceScanFilterWithActions) {
TEST(AdvertisementDecoder, MatchesMultipleFilters) {
std::vector<DataElement> adv = {
DataElement(DataElement::kPrivateIdentityFieldType, "payload")};
MockCredentialManager credential_manager;
DataElement model_id =
DataElement(DataElement::kModelIdFieldType, "model id");
DataElement salt = DataElement(DataElement::kSaltFieldType, "salt");
@@ -472,8 +399,7 @@ TEST(AdvertisementDecoder, MatchesMultipleFilters) {
static_cast<int>(ActionBit::kEddystoneAction)},
.extended_properties = {salt}};
AdvertisementDecoder decoder(&credential_manager,
ScanRequestBuilder()
AdvertisementDecoder decoder(ScanRequestBuilder()
.AddScanFilter(presence_filter)
.AddScanFilter(legacy_filter)
.Build());
@@ -15,6 +15,7 @@
#include "presence/implementation/advertisement_factory.h"
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
@@ -24,14 +25,14 @@
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/ldt.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
namespace presence {
using ::nearby::internal::IdentityType;
namespace {
using ::nearby::internal::IdentityType;
constexpr uint8_t kBaseVersion = 0;
constexpr size_t kMaxBaseNpAdvSize = 26;
@@ -94,18 +95,20 @@ std::string SerializeAction(const Action& action) {
} // namespace
absl::StatusOr<AdvertisementData> AdvertisementFactory::CreateAdvertisement(
const BaseBroadcastRequest& request) const {
const BaseBroadcastRequest& request,
std::vector<PrivateCredential>& credentials) const {
AdvertisementData advert = {};
if (absl::holds_alternative<BaseBroadcastRequest::BasePresence>(
request.variant)) {
return CreateBaseNpAdvertisement(request);
return CreateBaseNpAdvertisement(request, credentials);
}
return advert;
}
absl::StatusOr<AdvertisementData>
AdvertisementFactory::CreateBaseNpAdvertisement(
const BaseBroadcastRequest& request) const {
const BaseBroadcastRequest& request,
std::vector<PrivateCredential>& credentials) const {
const auto& presence =
absl::get<BaseBroadcastRequest::BasePresence>(request.variant);
std::string payload;
@@ -114,7 +117,8 @@ AdvertisementFactory::CreateBaseNpAdvertisement(
absl::Status result;
std::string tx_power_and_action = {static_cast<char>(request.tx_power)};
tx_power_and_action.append(SerializeAction(presence.action));
uint8_t identity_type = GetIdentityFieldType(presence.identity);
uint8_t identity_type =
GetIdentityFieldType(presence.credential_selector.identity_type);
bool needs_encryption =
identity_type != DataElement::kPublicIdentityFieldType;
if (needs_encryption) {
@@ -122,6 +126,9 @@ AdvertisementFactory::CreateBaseNpAdvertisement(
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported salt size %d", request.salt.size()));
}
if (credentials.empty()) {
return absl::FailedPreconditionError("Missing credentials");
}
std::string unencrypted;
// In v0 OTA format, this is a combined TX and Action DE
result = AppendDataElement(DataElement::kActionFieldType,
@@ -130,9 +137,7 @@ AdvertisementFactory::CreateBaseNpAdvertisement(
return result;
}
absl::StatusOr<std::string> encrypted =
credential_manager_.EncryptDataElements(presence.identity,
presence.account_name,
request.salt, unencrypted);
EncryptDataElements(credentials, request.salt, unencrypted);
if (!encrypted.ok()) {
return encrypted.status();
}
@@ -175,6 +180,39 @@ AdvertisementFactory::CreateBaseNpAdvertisement(
return AdvertisementData{.is_extended_advertisement = false,
.content = payload};
}
absl::StatusOr<std::string> AdvertisementFactory::EncryptDataElements(
std::vector<PrivateCredential>& credentials, absl::string_view salt,
absl::string_view data_elements) const {
PrivateCredential& credential = credentials.front();
if (credential.metadata_encryption_key().size() != kBaseMetadataSize) {
return absl::FailedPreconditionError(absl::StrFormat(
"Metadata key size %d, expected %d",
credential.metadata_encryption_key().size(), kBaseMetadataSize));
}
// HMAC is not used during encryption, so we can pass an empty value.
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(credential.authenticity_key(), /*known_hmac=*/"");
if (!encryptor.ok()) {
return encryptor.status();
}
std::string plaintext =
absl::StrCat(credential.metadata_encryption_key(), data_elements);
return encryptor->Encrypt(plaintext, salt);
}
absl::StatusOr<CredentialSelector> AdvertisementFactory::GetCredentialSelector(
const BaseBroadcastRequest& request) {
if (absl::holds_alternative<BaseBroadcastRequest::BasePresence>(
request.variant)) {
const auto& presence =
absl::get<BaseBroadcastRequest::BasePresence>(request.variant);
if (presence.credential_selector.identity_type !=
DataElement::kPublicIdentityFieldType) {
return presence.credential_selector;
}
}
return absl::NotFoundError("credentials not required");
}
} // namespace presence
} // namespace nearby
@@ -16,11 +16,11 @@
#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "internal/proto/credential.pb.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/credential_manager.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
@@ -29,18 +29,31 @@ namespace presence {
// Builds BLE advertisements from broadcast requests.
class AdvertisementFactory {
public:
explicit AdvertisementFactory(CredentialManager* credential_manager)
: credential_manager_(*credential_manager) {}
using PrivateCredential = internal::PrivateCredential;
// Returns a `CredentialSelector` if credentials are required to create an
// advertisement from the `request`.
static absl::StatusOr<CredentialSelector> GetCredentialSelector(
const BaseBroadcastRequest& request);
// Returns a BLE advertisement for given `request.
absl::StatusOr<AdvertisementData> CreateAdvertisement(
const BaseBroadcastRequest& request) const;
const BaseBroadcastRequest& request,
std::vector<PrivateCredential>& credentials) const;
absl::StatusOr<AdvertisementData> CreateAdvertisement(
const BaseBroadcastRequest& request) const {
std::vector<PrivateCredential> empty;
return CreateAdvertisement(request, empty);
}
private:
absl::StatusOr<AdvertisementData> CreateBaseNpAdvertisement(
const BaseBroadcastRequest& request) const;
CredentialManager& credential_manager_;
const BaseBroadcastRequest& request,
std::vector<PrivateCredential>& credentials) const;
absl::StatusOr<std::string> EncryptDataElements(
std::vector<PrivateCredential>& credentials, absl::string_view salt,
absl::string_view data_elements) const;
};
} // namespace presence
@@ -22,9 +22,10 @@
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "internal/platform/byte_array.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_element.h"
#include "presence/implementation/action_factory.h"
#include "presence/implementation/credential_manager_impl.h"
#include "presence/implementation/mediums/advertisement_data.h"
namespace nearby {
@@ -32,26 +33,35 @@ namespace presence {
namespace {
using ::location::nearby::ByteArray; // NOLINT
using ::nearby::internal::IdentityType;
using ::nearby::internal::PrivateCredential; // NOLINT
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::status::StatusIs;
class MockCredentialManager : public CredentialManagerImpl {
public:
MOCK_METHOD(absl::StatusOr<std::string>, EncryptDataElements,
(IdentityType identity, absl::string_view account_name,
absl::string_view salt, absl::string_view data_elements),
(override));
};
#if USE_RUST_LDT == 1
PrivateCredential CreatePrivateCredential(IdentityType identity_type) {
// Values copied from LDT tests
ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72,
184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152,
200, 193, 94, 107, 28, 194, 182, 32, 205, 57});
ByteArray metadata_key(
{205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174});
PrivateCredential private_credential;
private_credential.set_identity_type(identity_type);
private_credential.set_authenticity_key(seed.AsStringView());
private_credential.set_metadata_encryption_key(metadata_key.AsStringView());
return private_credential;
}
TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) {
std::string account_name = "Test account";
std::string salt = "AB";
std::string metadata_key =
absl::HexStringToBytes("1011121314151617181920212223");
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PRIVATE;
std::vector<PrivateCredential> credentials = {
CreatePrivateCredential(kIdentity)};
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
Action action = ActionFactory::CreateAction(data_elements);
@@ -61,24 +71,71 @@ TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) {
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
EXPECT_CALL(credential_manager,
EncryptDataElements(kIdentity, account_name, salt,
absl::HexStringToBytes("36050080")))
.WillOnce(Return(metadata_key + absl::HexStringToBytes("50515253")));
AdvertisementFactory factory(&credential_manager);
absl::StatusOr<AdvertisementData> result =
factory.CreateAdvertisement(request);
AdvertisementFactory().CreateAdvertisement(request, credentials);
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content),
"00414142101112131415161718192021222350515253");
"00414142ceb073b0e34f58d7dc6dea370783ac943fa5");
}
TEST(AdvertisementFactory, CreateAdvertisementFromTrustedIdentity) {
std::string account_name = "Test account";
std::string salt = "AB";
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_TRUSTED;
std::vector<PrivateCredential> credentials = {
CreatePrivateCredential(kIdentity)};
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
data_elements.emplace_back(DataElement(ActionBit::kFitCastAction));
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
absl::StatusOr<AdvertisementData> result =
AdvertisementFactory().CreateAdvertisement(request, credentials);
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content),
"00424142253536ac63191a96894d95f0ffa38b57cf9b");
}
TEST(AdvertisementFactory, CreateAdvertisementFromProvisionedIdentity) {
std::string account_name = "Test account";
std::string salt = "AB";
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PROVISIONED;
std::vector<PrivateCredential> credentials = {
CreatePrivateCredential(kIdentity)};
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
data_elements.emplace_back(DataElement(ActionBit::kFitCastAction));
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
absl::StatusOr<AdvertisementData> result =
AdvertisementFactory().CreateAdvertisement(request, credentials);
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content),
"00444142253536ac63191a96894d95f0ffa38b57cf9b");
}
#endif /*USE_RUST_LDT*/
TEST(AdvertisementFactory, CreateAdvertisementFromPublicIdentity) {
std::string salt = "AB";
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PUBLIC;
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
@@ -89,97 +146,16 @@ TEST(AdvertisementFactory, CreateAdvertisementFromPublicIdentity) {
.SetTxPower(5)
.SetAction(action));
AdvertisementFactory factory(&credential_manager);
absl::StatusOr<AdvertisementData> result =
factory.CreateAdvertisement(request);
AdvertisementFactory().CreateAdvertisement(request);
ASSERT_OK(result);
EXPECT_FALSE(result->is_extended_advertisement);
EXPECT_EQ(absl::BytesToHexString(result->content), "000320414236050080");
}
TEST(AdvertisementFactory, CreateAdvertisementFailsWhenEncryptionFails) {
std::string account_name = "Test account";
std::string salt = "AB";
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE;
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
EXPECT_CALL(credential_manager,
EncryptDataElements(kIdentity, account_name, salt,
absl::HexStringToBytes("36050080")))
.WillOnce(Return(absl::OutOfRangeError("failed")));
AdvertisementFactory factory(&credential_manager);
EXPECT_THAT(factory.CreateAdvertisement(request),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementFactory,
CreateAdvertisementFailsWhenEncryptionReturnsTooMuchData) {
std::string account_name = "Test account";
std::string salt = "AB";
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE;
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
EXPECT_CALL(credential_manager,
EncryptDataElements(kIdentity, account_name, salt,
absl::HexStringToBytes("36050080")))
.WillOnce(Return(absl::HexStringToBytes(
"deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead"
"deaddeaddeaddeaddeaddeaddead")));
AdvertisementFactory factory(&credential_manager);
EXPECT_THAT(factory.CreateAdvertisement(request),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(AdvertisementFactory,
CreateAdvertisementFailsWhenEncryptionReturnsTooLittleData) {
std::string account_name = "Test account";
std::string salt = "AB";
std::string metadata_key =
absl::HexStringToBytes("1011121314151617181920212223");
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE;
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
Action action = ActionFactory::CreateAction(data_elements);
BaseBroadcastRequest request =
BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity)
.SetAccountName(account_name)
.SetSalt(salt)
.SetTxPower(5)
.SetAction(action));
EXPECT_CALL(credential_manager,
EncryptDataElements(kIdentity, account_name, salt,
absl::HexStringToBytes("36050080")))
.WillOnce(Return(metadata_key));
AdvertisementFactory factory(&credential_manager);
EXPECT_THAT(factory.CreateAdvertisement(request),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(AdvertisementFactory, CreateAdvertisementFailsWhenSaltIsTooShort) {
std::string salt = "AB";
NiceMock<MockCredentialManager> credential_manager;
constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE;
std::vector<DataElement> data_elements;
data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction));
@@ -192,8 +168,7 @@ TEST(AdvertisementFactory, CreateAdvertisementFailsWhenSaltIsTooShort) {
// Override the salt with invalid value
request.salt = "C";
AdvertisementFactory factory(&credential_manager);
EXPECT_THAT(factory.CreateAdvertisement(request),
EXPECT_THAT(AdvertisementFactory().CreateAdvertisement(request),
StatusIs(absl::StatusCode::kInvalidArgument));
}
@@ -60,9 +60,18 @@ BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetAccountName(
return *this;
}
BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetManagerAppId(
absl::string_view manager_app_id) {
manager_app_id_ = std::string(manager_app_id);
return *this;
}
BasePresenceRequestBuilder::operator BaseBroadcastRequest() const {
BaseBroadcastRequest::BasePresence presence{
.identity = identity_, .action = action_, .account_name = account_name_};
.credential_selector = {.manager_app_id = manager_app_id_,
.account_name = account_name_,
.identity_type = identity_},
.action = action_};
BaseBroadcastRequest broadcast_request{
.variant = presence,
.salt = salt_.size() == kSaltSize
@@ -92,6 +101,7 @@ absl::StatusOr<BaseBroadcastRequest> BaseBroadcastRequest::Create(
.SetTxPower(request.tx_power)
.SetAction(ActionFactory::CreateAction(section.extended_properties))
.SetPowerMode(request.power_mode)
.SetManagerAppId(section.manager_app_id)
.SetAccountName(section.account_name));
}
return absl::UnimplementedError("Request not supported");
@@ -22,6 +22,7 @@
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/proto/credential.pb.h"
#include "presence/broadcast_request.h"
#include "presence/power_mode.h"
@@ -47,9 +48,8 @@ struct BaseBroadcastRequest {
const BroadcastRequest& request);
struct BasePresence {
nearby::internal::IdentityType identity;
CredentialSelector credential_selector;
Action action;
std::string account_name;
};
struct BaseFastPair {
struct Discoverable {
@@ -82,6 +82,7 @@ class BasePresenceRequestBuilder {
BasePresenceRequestBuilder& SetAction(const Action& action);
BasePresenceRequestBuilder& SetPowerMode(PowerMode power_mode);
BasePresenceRequestBuilder& SetAccountName(absl::string_view account_name);
BasePresenceRequestBuilder& SetManagerAppId(absl::string_view manager_app_id);
explicit operator BaseBroadcastRequest() const;
@@ -92,6 +93,7 @@ class BasePresenceRequestBuilder {
Action action_;
PowerMode power_mode_ = PowerMode::kNoPower;
std::string account_name_;
std::string manager_app_id_;
};
} // namespace presence
@@ -49,11 +49,13 @@ TEST(BroadcastRequestTest, CreateFromPresenceRequest) {
constexpr uint32_t kExpectedAction =
(1 << 23); // encoded kActiveUnlockAction
std::string account_name = "Test account";
std::string manager_app_id = "Manager app id";
PresenceBroadcast::BroadcastSection section = {
.identity = internal::IDENTITY_TYPE_PUBLIC,
.extended_properties = {DataElement(
DataElement(ActionBit::kActiveUnlockAction))},
.account_name = account_name};
.account_name = account_name,
.manager_app_id = manager_app_id};
PresenceBroadcast presence_request = {.sections = {section}};
BroadcastRequest input = {.tx_power = kTxPower, .variant = presence_request};
@@ -62,15 +64,18 @@ TEST(BroadcastRequestTest, CreateFromPresenceRequest) {
ASSERT_OK(request);
EXPECT_THAT(request->tx_power, kTxPower);
EXPECT_THAT(
absl::get<BaseBroadcastRequest::BasePresence>(request->variant).identity,
internal::IDENTITY_TYPE_PUBLIC);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.credential_selector.identity_type,
internal::IDENTITY_TYPE_PUBLIC);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.action.action,
kExpectedAction);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.account_name,
.credential_selector.account_name,
account_name);
EXPECT_THAT(absl::get<BaseBroadcastRequest::BasePresence>(request->variant)
.credential_selector.manager_app_id,
manager_app_id);
}
TEST(BroadcastRequestTest, CreateFromEmptyPresenceRequestFails) {
@@ -74,26 +74,6 @@ class CredentialManager {
absl::string_view device_metadata_encryption_key,
absl::string_view authenticity_key,
absl::string_view device_metadata_string) = 0;
// Decrypts Data Elements from an NP advertisement.
// Returns an error if `data_elements` could not be deciphered with any known
// credentials (identity).
virtual absl::StatusOr<std::string> DecryptDataElements(
absl::string_view account_name, absl::string_view salt,
absl::string_view data_elements) = 0;
// Decrypts Data Elements from an NP advertisement.
// Returns an error if `data_elements` could not be deciphered with any of the
// provided credentials.
virtual absl::StatusOr<std::string> DecryptDataElements(
const std::vector<nearby::internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements) = 0;
// Encrypts `data_elements` using certificate associated with `identity`,
// `account_name` and `salt`.
virtual absl::StatusOr<std::string> EncryptDataElements(
nearby::internal::IdentityType identity, absl::string_view account_name,
absl::string_view salt, absl::string_view data_elements) = 0;
};
} // namespace presence
@@ -309,71 +309,5 @@ CredentialManagerImpl::GetPublicCredentialsSync(
return result.Get(timeout);
}
absl::StatusOr<std::string> CredentialManagerImpl::DecryptDataElements(
absl::string_view account_name, absl::string_view salt,
absl::string_view data_elements) {
CredentialSelector selector = {
.manager_app_id = "",
.account_name = std::string(account_name),
.identity_type = internal::IDENTITY_TYPE_PUBLIC,
};
ExceptionOr<std::vector<PublicCredential>> credentials =
GetPublicCredentialsSync(
selector, PublicCredentialType::kRemotePublicCredential, kTimeout);
if (!credentials.ok()) {
return absl::UnavailableError("Failed to fetch credentials");
}
return DecryptDataElements(credentials.result(), salt, data_elements);
}
absl::StatusOr<std::string> CredentialManagerImpl::DecryptDataElements(
const std::vector<nearby::internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements) {
if (credentials.empty()) {
return absl::UnavailableError("No credentials");
}
for (const auto& credential : credentials) {
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(credential.authenticity_key(),
credential.metadata_encryption_key_tag());
if (encryptor.ok()) {
absl::StatusOr<std::string> result =
encryptor->DecryptAndVerify(data_elements, salt);
if (result.ok()) {
return result;
}
}
}
return absl::UnavailableError(
"Couldn't decrypt the message with any credentials");
}
absl::StatusOr<std::string> CredentialManagerImpl::EncryptDataElements(
nearby::internal::IdentityType identity, absl::string_view account_name,
absl::string_view salt, absl::string_view data_elements) {
CredentialSelector selector = {
.manager_app_id = "",
.account_name = std::string(account_name),
.identity_type = identity,
};
ExceptionOr<std::vector<PrivateCredential>> credentials =
GetPrivateCredentialsSync(selector, kTimeout);
if (!credentials.ok()) {
return absl::UnavailableError("Failed to fetch credentials");
}
if (credentials.result().empty()) {
return absl::UnavailableError("No credentials");
}
PrivateCredential& credential = credentials.result().front();
// HMAC is not used during encryption, so we can pass an empty value.
absl::StatusOr<LdtEncryptor> encryptor =
LdtEncryptor::Create(credential.authenticity_key(), /*known_hmac=*/"");
if (!encryptor.ok()) {
return encryptor.status();
}
return encryptor->Encrypt(data_elements, salt);
}
} // namespace presence
} // namespace nearby
@@ -94,18 +94,6 @@ class CredentialManagerImpl : public CredentialManager {
absl::string_view authenticity_key,
absl::string_view device_metadata_string) override;
absl::StatusOr<std::string> DecryptDataElements(
absl::string_view account_name, absl::string_view salt,
absl::string_view data_elements) override;
absl::StatusOr<std::string> DecryptDataElements(
const std::vector<nearby::internal::PublicCredential>& credentials,
absl::string_view salt, absl::string_view data_elements) override;
absl::StatusOr<std::string> EncryptDataElements(
nearby::internal::IdentityType identity, absl::string_view account_name,
absl::string_view salt, absl::string_view data_elements) override;
std::pair<nearby::internal::PrivateCredential,
nearby::internal::PublicCredential>
CreatePrivateCredential(
@@ -381,55 +381,6 @@ TEST(CredentialManagerImpl, PublicCredentialsFailEncryption) {
EXPECT_TRUE(publicCredentials.empty());
}
TEST(CredentialManagerImpl, EncryptDataElements) {
absl::string_view salt = "AB";
absl::string_view data_elements = "data_elements";
#if USE_RUST_LDT == 1
// `data_elements` is too short for LDT
absl::Status kExpectedError =
absl::InternalError("LDT encryption failed, errorcode -1");
#else
absl::Status kExpectedError =
absl::UnavailableError("Failed to create LDT encryptor");
#endif /* USE_RUST_LDT */
DeviceMetadata device_metadata = CreateTestDeviceMetadata();
CredentialManagerImpl credential_manager;
std::vector<IdentityType> identity_types{IDENTITY_TYPE_PRIVATE};
credential_manager.GenerateCredentials(
device_metadata, "", identity_types, 1, 1,
{
.credentials_generated_cb =
[](std::vector<nearby::internal::PublicCredential>) {},
});
EXPECT_THAT(credential_manager.EncryptDataElements(
IDENTITY_TYPE_PRIVATE, "test_account", salt, data_elements),
kExpectedError);
}
TEST(CredentialManagerImpl, DecryptDataElements) {
absl::string_view salt = "AB";
absl::string_view data_elements = "data_elements";
CredentialManagerImpl credential_manager;
EXPECT_THAT(credential_manager.DecryptDataElements("test_account", salt,
data_elements),
absl::Status(absl::StatusCode::kUnavailable,
"Failed to fetch credentials"));
}
TEST(CredentialManagerImpl, DecryptDataElementsWithCredentials) {
absl::string_view salt = "AB";
absl::string_view data_elements = "data_elements";
CredentialManagerImpl credential_manager;
std::vector<nearby::internal::PublicCredential> credentials = {{}};
EXPECT_THAT(
credential_manager.DecryptDataElements(credentials, salt, data_elements),
absl::Status(absl::StatusCode::kUnavailable,
"Couldn't decrypt the message with any credentials"));
}
} // namespace
} // namespace presence
+43 -3
View File
@@ -25,6 +25,7 @@
#include "absl/types/variant.h"
#include "internal/platform/future.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/credential_callbacks.h"
#include "internal/platform/uuid.h"
#include "presence/data_types.h"
#include "presence/implementation/advertisement_decoder.h"
@@ -79,12 +80,12 @@ ScanSessionId ScanManager::StartScan(ScanRequest scan_request,
NotifyFoundBle(id, data, address);
});
}};
FetchCredentials(id, scan_request);
scan_sessions_.insert(
{id, ScanSessionState{
.request = scan_request,
.callback = std::move(scan_callback),
.decoder = AdvertisementDecoder(credential_manager_,
scan_request),
.decoder = AdvertisementDecoder(scan_request),
.scanning_session = mediums_->GetBle().StartScanning(
scan_request, std::move(callback))}});
});
@@ -118,7 +119,7 @@ void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data,
// This advertisement is not relevant to the current element, skip.
return;
}
if (it->second.decoder.MatchesScanFilter(advert.value())) {
if (it->second.decoder.MatchesScanFilter(advert->data_elements)) {
// TODO(b/256913915): Provide more information in PresenceDevice once
// fully implemented
internal::DeviceMetadata metadata;
@@ -127,6 +128,45 @@ void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data,
}
}
void ScanManager::FetchCredentials(ScanSessionId id,
const ScanRequest& scan_request) {
std::vector<CredentialSelector> credential_selectors =
AdvertisementDecoder::GetCredentialSelectors(scan_request);
for (const CredentialSelector& selector : credential_selectors) {
credential_manager_->GetPublicCredentials(
selector, PublicCredentialType::kRemotePublicCredential,
{.credentials_fetched_cb =
[this, id, identity_type = selector.identity_type](
std::vector<::nearby::internal::PublicCredential>
credentials) {
RunOnServiceControllerThread(
"update-credentials",
[this, id, identity_type,
credentials = std::move(credentials)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
UpdateCredentials(id, identity_type,
std::move(credentials));
});
},
.get_credentials_failed_cb =
[](CredentialOperationStatus status) {
NEARBY_LOGS(WARNING) << "Failed to fetch credentials";
}});
}
}
void ScanManager::UpdateCredentials(ScanSessionId id,
IdentityType identity_type,
std::vector<PublicCredential> credentials) {
auto it = scan_sessions_.find(id);
if (it == scan_sessions_.end()) {
return;
}
ScanSessionState& session = it->second;
session.credentials[identity_type] = std::move(credentials);
session.decoder = AdvertisementDecoder(session.request, &session.credentials);
}
int ScanManager::ScanningCallbacksLengthForTest() {
::location::nearby::Future<int> count;
RunOnServiceControllerThread("callbacks-size",
+10
View File
@@ -23,6 +23,7 @@
#include "absl/container/flat_hash_map.h"
#include "absl/random/random.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.pb.h"
#include "presence/data_types.h"
#include "presence/implementation/advertisement_decoder.h"
#include "presence/implementation/credential_manager.h"
@@ -44,6 +45,8 @@ class ScanManager {
using Runnable = ::location::nearby::Runnable;
using BleAdvertisementData =
::location::nearby::api::ble_v2::BleAdvertisementData;
using PublicCredential = ::nearby::internal::PublicCredential;
using IdentityType = ::nearby::internal::IdentityType;
ScanManager(Mediums& mediums, CredentialManager& credential_manager,
SingleThreadExecutor& executor) {
@@ -62,12 +65,19 @@ class ScanManager {
struct ScanSessionState {
ScanRequest request;
ScanCallback callback;
absl::flat_hash_map<IdentityType, std::vector<PublicCredential>>
credentials;
AdvertisementDecoder decoder;
std::unique_ptr<ScanningSession> scanning_session;
};
void NotifyFoundBle(ScanSessionId id, BleAdvertisementData data,
absl::string_view remote_address)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void FetchCredentials(ScanSessionId id, const ScanRequest& scan_request)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void UpdateCredentials(ScanSessionId id, IdentityType identity_type,
std::vector<PublicCredential> credentials)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) {
executor_->Execute(std::string(name), std::move(runnable));
}
+1 -2
View File
@@ -66,9 +66,8 @@ class ScanManagerTest : public testing::Test {
absl::StatusOr<BaseBroadcastRequest> request =
BaseBroadcastRequest::Create(input);
EXPECT_OK(request);
AdvertisementFactory factory(&credential_manager_);
absl::StatusOr<AdvertisementData> advertisement =
factory.CreateAdvertisement(request.value());
AdvertisementFactory().CreateAdvertisement(request.value());
EXPECT_OK(advertisement);
std::unique_ptr<AdvertisingSession> session = ble.StartAdvertising(
advertisement.value(), PowerMode::kLowPower,
@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/random/random.h"
#include "absl/status/status.h"
@@ -68,36 +69,80 @@ absl::StatusOr<BroadcastSessionId> ServiceControllerImpl::StartBroadcast(
RunOnServiceControllerThread(
"start-broadcast",
[this, id, power_mode = broadcast_request.power_mode, request = *request,
broadcast_callback =
std::move(callback)]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) {
sessions_.insert({id, BroadcastSessionState(broadcast_callback)});
absl::StatusOr<AdvertisementData> advertisement =
AdvertisementFactory(&credential_manager_)
.CreateAdvertisement(request);
if (!advertisement.ok()) {
NEARBY_LOGS(WARNING) << "Can't create advertisement, reason: "
<< advertisement.status();
NotifyStartCallbackStatus(id, Status{Status::Value::kError});
return;
}
std::unique_ptr<AdvertisingSession> session =
mediums_.GetBle().StartAdvertising(
*advertisement, power_mode,
AdvertisingCallback{.start_advertising_result =
[this, id](BleOperationStatus status) {
NotifyStartCallbackStatus(
id, ConvertBleStatus(status));
}});
if (!session) {
NotifyStartCallbackStatus(id, Status{Status::Value::kError});
return;
}
sessions_.at(id).SetAdvertisingSession(std::move(session));
});
broadcast_callback = std::move(callback)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) {
sessions_.insert(
{id, BroadcastSessionState(broadcast_callback, power_mode)});
FetchCredentials(id, std::move(request));
});
return id;
}
void ServiceControllerImpl::FetchCredentials(
BroadcastSessionId id, BaseBroadcastRequest broadcast_request) {
absl::StatusOr<CredentialSelector> credential_selector =
AdvertisementFactory::GetCredentialSelector(broadcast_request);
if (!credential_selector.ok()) {
// Public advertisement, we don't need credential to advertise.
Advertise(id, broadcast_request, /*credentials=*/{});
return;
}
credential_manager_.GetPrivateCredentials(
*credential_selector,
GetPrivateCredentialsResultCallback{
.credentials_fetched_cb =
[this, id, broadcast_request = std::move(broadcast_request)](
std::vector<::nearby::internal::PrivateCredential>
credentials) {
RunOnServiceControllerThread(
"advertise-non-public",
[this, id, broadcast_request = std::move(broadcast_request),
credentials = std::move(credentials)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) {
Advertise(id, broadcast_request, credentials);
});
},
.get_credentials_failed_cb =
[this, id](CredentialOperationStatus status) {
NEARBY_LOGS(WARNING) << "Failed to fetch credentials, status: "
<< static_cast<int>(status);
NotifyStartCallbackStatus(id, Status{Status::Value::kError});
}});
}
void ServiceControllerImpl::Advertise(
BroadcastSessionId id, BaseBroadcastRequest broadcast_request,
std::vector<PrivateCredential> credentials) {
auto it = sessions_.find(id);
if (it == sessions_.end()) {
NEARBY_LOGS(INFO) << "Broadcast session terminated, id: " << id;
return;
}
absl::StatusOr<AdvertisementData> advertisement =
AdvertisementFactory().CreateAdvertisement(broadcast_request,
credentials);
if (!advertisement.ok()) {
NEARBY_LOGS(WARNING) << "Can't create advertisement, reason: "
<< advertisement.status();
NotifyStartCallbackStatus(id, Status{Status::Value::kError});
return;
}
std::unique_ptr<AdvertisingSession> session =
mediums_.GetBle().StartAdvertising(
*advertisement, it->second.GetPowerMode(),
AdvertisingCallback{.start_advertising_result =
[this, id](BleOperationStatus status) {
NotifyStartCallbackStatus(
id, ConvertBleStatus(status));
}});
if (!session) {
NotifyStartCallbackStatus(id, Status{Status::Value::kError});
return;
}
it->second.SetAdvertisingSession(std::move(session));
}
void ServiceControllerImpl::NotifyStartCallbackStatus(BroadcastSessionId id,
Status status) {
RunOnServiceControllerThread("started-broadcast-cb",
@@ -24,8 +24,10 @@
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/proto/credential.pb.h"
#include "presence/broadcast_request.h"
#include "presence/data_types.h"
#include "presence/implementation/base_broadcast_request.h"
#include "presence/implementation/credential_manager_impl.h"
#include "presence/implementation/mediums/mediums.h"
#include "presence/implementation/scan_manager.h"
@@ -45,6 +47,7 @@ class ServiceControllerImpl : public ServiceController {
using AdvertisingSession =
::location::nearby::api::ble_v2::BleMedium::AdvertisingSession;
using Runnable = ::location::nearby::Runnable;
using PrivateCredential = internal::PrivateCredential;
ServiceControllerImpl() = default;
~ServiceControllerImpl() override { executor_.Shutdown(); }
@@ -64,8 +67,9 @@ class ServiceControllerImpl : public ServiceController {
private:
class BroadcastSessionState {
public:
explicit BroadcastSessionState(BroadcastCallback broadcast_callback)
: broadcast_callback_(broadcast_callback) {}
explicit BroadcastSessionState(BroadcastCallback broadcast_callback,
PowerMode power_mode)
: broadcast_callback_(broadcast_callback), power_mode_(power_mode) {}
void SetAdvertisingSession(std::unique_ptr<AdvertisingSession> session);
@@ -73,8 +77,11 @@ class ServiceControllerImpl : public ServiceController {
void StopAdvertising();
PowerMode GetPowerMode() { return power_mode_; }
private:
BroadcastCallback broadcast_callback_;
PowerMode power_mode_;
std::unique_ptr<AdvertisingSession> advertising_session_;
};
SingleThreadExecutor executor_;
@@ -83,6 +90,14 @@ class ServiceControllerImpl : public ServiceController {
void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) {
executor_.Execute(std::string(name), std::move(runnable));
}
void FetchCredentials(BroadcastSessionId id,
BaseBroadcastRequest broadcast_request)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_);
void Advertise(BroadcastSessionId id, BaseBroadcastRequest broadcast_request,
std::vector<PrivateCredential> credentials)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_);
Mediums mediums_; // NOLINT: further impl will use it.
CredentialManagerImpl
credential_manager_; // NOLINT: further impl will use it.
@@ -134,6 +134,7 @@ TEST_P(ServiceControllerImplTest, StopBroadcastTwiceNoSideEffects) {
EXPECT_TRUE(IsAdvertising());
service_controller_.StopBroadcast(*session);
service_controller_.StopBroadcast(*session);
}
TEST_P(ServiceControllerImplTest, StopBroadcastInvalidSessionNoSideEffects) {