[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
This commit is contained in:
Juliet Levesque
2024-02-02 17:23:41 -08:00
committed by Copybara-Service
parent 633b95b1a0
commit 6b2f7fa94e
8 changed files with 305 additions and 23 deletions
-1
View File
@@ -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",
],
+1
View File
@@ -93,6 +93,7 @@ cc_library(
"//internal/network:__subpackages__",
"//internal/platform:__pkg__",
"//internal/platform/implementation:__subpackages__",
"//presence:__subpackages__",
"//presence/implementation:__subpackages__",
],
deps = [
+13
View File
@@ -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": [
@@ -16,8 +16,11 @@
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_
#include <memory>
#include <vector>
#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<ScanSessionId>, StartScan,
(ScanRequest scan_request, ScanCallback callback), (override));
MOCK_METHOD(void, StopScan, (ScanSessionId session_id), (override));
MOCK_METHOD(absl::StatusOr<BroadcastSessionId>, 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<nearby::internal::IdentityType>& 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<nearby::internal::SharedCredential>&
remote_public_creds,
UpdateRemotePublicCredentialsCallback credentials_updated_cb),
(override));
MOCK_METHOD(void, GetLocalCredentials,
(const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback),
(override));
};
} // namespace presence
+128
View File
@@ -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 <optional>
#include <string>
#include <vector>
#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<internal::LocalCredential> GetValidCredential(
std::vector<internal::LocalCredential> 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<AuthenticationStatus> 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<AuthenticationStatus> result = response.Get();
CHECK(result.ok());
NEARBY_LOGS(INFO) << "Future:[" << __func__ << "] completed with status:"
<< AuthenticationErrorToString(result.result());
return result.result();
}
} // namespace presence
} // namespace nearby
+17 -6
View File
@@ -15,6 +15,10 @@
#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_
#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_
#include <string>
#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_;
};
+114 -12
View File
@@ -14,14 +14,25 @@
#include "presence/presence_device_provider.h"
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#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<PresenceDeviceProvider>(&mock_service_controller_);
}
protected:
MockServiceController mock_service_controller_;
std::unique_ptr<PresenceDeviceProvider> provider_;
};
TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) {
EXPECT_FALSE(std::is_trivially_constructible<PresenceDeviceProvider>::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<const PresenceDevice*>(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<const PresenceDevice*>(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<nearby::internal::LocalCredential> 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<nearby::internal::LocalCredential> 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
+3 -2
View File
@@ -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<ServiceControllerImpl>(
&executor_, &credential_manager_, &scan_manager_, &broadcast_manager_);
provider_ = std::make_unique<PresenceDeviceProvider>(
service_controller_->GetLocalDeviceMetadata());
provider_ =
std::make_unique<PresenceDeviceProvider>(service_controller_.get());
}
std::unique_ptr<PresenceClient> PresenceServiceImpl::CreatePresenceClient() {