From 6b2f7fa94e345d759b6c13df3a93a983e182a203 Mon Sep 17 00:00:00 2001 From: Juliet Levesque Date: Fri, 2 Feb 2024 13:25:09 -0800 Subject: [PATCH] [Nearby Presence] Fetch and select correct local credentials Fetch the local credentials and select the correct local credential by verifying the validity of the time. This CL will be followed by using the selected local credential for authentication. See go/cros-nearby-presence-np-nc-authentication for details. PiperOrigin-RevId: 603772266 --- internal/interop/BUILD | 1 - internal/platform/implementation/BUILD | 1 + presence/BUILD | 13 ++ .../implementation/mock_service_controller.h | 31 ++++- presence/presence_device_provider.cc | 128 ++++++++++++++++++ presence/presence_device_provider.h | 23 +++- presence/presence_device_provider_test.cc | 126 +++++++++++++++-- presence/presence_service_impl.cc | 5 +- 8 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 presence/presence_device_provider.cc diff --git a/internal/interop/BUILD b/internal/interop/BUILD index 9abb983b..a4f9e5f3 100644 --- a/internal/interop/BUILD +++ b/internal/interop/BUILD @@ -24,7 +24,6 @@ cc_library( deps = [ ":authentication_transport_interface", "//internal/platform:connection_info", - "//internal/platform:types", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:variant", ], diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index a8194618..b36d802e 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -93,6 +93,7 @@ cc_library( "//internal/network:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", + "//presence:__subpackages__", "//presence/implementation:__subpackages__", ], deps = [ diff --git a/presence/BUILD b/presence/BUILD index 8969a12c..8fc54766 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -19,6 +19,7 @@ cc_library( name = "presence", srcs = [ "presence_client_impl.cc", + "presence_device_provider.cc", "presence_service_impl.cc", ], hdrs = [ @@ -30,13 +31,19 @@ cc_library( ], deps = [ ":types", + "//internal/interop:authentication_transport_interface", "//internal/interop:device", + "//internal/platform:base", "//internal/platform:types", + "//internal/platform/implementation:types", "//internal/proto:metadata_cc_proto", "//presence/implementation:internal", # build_cleaner: keep "//presence/implementation/mediums", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", ], ) @@ -175,12 +182,18 @@ cc_test( deps = [ ":presence", ":types", + "//internal/interop:authentication_transport_interface", + "//internal/interop:device", "//internal/platform:test_util", "//internal/platform:types", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:types", "//internal/proto:metadata_cc_proto", + "//presence/implementation:internal_test", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ] + select({ "@platforms//os:windows": [ diff --git a/presence/implementation/mock_service_controller.h b/presence/implementation/mock_service_controller.h index 84960f12..7fc3b7e9 100644 --- a/presence/implementation/mock_service_controller.h +++ b/presence/implementation/mock_service_controller.h @@ -16,8 +16,11 @@ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_ #include +#include #include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/credential_callbacks.h" #include "presence/implementation/service_controller.h" namespace nearby { @@ -33,11 +36,35 @@ class MockServiceController : public ServiceController { MOCK_METHOD(absl::StatusOr, StartScan, (ScanRequest scan_request, ScanCallback callback), (override)); + MOCK_METHOD(void, StopScan, (ScanSessionId session_id), (override)); MOCK_METHOD(absl::StatusOr, StartBroadcast, (BroadcastRequest broadcast_request, BroadcastCallback callback), (override)); - - private: + MOCK_METHOD(void, StopBroadcast, (BroadcastSessionId session_id), (override)); + MOCK_METHOD( + void, UpdateLocalDeviceMetadata, + (const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb), + (override)); + MOCK_METHOD(::nearby::internal::Metadata, GetLocalDeviceMetadata, (), + (override)); + MOCK_METHOD(void, GetLocalPublicCredentials, + (const CredentialSelector& credential_selector, + GetPublicCredentialsResultCallback callback), + (override)); + MOCK_METHOD(void, UpdateRemotePublicCredentials, + (absl::string_view manager_app_id, absl::string_view account_name, + const std::vector& + remote_public_creds, + UpdateRemotePublicCredentialsCallback credentials_updated_cb), + (override)); + MOCK_METHOD(void, GetLocalCredentials, + (const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback), + (override)); }; } // namespace presence diff --git a/presence/presence_device_provider.cc b/presence/presence_device_provider.cc new file mode 100644 index 00000000..3b4ec254 --- /dev/null +++ b/presence/presence_device_provider.cc @@ -0,0 +1,128 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "presence/presence_device_provider.h" + +#include +#include +#include + +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "internal/interop/authentication_transport.h" +#include "internal/interop/device.h" +#include "internal/interop/device_provider.h" +#include "internal/platform/exception.h" +#include "internal/platform/future.h" +#include "internal/platform/implementation/system_clock.h" +#include "internal/platform/logging.h" +#include "presence/implementation/service_controller.h" + +namespace nearby { +namespace presence { + +namespace { + +// TODO(b/317215548): Use Status code rather than custom defined +// authentication status. +std::string AuthenticationErrorToString(AuthenticationStatus status) { + switch (status) { + case AuthenticationStatus::kUnknown: + return "AuthenticationStatus::kUnknown"; + case AuthenticationStatus::kSuccess: + return "AuthenticationStatus::kSuccess"; + case AuthenticationStatus::kFailure: + return "AuthenticationStatus::kFailure"; + } + return "AuthenticationStatus::kUnknown"; +} + +std::optional GetValidCredential( + std::vector local_credentials) { + absl::Time now = SystemClock::ElapsedRealtime(); + for (auto& credential : local_credentials) { + if (absl::FromUnixMillis(credential.start_time_millis()) <= now && + absl::FromUnixMillis(credential.end_time_millis()) > now) { + return credential; + } + } + return std::nullopt; +} + +} // namespace + +PresenceDeviceProvider::PresenceDeviceProvider( + ServiceController* service_controller) + : service_controller_(*service_controller), + device_{service_controller_.GetLocalDeviceMetadata()} {} + +AuthenticationStatus PresenceDeviceProvider::AuthenticateAsInitiator( + const NearbyDevice& remote_device, absl::string_view shared_secret, + const AuthenticationTransport& authentication_transport) const { + Future response; + + // 1. Fetch the local credentials and select the correct one to use + // for authentication by calling `GetValidCredential()`, which + // iterates over the returned list and returns the local credential + // that corresponds with the current time. + // + // TODO(b/304843571): Add support for additional IdentityTypes. Currently, + // only `IDENTITY_TYPE_PRIVATE` is supported in order to unblock Nearby + // Presence MVP. + service_controller_.GetLocalCredentials( + /*credential_selector=*/{.manager_app_id = manager_app_id_, + .account_name = + device_.GetMetadata().account_name(), + .identity_type = ::nearby::internal:: + IdentityType::IDENTITY_TYPE_PRIVATE}, + /*callback=*/{ + .credentials_fetched_cb = [&response](auto status_or_credentials) { + if (!status_or_credentials.ok()) { + NEARBY_LOGS(INFO) + << __func__ << ": failure to fetch local credentials"; + response.Set(AuthenticationStatus::kFailure); + return; + } + + auto credential = GetValidCredential(status_or_credentials.value()); + if (!credential.has_value()) { + NEARBY_LOGS(INFO) + << __func__ << ": failure to find a valid local credential"; + response.Set(AuthenticationStatus::kFailure); + return; + } + + // TODO(b/282027237): Continue with the following steps, which will + // be done in follow up CL's. + // 2. Construct the frame and write to the + // |authentication_transport|. + // 3. Read the message from the remote device via + // |authentication_transport|. + // 4. Return the status of the authentication to the callers. + // For now, return success on the Future. + response.Set(AuthenticationStatus::kSuccess); + }}); + + NEARBY_LOGS(INFO) << __func__ << ": Waiting for future to complete"; + ExceptionOr result = response.Get(); + CHECK(result.ok()); + + NEARBY_LOGS(INFO) << "Future:[" << __func__ << "] completed with status:" + << AuthenticationErrorToString(result.result()); + return result.result(); +} + +} // namespace presence +} // namespace nearby diff --git a/presence/presence_device_provider.h b/presence/presence_device_provider.h index 6a627bdc..e343a548 100644 --- a/presence/presence_device_provider.h +++ b/presence/presence_device_provider.h @@ -15,6 +15,10 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ #define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ +#include + +#include "absl/strings/string_view.h" +#include "internal/interop/device.h" #include "internal/interop/device_provider.h" #include "internal/proto/metadata.pb.h" #include "presence/presence_device.h" @@ -22,18 +26,24 @@ namespace nearby { namespace presence { +class ServiceController; + class PresenceDeviceProvider : public NearbyDeviceProvider { public: - explicit PresenceDeviceProvider(::nearby::internal::Metadata metadata) - : device_{metadata} {} + explicit PresenceDeviceProvider(ServiceController* service_controller); const NearbyDevice* GetLocalDevice() override { return &device_; } + + // To authenticate as an initiator (when the device is in the scanning role), + // the PresenceDeviceProvider will block and: + // 1. Fetch the local credentials and select the correct one to use for + // authentication. + // 2. Construct the frame and write to the |authentication_transport|. + // 3. Read the message from the remote device via |authentication_transport|. + // 4. Return the status of the authentication to the callers. AuthenticationStatus AuthenticateAsInitiator( const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const override { - // TODO(b/282027237): Implement. - return AuthenticationStatus::kUnknown; - } + const AuthenticationTransport& authentication_transport) const override; AuthenticationStatus AuthenticateAsResponder( absl::string_view shared_secret, @@ -55,6 +65,7 @@ class PresenceDeviceProvider : public NearbyDeviceProvider { } private: + ServiceController& service_controller_; PresenceDevice device_; std::string manager_app_id_; }; diff --git a/presence/presence_device_provider_test.cc b/presence/presence_device_provider_test.cc index b7a6eb79..cabab771 100644 --- a/presence/presence_device_provider_test.cc +++ b/presence/presence_device_provider_test.cc @@ -14,14 +14,25 @@ #include "presence/presence_device_provider.h" +#include +#include #include +#include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "internal/interop/authentication_transport.h" +#include "internal/interop/device_provider.h" +#include "internal/platform/implementation/credential_callbacks.h" +#include "internal/platform/implementation/system_clock.h" #include "internal/proto/metadata.pb.h" #include "internal/proto/metadata.proto.h" +#include "presence/implementation/mock_service_controller.h" #include "presence/presence_device.h" namespace nearby { @@ -43,37 +54,128 @@ Metadata CreateTestMetadata() { return metadata; } -TEST(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { +nearby::internal::LocalCredential CreateValidLocalCredential() { + nearby::internal::LocalCredential credential; + absl::Time now = SystemClock::ElapsedRealtime(); + credential.set_start_time_millis(absl::ToUnixMillis(now)); + credential.set_end_time_millis(absl::ToUnixMillis(now + absl::Minutes(10))); + return credential; +} + +nearby::internal::LocalCredential CreateInvalidLocalCredential() { + nearby::internal::LocalCredential credential; + absl::Time now = SystemClock::ElapsedRealtime(); + credential.set_start_time_millis(absl::ToUnixMillis(now - absl::Minutes(30))); + credential.set_end_time_millis(absl::ToUnixMillis(now - absl::Minutes(10))); + return credential; +} + +class MockAuthenticationTransport : public AuthenticationTransport { + MOCK_METHOD(void, WriteMessage, (absl::string_view), (const override)); + MOCK_METHOD(std::string, ReadMessage, (), (const override)); +}; + +class PresenceDeviceProviderTest : public ::testing::Test { + public: + PresenceDeviceProviderTest() { + ON_CALL(mock_service_controller_, GetLocalDeviceMetadata) + .WillByDefault(testing::Return(CreateTestMetadata())); + provider_ = + std::make_unique(&mock_service_controller_); + } + + protected: + MockServiceController mock_service_controller_; + std::unique_ptr provider_; +}; + +TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { EXPECT_FALSE(std::is_trivially_constructible::value); } -TEST(PresenceDeviceProviderTest, DeviceProviderWorks) { - PresenceDeviceProvider provider(CreateTestMetadata()); - auto device = provider.GetLocalDevice(); +TEST_F(PresenceDeviceProviderTest, DeviceProviderWorks) { + auto device = provider_->GetLocalDevice(); ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); auto presence_device = static_cast(device); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), CreateTestMetadata().SerializeAsString()); } -TEST(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { - PresenceDeviceProvider provider(CreateTestMetadata()); - auto device = provider.GetLocalDevice(); +TEST_F(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { + auto device = provider_->GetLocalDevice(); ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); auto presence_device = static_cast(device); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), CreateTestMetadata().SerializeAsString()); Metadata new_metadata = CreateTestMetadata(); new_metadata.set_device_name("NP interop device"); - provider.UpdateMetadata(new_metadata); + provider_->UpdateMetadata(new_metadata); EXPECT_EQ(presence_device->GetMetadata().SerializeAsString(), new_metadata.SerializeAsString()); } -TEST(PresenceDeviceProviderTest, SetManagerAppId) { - PresenceDeviceProvider provider(CreateTestMetadata()); - provider.SetManagerAppId(kManagerAppId); - EXPECT_EQ(provider.GetManagerAppId(), kManagerAppId); +TEST_F(PresenceDeviceProviderTest, SetGetManagerAppId) { + provider_->SetManagerAppId(kManagerAppId); + EXPECT_EQ(provider_->GetManagerAppId(), kManagerAppId); +} + +TEST_F(PresenceDeviceProviderTest, + AuthenticateAsInitiatorFails_FailToFetchCredentials) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::move(callback.credentials_fetched_cb)( + absl::Status(absl::StatusCode::kCancelled, /*msg=*/"")); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + EXPECT_EQ(AuthenticationStatus::kFailure, status); +} + +TEST_F(PresenceDeviceProviderTest, + AuthenticateAsInitiatorFails_NoValidCredentials) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::vector credentials; + credentials.push_back(CreateInvalidLocalCredential()); + credentials.push_back(CreateInvalidLocalCredential()); + credentials.push_back(CreateInvalidLocalCredential()); + std::move(callback.credentials_fetched_cb)(credentials); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + EXPECT_EQ(AuthenticationStatus::kFailure, status); +} + +TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiatorSuccess) { + EXPECT_CALL(mock_service_controller_, GetLocalCredentials) + .WillOnce([&](const CredentialSelector& credential_selector, + GetLocalCredentialsResultCallback callback) { + std::vector credentials = { + CreateInvalidLocalCredential(), CreateValidLocalCredential(), + CreateInvalidLocalCredential()}; + std::move(callback.credentials_fetched_cb)(credentials); + }); + + PresenceDevice remote_device{CreateTestMetadata()}; + MockAuthenticationTransport authentication_transport; + auto status = provider_->AuthenticateAsInitiator( + /*remote_device=*/remote_device, /*shared_secret=*/"", + /*authentication_transport=*/authentication_transport); + + // TODO(b/282027237): Once additional logic is added in follow up CL's + // to continue the authentication, add coverage in this unit test for a + // success case. + EXPECT_EQ(AuthenticationStatus::kSuccess, status); } } // namespace diff --git a/presence/presence_service_impl.cc b/presence/presence_service_impl.cc index 8b14d936..660bb609 100644 --- a/presence/presence_service_impl.cc +++ b/presence/presence_service_impl.cc @@ -22,6 +22,7 @@ #include "presence/data_types.h" #include "presence/implementation/service_controller_impl.h" #include "presence/presence_client_impl.h" +#include "presence/presence_device_provider.h" namespace nearby { namespace presence { @@ -29,8 +30,8 @@ namespace presence { PresenceServiceImpl::PresenceServiceImpl() { service_controller_ = std::make_unique( &executor_, &credential_manager_, &scan_manager_, &broadcast_manager_); - provider_ = std::make_unique( - service_controller_->GetLocalDeviceMetadata()); + provider_ = + std::make_unique(service_controller_.get()); } std::unique_ptr PresenceServiceImpl::CreatePresenceClient() {