diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index b2011183..82b9f983 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -170,6 +170,9 @@ cc_test( "//internal/proto:credential_cc_proto", "//presence:types", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ @@ -370,12 +373,19 @@ cc_test( srcs = ["scan_manager_test.cc"], deps = [ ":internal", + ":internal_test", + "//internal/platform:base", "//internal/platform:comm", "//internal/platform:test_util", "//internal/platform:types", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:types", + "//internal/proto:credential_cc_proto", + "//presence:types", "//presence/implementation/mediums", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:variant", "@com_google_googletest//:gtest_main", ] + select({ "@platforms//os:windows": [ diff --git a/presence/implementation/advertisement_decoder.cc b/presence/implementation/advertisement_decoder.cc index 8428fb88..9f5bbc88 100644 --- a/presence/implementation/advertisement_decoder.cc +++ b/presence/implementation/advertisement_decoder.cc @@ -14,11 +14,13 @@ #include "presence/implementation/advertisement_decoder.h" +#include #include #include #include #include +#include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/escaping.h" @@ -155,8 +157,8 @@ absl::StatusOr ParseDataElement(const absl::string_view input, } } // namespace -void AdvertisementDecoder::DecodeBaseAction( - absl::string_view serialized_action) { +void DecodeBaseAction(absl::string_view serialized_action, + Advertisement& decoded_advertisement) { if (serialized_action.empty() || serialized_action.size() > 3) { NEARBY_LOGS(WARNING) << "Base NP action \'" << absl::BytesToHexString(serialized_action) @@ -171,25 +173,25 @@ void AdvertisementDecoder::DecodeBaseAction( action.action |= serialized_action[i] << offset; } - ActionFactory::DecodeAction(action, decoded_advertisement_.data_elements); + ActionFactory::DecodeAction(action, decoded_advertisement.data_elements); } -absl::StatusOr AdvertisementDecoder::DecryptLdt( +absl::StatusOr DecryptLdt( const std::vector& credentials, - absl::string_view salt, absl::string_view data_elements) { + absl::string_view salt, absl::string_view encrypted_contents, + Advertisement& decoded_advertisement) { if (credentials.empty()) { return absl::UnavailableError("No credentials"); } for (const auto& credential : credentials) { absl::StatusOr encryptor = LdtEncryptor::Create( - credential.key_seed(), - credential.metadata_encryption_key_tag_v0()); + credential.key_seed(), credential.metadata_encryption_key_tag_v0()); if (encryptor.ok()) { absl::StatusOr result = - encryptor->DecryptAndVerify(data_elements, salt); + encryptor->DecryptAndVerify(encrypted_contents, salt); if (result.ok() && result->size() > kBaseMetadataSize) { - decoded_advertisement_.public_credential = credential; - decoded_advertisement_.metadata_key = + decoded_advertisement.public_credential = credential; + decoded_advertisement.metadata_key = result->substr(0, kBaseMetadataSize); return result->substr(kBaseMetadataSize); } @@ -199,18 +201,20 @@ absl::StatusOr AdvertisementDecoder::DecryptLdt( "Couldn't decrypt the message with any credentials"); } -absl::Status AdvertisementDecoder::DecryptDataElements( - const DataElement& elem) { +absl::Status DecryptDataElements( + const std::vector& credentials, + const DataElement& elem, Advertisement& decoded_advertisement) { 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); - decoded_advertisement_.data_elements.emplace_back(DataElement::kSaltFieldType, - salt); + decoded_advertisement.data_elements.emplace_back(DataElement::kSaltFieldType, + salt); absl::string_view encrypted = elem.GetValue().substr(kSaltSize); - absl::StatusOr decrypted = Decrypt(salt, encrypted); + absl::StatusOr decrypted = + DecryptLdt(credentials, salt, encrypted, decoded_advertisement); if (!decrypted.ok()) { NEARBY_LOGS(WARNING) << "Failed to decrypt advertisement, status: " << decrypted.status(); @@ -226,74 +230,19 @@ absl::Status AdvertisementDecoder::DecryptDataElements( return internal_elem.status(); } if (internal_elem->GetType() == DataElement::kActionFieldType) { - DecodeBaseAction(internal_elem->GetValue()); + DecodeBaseAction(internal_elem->GetValue(), decoded_advertisement); } else { - decoded_advertisement_.data_elements.push_back(*std::move(internal_elem)); + decoded_advertisement.data_elements.push_back(*std::move(internal_elem)); } } return absl::OkStatus(); } -absl::StatusOr AdvertisementDecoder::Decrypt( - absl::string_view salt, absl::string_view encrypted) { - for (const auto& scan_filter : scan_request_.scan_filters) { - if (!absl::holds_alternative(scan_filter)) { - continue; - } - const std::vector& credentials = - absl::get(scan_filter) - .remote_public_credentials; - if (credentials.empty()) { - continue; - } - absl::StatusOr decrypted = - DecryptLdt(credentials, salt, encrypted); - if (decrypted.ok()) { - return decrypted; - } - } - if (credentials_ == nullptr) { - return absl::FailedPreconditionError("Missing credentials"); - } - - return DecryptLdt((*credentials_)[decoded_advertisement_.identity_type], salt, - encrypted); -} - -void AdvertisementDecoder::AddBannedDataTypes() { - // The scan request has information what identity types the client is - // interested in. We'll ban all other idenitity data types. - banned_data_types_ = {DataElement::kPrivateIdentityFieldType, - DataElement::kTrustedIdentityFieldType, - DataElement::kPublicIdentityFieldType, - DataElement::kProvisionedIdentityFieldType}; - for (nearby::internal::IdentityType identity_type : - scan_request_.identity_types) { - switch (identity_type) { - case internal::IDENTITY_TYPE_PRIVATE: - banned_data_types_.erase(DataElement::kPrivateIdentityFieldType); - break; - case internal::IDENTITY_TYPE_TRUSTED: - banned_data_types_.erase(DataElement::kTrustedIdentityFieldType); - break; - case internal::IDENTITY_TYPE_PUBLIC: - banned_data_types_.erase(DataElement::kPublicIdentityFieldType); - break; - case internal::IDENTITY_TYPE_PROVISIONED: - banned_data_types_.erase(DataElement::kProvisionedIdentityFieldType); - break; - default: - // Nothing to do - break; - } - } -} - absl::StatusOr 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{}; + Advertisement decoded_advertisement = Advertisement{}; std::vector result; NEARBY_LOGS(INFO) << "Advertisement: " << absl::BytesToHexString(advertisement); @@ -306,7 +255,7 @@ absl::StatusOr AdvertisementDecoder::DecodeAdvertisement( return absl::UnimplementedError(absl::StrFormat( "Advertisement version (%d) is not supported", version)); } - decoded_advertisement_.version = version; + decoded_advertisement.version = version; size_t index = 1; absl::StatusOr decrypted; while (index < advertisement.size()) { @@ -316,31 +265,30 @@ absl::StatusOr AdvertisementDecoder::DecodeAdvertisement( << elem.status(); return elem.status(); } - // This checks allows us to bail before decryption when, for example, the - // client is scanning for advertisements with private identity but the - // advertisement uses trusted identity. - if (banned_data_types_.contains(elem->GetType())) { - return absl::FailedPreconditionError( - absl::StrFormat("Ignoring advertisement with data element type: %d", - elem->GetType())); - } if (IsIdentity(elem->GetType())) { - decoded_advertisement_.identity_type = GetIdentityType(elem->GetType()); + decoded_advertisement.identity_type = GetIdentityType(elem->GetType()); } if (IsEncryptedIdentity(elem->GetType())) { - absl::Status status = DecryptDataElements(*elem); + if (credentials_map_ == nullptr) { + return absl::FailedPreconditionError("Missing credentials"); + } + auto identity_type_specific_creds = + (*credentials_map_)[decoded_advertisement.identity_type]; + absl::Status status = DecryptDataElements(identity_type_specific_creds, + *elem, decoded_advertisement); if (!status.ok()) { return status; } } else { if (elem->GetType() == DataElement::kActionFieldType) { - DecodeBaseAction(elem->GetValue()); + DecodeBaseAction(elem->GetValue(), decoded_advertisement); } else { - decoded_advertisement_.data_elements.push_back(*std::move(elem)); + decoded_advertisement.data_elements.push_back(*std::move(elem)); } } } - return std::move(decoded_advertisement_); + + return std::move(decoded_advertisement); } } // namespace presence diff --git a/presence/implementation/advertisement_decoder.h b/presence/implementation/advertisement_decoder.h index b801ff8c..23a70594 100644 --- a/presence/implementation/advertisement_decoder.h +++ b/presence/implementation/advertisement_decoder.h @@ -15,17 +15,16 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_H_ #define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_H_ +#include #include #include #include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "internal/platform/implementation/credential_callbacks.h" +#include "absl/strings/string_view.h" #include "internal/proto/credential.pb.h" #include "presence/data_element.h" -#include "presence/scan_request.h" namespace nearby { namespace presence { @@ -42,20 +41,13 @@ struct Advertisement { // Decodes BLE NP advertisements class AdvertisementDecoder { public: - using IdentityType = ::nearby::internal::IdentityType; + explicit AdvertisementDecoder( + absl::flat_hash_map>* + credentials_map) + : credentials_map_(credentials_map) {}; - AdvertisementDecoder( - ScanRequest scan_request, - absl::flat_hash_map>* credentials) - : scan_request_(scan_request), credentials_(credentials) { - AddBannedDataTypes(); - } - - explicit AdvertisementDecoder(ScanRequest scan_request) - : scan_request_(scan_request) { - AddBannedDataTypes(); - } + explicit AdvertisementDecoder() = default; // Returns a list of Data Elements decoded from the advertisement. // Returns an error if the advertisement is misformatted or if it couldn't be @@ -64,22 +56,9 @@ class AdvertisementDecoder { absl::string_view advertisement); private: - // Decrypts data elements stored inside encrypted `elem` and appends them to - // `decoded_advertisement_`. - absl::Status DecryptDataElements(const DataElement& elem); - absl::StatusOr Decrypt(absl::string_view salt, - absl::string_view encrypted); - void DecodeBaseAction(absl::string_view serialized_action); - absl::StatusOr DecryptLdt( - const std::vector& credentials, - absl::string_view salt, absl::string_view data_elements); - void AddBannedDataTypes(); - - ScanRequest scan_request_; - absl::flat_hash_map>* - credentials_ = nullptr; - absl::flat_hash_set banned_data_types_; - Advertisement decoded_advertisement_; + absl::flat_hash_map>* + credentials_map_ = nullptr; }; } // namespace presence diff --git a/presence/implementation/advertisement_decoder_test.cc b/presence/implementation/advertisement_decoder_test.cc index 59097bd5..a063f01c 100644 --- a/presence/implementation/advertisement_decoder_test.cc +++ b/presence/implementation/advertisement_decoder_test.cc @@ -14,15 +14,16 @@ #include "presence/implementation/advertisement_decoder.h" -#include #include #include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/status/statusor.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" @@ -34,13 +35,10 @@ namespace nearby { namespace presence { namespace { -using ::nearby::ByteArray; // NOLINT -using ::nearby::internal::IdentityType; +using ::nearby::ByteArray; // NOLINT +using ::nearby::internal::IdentityType; // NOLINT using ::nearby::internal::SharedCredential; // NOLINT using ::testing::ElementsAre; -using ::testing::Matcher; -using ::testing::Pointwise; -using ::testing::Return; using ::testing::UnorderedElementsAre; using ::testing::status::StatusIs; @@ -64,7 +62,6 @@ ScanRequest GetScanRequest(std::vector credentials) { .AddIdentityType(IdentityType::IDENTITY_TYPE_TRUSTED) .AddIdentityType(IdentityType::IDENTITY_TYPE_PUBLIC) .AddIdentityType(IdentityType::IDENTITY_TYPE_PROVISIONED) - .AddScanFilter(scan_filter) .Build(); } @@ -92,11 +89,10 @@ TEST(AdvertisementDecoder, DecodeBaseNpPrivateAdvertisement) { credentials; credentials[IdentityType::IDENTITY_TYPE_PRIVATE].push_back( GetPublicCredential()); - AdvertisementDecoder decoder(GetScanRequest(), &credentials); + AdvertisementDecoder decoder(&credentials); absl::StatusOr result = decoder.DecodeAdvertisement( absl::HexStringToBytes("00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c")); - ASSERT_OK(result); EXPECT_EQ(result->metadata_key, metadata_key.AsStringView()); EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE); @@ -113,13 +109,15 @@ TEST(AdvertisementDecoder, const std::string salt = "AB"; ByteArray metadata_key( {205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174}); - std::vector credentials = {GetPublicCredential()}; - AdvertisementDecoder decoder(GetScanRequest(credentials)); + absl::flat_hash_map> + credentials; + credentials[IdentityType::IDENTITY_TYPE_PRIVATE].push_back( + GetPublicCredential()); + AdvertisementDecoder decoder(&credentials); absl::StatusOr result = decoder.DecodeAdvertisement( absl::HexStringToBytes("00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c")); - ASSERT_OK(result); EXPECT_EQ(result->metadata_key, metadata_key.AsStringView()); EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE); @@ -139,11 +137,10 @@ TEST(AdvertisementDecoder, DecodeBaseNpTrustedAdvertisement) { credentials; credentials[IdentityType::IDENTITY_TYPE_TRUSTED].push_back( GetPublicCredential()); - AdvertisementDecoder decoder(GetScanRequest(), &credentials); + AdvertisementDecoder decoder(&credentials); absl::StatusOr result = decoder.DecodeAdvertisement( absl::HexStringToBytes("0052414257a35c020f1c547d7e169303196d75da7118ba")); - ASSERT_OK(result); EXPECT_EQ(result->metadata_key, metadata_key.AsStringView()); EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_TRUSTED); @@ -166,11 +163,10 @@ TEST(AdvertisementDecoder, DecodeBaseNpProvisionedAdvertisement) { credentials; credentials[IdentityType::IDENTITY_TYPE_PROVISIONED].push_back( GetPublicCredential()); - AdvertisementDecoder decoder(GetScanRequest(), &credentials); + AdvertisementDecoder decoder(&credentials); absl::StatusOr result = decoder.DecodeAdvertisement( absl::HexStringToBytes("0054414257a35c020f1c547d7e169303196d75da7118ba")); - ASSERT_OK(result); EXPECT_EQ(result->metadata_key, metadata_key.AsStringView()); EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PROVISIONED); @@ -193,7 +189,7 @@ TEST(AdvertisementDecoder, InvalidEncryptedContent) { credentials; credentials[IdentityType::IDENTITY_TYPE_PRIVATE].push_back( GetPublicCredential()); - AdvertisementDecoder decoder(GetScanRequest(), &credentials); + AdvertisementDecoder decoder(&credentials); EXPECT_THAT(decoder.DecodeAdvertisement(absl::HexStringToBytes( "00414142f085d661ac8cb110e792e7faeb736294")), @@ -204,7 +200,7 @@ TEST(AdvertisementDecoder, InvalidEncryptedContent) { TEST(AdvertisementDecoder, DecodeBaseNpPublicAdvertisement) { const std::string salt = "AB"; - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; const absl::StatusOr result = decoder.DecodeAdvertisement( absl::HexStringToBytes("002041420337C1C2C31BEE")); @@ -224,7 +220,7 @@ TEST(AdvertisementDecoder, DecodeBaseNpPublicAdvertisement) { TEST(AdvertisementDecoder, DecodeBaseNpWithTxAndActionFields) { std::string salt = "AB"; - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; auto result = decoder.DecodeAdvertisement( absl::HexStringToBytes("0020414203155036B04180")); @@ -243,12 +239,11 @@ TEST(AdvertisementDecoder, DecodeBaseNpWithTxAndActionFields) { } TEST(AdvertisementDecoder, DecodeBaseNpV0PublicIdentityWithTxAndActionFields) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; auto result = decoder.DecodeAdvertisement( // v0 public identity, power and action, action value 8 for active unlock. absl::HexStringToBytes("000315FF260080")); - EXPECT_OK(result); EXPECT_THAT(result->data_elements, UnorderedElementsAre( @@ -258,21 +253,8 @@ TEST(AdvertisementDecoder, DecodeBaseNpV0PublicIdentityWithTxAndActionFields) { DataElement(DataElement(ActionBit::kActiveUnlockAction)))); } -TEST(AdvertisementDecoder, - ScanForEncryptedIdentityIgnoresPublicIdentityAdvertisement) { - AdvertisementDecoder decoder( - {.account_name = std::string(kAccountName), - .identity_types = {IdentityType::IDENTITY_TYPE_PRIVATE, - IdentityType::IDENTITY_TYPE_TRUSTED, - IdentityType::IDENTITY_TYPE_PROVISIONED}}); - - EXPECT_THAT(decoder.DecodeAdvertisement( - absl::HexStringToBytes("00204142034650B04180")), - StatusIs(absl::StatusCode::kFailedPrecondition)); -} - TEST(AdvertisementDecoder, DecodeEddystone) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; std::string eddystone_id = absl::HexStringToBytes("A0A1A2A3A4A5A6A7A8A9B0B1B2B3B4B5B6B7B8B9"); @@ -288,7 +270,7 @@ TEST(AdvertisementDecoder, DecodeEddystone) { // TODO(b/238214467): Add more negative tests TEST(AdvertisementDecoder, UnsupportedDataElement) { std::string valid_header_and_salt = absl::HexStringToBytes("00204142"); - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; EXPECT_THAT(decoder.DecodeAdvertisement(valid_header_and_salt + absl::HexStringToBytes("0D")), @@ -296,7 +278,7 @@ TEST(AdvertisementDecoder, UnsupportedDataElement) { } TEST(AdvertisementDecoder, InvalidAdvertisementFieldTooShort) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; // 0x59 header means 5 bytes long Account Key Data but only 4 bytes follow. EXPECT_THAT( @@ -305,7 +287,7 @@ TEST(AdvertisementDecoder, InvalidAdvertisementFieldTooShort) { } TEST(AdvertisementDecoder, ZeroLengthPayload) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; // A action with type 0xA and no payload const absl::StatusOr result = @@ -316,14 +298,14 @@ TEST(AdvertisementDecoder, ZeroLengthPayload) { } TEST(AdvertisementDecoder, EmptyAdvertisement) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; EXPECT_THAT(decoder.DecodeAdvertisement(""), StatusIs(absl::StatusCode::kOutOfRange)); } TEST(AdvertisementDecoder, UnsupportedAdvertisementVersion) { - AdvertisementDecoder decoder(GetScanRequest()); + AdvertisementDecoder decoder; EXPECT_THAT(decoder.DecodeAdvertisement( absl::HexStringToBytes("012041420318CD29EEFF")), diff --git a/presence/implementation/advertisement_filter.cc b/presence/implementation/advertisement_filter.cc index 4bf0c1ba..36b1001f 100644 --- a/presence/implementation/advertisement_filter.cc +++ b/presence/implementation/advertisement_filter.cc @@ -18,7 +18,9 @@ #include #include "absl/types/variant.h" +#include "internal/platform/logging.h" #include "presence/data_element.h" +#include "presence/implementation/advertisement_decoder.h" #include "presence/scan_request.h" namespace nearby { @@ -54,21 +56,42 @@ bool ContainsAny(const std::vector& data_elements, } bool AdvertisementFilter::MatchesScanFilter( - const std::vector& data_elements) { + const Advertisement& advertisement) { + // Verify the identity is one requested in the scan_request. + // Per the Public API of scan_request, if identity_types provided in the + // scan_request is empty then decode advertisements of every identity type + auto requested_identity_types = scan_request_.identity_types; + if (!requested_identity_types.empty() && + !(std::find( + requested_identity_types.begin(), requested_identity_types.end(), + advertisement.identity_type) != requested_identity_types.end())) { + NEARBY_LOGS(INFO) + << "Skipping advertisement with identity type: " + << advertisement.identity_type + << " because that identity type was not requested in the scan " + "request"; + return false; + } + // The advertisement matches the scan request when it matches at least // one of the filters in the request. if (scan_request_.scan_filters.empty()) { return true; } + + // NOLINT is used to suppress google3-legacy-absl-backport lints because the + // the suggestion is not compatible with Chrome for (const auto& filter : scan_request_.scan_filters) { - if (absl::holds_alternative(filter)) { - if (MatchesScanFilter(data_elements, - absl::get(filter))) { + if (absl::holds_alternative(filter)) { // NOLINT + if (MatchesScanFilter(advertisement.data_elements, + absl::get(filter))) { // NOLINT return true; } - } else if (absl::holds_alternative(filter)) { - if (MatchesScanFilter(data_elements, - absl::get(filter))) { + } else if (absl::holds_alternative( // NOLINT + filter)) { + if (MatchesScanFilter( + advertisement.data_elements, + absl::get(filter))) { // NOLINT return true; } } diff --git a/presence/implementation/advertisement_filter.h b/presence/implementation/advertisement_filter.h index a70bd8f2..2e2db1e4 100644 --- a/presence/implementation/advertisement_filter.h +++ b/presence/implementation/advertisement_filter.h @@ -18,6 +18,7 @@ #include #include "presence/data_element.h" +#include "presence/implementation/advertisement_decoder.h" #include "presence/scan_request.h" namespace nearby { @@ -29,7 +30,7 @@ class AdvertisementFilter { // Returns true if the decoded advertisement in `data_elements` matches the // filters in `scan_request`. - bool MatchesScanFilter(const std::vector& data_elements); + bool MatchesScanFilter(const Advertisement& adv); private: bool MatchesScanFilter(const std::vector& data_elements, diff --git a/presence/implementation/advertisement_filter_test.cc b/presence/implementation/advertisement_filter_test.cc index 9ff7fc5e..7043abf0 100644 --- a/presence/implementation/advertisement_filter_test.cc +++ b/presence/implementation/advertisement_filter_test.cc @@ -27,6 +27,7 @@ #include "internal/platform/byte_array.h" #include "internal/proto/credential.pb.h" #include "presence/data_element.h" +#include "presence/implementation/advertisement_decoder.h" #include "presence/scan_request.h" #include "presence/scan_request_builder.h" @@ -42,7 +43,8 @@ TEST(AdvertisementFilter, MatchesScanFilterNoFilterPasses) { // A scan request without scan filters matches any advertisement EXPECT_TRUE(adv_filter.MatchesScanFilter( - {DataElement(DataElement::kPrivateIdentityFieldType, "payload")})); + {.data_elements = { + DataElement(DataElement::kPrivateIdentityFieldType, "payload")}})); EXPECT_TRUE(adv_filter.MatchesScanFilter({})); } @@ -56,14 +58,16 @@ TEST(AdvertisementFilter, MatchesPresenceScanFilter) { PresenceScanFilter filter = {.extended_properties = {model_id, salt}}; AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); EXPECT_FALSE(adv_filter.MatchesScanFilter({})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({salt})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, model_id})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, salt2, model_id})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({salt2, model_id})); + EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}})); + EXPECT_TRUE( + adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); + EXPECT_TRUE( + adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}})); + EXPECT_FALSE( + adv_filter.MatchesScanFilter({.data_elements = {salt2, model_id}})); } TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilter) { @@ -76,14 +80,48 @@ TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilter) { LegacyPresenceScanFilter filter = {.extended_properties = {model_id, salt}}; AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); - EXPECT_FALSE(adv_filter.MatchesScanFilter({})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({salt})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, model_id})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, salt2, model_id})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({salt2, model_id})); + EXPECT_FALSE(adv_filter.MatchesScanFilter(Advertisement{})); + EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}})); + EXPECT_TRUE( + adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); + EXPECT_TRUE( + adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}})); + EXPECT_FALSE(adv_filter.MatchesScanFilter( + Advertisement{.data_elements = {salt2, model_id}})); +} + +TEST(AdvertisementFilter, + EncryptedIdentityFilterIgnoresPublicIdentityAdvertisement) { + AdvertisementFilter adv_filter( + {.identity_types = {internal::IdentityType::IDENTITY_TYPE_PRIVATE, + internal::IdentityType::IDENTITY_TYPE_TRUSTED, + internal::IdentityType::IDENTITY_TYPE_PROVISIONED}}); + + EXPECT_FALSE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); + EXPECT_TRUE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE})); +} + +TEST(AdvertisementFilter, PublicIdentityFilterMatchesPublicIdentityAdv) { + AdvertisementFilter adv_filter( + {.identity_types = {internal::IdentityType::IDENTITY_TYPE_PUBLIC}}); + + EXPECT_TRUE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); + EXPECT_FALSE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE})); +} + +TEST(AdvertisementFilter, EmptyIdentityFilterMatchesAllAdvIdentityTypes) { + AdvertisementFilter adv_filter({}); + + EXPECT_TRUE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); + EXPECT_TRUE(adv_filter.MatchesScanFilter( + {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE})); } TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilterWithActions) { @@ -99,11 +137,12 @@ TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilterWithActions) { .extended_properties = {model_id, salt}}; AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); - EXPECT_FALSE(adv_filter.MatchesScanFilter({salt, model_id})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, ttt_action, model_id})); + EXPECT_FALSE( + adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); + EXPECT_TRUE(adv_filter.MatchesScanFilter( + {.data_elements = {salt, ttt_action, model_id}})); } TEST(AdvertisementFilter, MatchesMultipleFilters) { @@ -124,9 +163,10 @@ TEST(AdvertisementFilter, MatchesMultipleFilters) { .AddScanFilter(legacy_filter) .Build()); - EXPECT_TRUE(adv_filter.MatchesScanFilter({model_id})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({salt, ttt_action})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({ttt_action})); + EXPECT_TRUE(adv_filter.MatchesScanFilter({.data_elements = {model_id}})); + EXPECT_TRUE( + adv_filter.MatchesScanFilter({.data_elements = {salt, ttt_action}})); + EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {ttt_action}})); } } // namespace diff --git a/presence/implementation/scan_manager.cc b/presence/implementation/scan_manager.cc index 9e0759d7..ebd4745e 100644 --- a/presence/implementation/scan_manager.cc +++ b/presence/implementation/scan_manager.cc @@ -14,24 +14,29 @@ #include "presence/implementation/scan_manager.h" -#include -#include +#include + +#include #include #include #include #include +#include "absl/base/thread_annotations.h" #include "absl/status/status.h" -#include "absl/types/variant.h" +#include "absl/strings/string_view.h" #include "internal/platform/future.h" #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/credential_callbacks.h" #include "internal/platform/implementation/crypto.h" -#include "internal/platform/uuid.h" +#include "internal/platform/logging.h" #include "presence//implementation/advertisement_filter.h" +#include "presence/data_element.h" #include "presence/data_types.h" +#include "presence/device_motion.h" #include "presence/implementation/advertisement_decoder.h" #include "presence/implementation/mediums/ble.h" +#include "presence/presence_action.h" #include "presence/presence_device.h" #include "presence/scan_request.h" @@ -75,7 +80,7 @@ ScanSessionId ScanManager::StartScan(ScanRequest scan_request, {id, ScanSessionState{ .request = scan_request, .callback = std::move(scan_callback), - .decoder = AdvertisementDecoder(scan_request), + .decoder = AdvertisementDecoder(), .advertisement_filter = AdvertisementFilter(scan_request), .scanning_session = mediums_->GetBle().StartScanning( scan_request, std::move(callback))}}); @@ -113,8 +118,8 @@ void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, // This advertisement is not relevant to the current element, skip. return; } - if (it->second.advertisement_filter.MatchesScanFilter( - advert->data_elements)) { + + if (it->second.advertisement_filter.MatchesScanFilter(*advert)) { internal::DeviceIdentityMetaData device_identity_metadata; device_identity_metadata.set_bluetooth_mac_address( std::string(remote_address)); @@ -194,13 +199,19 @@ void ScanManager::FetchCredentials(ScanSessionId id, void ScanManager::UpdateCredentials(ScanSessionId id, IdentityType identity_type, std::vector credentials) { + // Credentials should never get fetched for PUBLIC of No-Identity requests + assert(identity_type != internal::IDENTITY_TYPE_UNSPECIFIED); + assert(identity_type != internal::IDENTITY_TYPE_PUBLIC); + 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); + session.decoder = AdvertisementDecoder(&session.credentials); } int ScanManager::ScanningCallbacksLengthForTest() { diff --git a/presence/implementation/scan_manager_test.cc b/presence/implementation/scan_manager_test.cc index d5f00288..f211a31c 100644 --- a/presence/implementation/scan_manager_test.cc +++ b/presence/implementation/scan_manager_test.cc @@ -14,9 +14,7 @@ #include "presence/implementation/scan_manager.h" -#include - -#include +#include #include #include #include @@ -25,17 +23,30 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" -#include "absl/time/time.h" +#include "absl/types/variant.h" #include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/credential_callbacks.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" +#include "internal/proto/credential.proto.h" +#include "presence/broadcast_request.h" +#include "presence/data_element.h" +#include "presence/data_types.h" #include "presence/implementation/advertisement_factory.h" #include "presence/implementation/base_broadcast_request.h" #include "presence/implementation/credential_manager_impl.h" +#include "presence/implementation/mediums/advertisement_data.h" #include "presence/implementation/mediums/ble.h" #include "presence/implementation/mediums/mediums.h" +#include "presence/implementation/mock_credential_manager.h" +#include "presence/power_mode.h" +#include "presence/presence_action.h" +#include "presence/presence_device.h" +#include "presence/scan_request.h" namespace nearby { namespace presence { @@ -325,6 +336,90 @@ TEST_F(ScanManagerTest, NoDeviceFoundAfterStopScan) { executor_.Shutdown(); } +internal::SharedCredential 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({0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81, + 0x75, 0x8D, 0x97, 0x6B, 0x5A, 0x62, 0x1C, 0x05, + 0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE, 0x59, 0x87, + 0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E}); + internal::SharedCredential public_credential; + public_credential.set_key_seed(seed.AsStringView()); + public_credential.set_metadata_encryption_key_tag_v0( + known_mac.AsStringView()); + return public_credential; +} + +std::vector BuildSharedCredentials() { + return {GetPublicCredential()}; +} + +internal::LocalCredential CreateLocalCredential( + internal::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}); + + internal::LocalCredential private_credential; + private_credential.set_identity_type(identity_type); + private_credential.set_key_seed(seed.AsStringView()); + private_credential.set_metadata_encryption_key_v0( + metadata_key.AsStringView()); + return private_credential; +} + +TEST_F(ScanManagerTest, ScanningE2EWithEncryptedAdvertisementAndCredentials) { + Mediums mediums; + auto mock_credential_manager = MockCredentialManager(); + EXPECT_CALL(mock_credential_manager, GetPublicCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + PublicCredentialType public_credential_type, + GetPublicCredentialsResultCallback callback) { + callback.credentials_fetched_cb(BuildSharedCredentials()); + }); + ScanManager manager(mediums, mock_credential_manager, executor_); + + // Set up advertiser to broadcast a private identity adv + nearby::BluetoothAdapter server_adapter; + Ble ble2(server_adapter); + PresenceBroadcast::BroadcastSection section = { + .identity = internal::IdentityType::IDENTITY_TYPE_PRIVATE, + .extended_properties = MakeDefaultExtendedProperties(), + .account_name = "Test account"}; + PresenceBroadcast presence_request = {.sections = {section}}; + BroadcastRequest input = {.tx_power = 30, .variant = presence_request}; + absl::StatusOr request = + BaseBroadcastRequest::Create(input); + EXPECT_OK(request); + absl::StatusOr advertisement = + AdvertisementFactory().CreateAdvertisement( + request.value(), + CreateLocalCredential(internal::IdentityType::IDENTITY_TYPE_PRIVATE)); + EXPECT_OK(advertisement); + std::unique_ptr session = ble2.StartAdvertising( + advertisement.value(), PowerMode::kLowPower, + AdvertisingCallback{.start_advertising_result = [](absl::Status) {}}); + env_.Sync(); + + auto scan_request = MakeDefaultScanRequest(); + scan_request.identity_types = { + nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE}; + + // Start scanning + ScanSessionId scan_session = + manager.StartScan(scan_request, MakeDefaultScanCallback()); + EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); + EXPECT_TRUE(start_latch_.Await().Ok()); + EXPECT_TRUE(found_latch_.Await().Ok()); + manager.StopScan(scan_session); + EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); +} + } // namespace } // namespace presence } // namespace nearby