[Nearby Presence] Construct Presence Frame and write to remote device

Constructs a Presence frame using BuildSignedMessageAsInitiator() and write to the AuthenticationTransport for authentication. In follow up CL's, authentication protocol will read the message from the remote device and verify it - see go/cros-nearby-presence-np-nc-authentication for details.

PiperOrigin-RevId: 603776791
This commit is contained in:
Juliet Levesque
2024-02-02 17:24:47 -08:00
committed by Copybara-Service
parent 6b2f7fa94e
commit 58452f4049
4 changed files with 196 additions and 42 deletions
+6
View File
@@ -36,9 +36,11 @@ cc_library(
"//internal/platform:base",
"//internal/platform:types",
"//internal/platform/implementation:types",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence/implementation:internal", # build_cleaner: keep
"//presence/implementation/mediums",
"//presence/proto:presence_frame_cc_proto",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
@@ -182,14 +184,18 @@ cc_test(
deps = [
":presence",
":types",
"//internal/crypto",
"//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:credential_cc_proto",
"//internal/proto:local_credential_cc_proto",
"//internal/proto:metadata_cc_proto",
"//presence/implementation:internal_test",
"//presence/proto:presence_frame_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
+109 -25
View File
@@ -16,6 +16,7 @@
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "absl/log/check.h"
@@ -28,13 +29,18 @@
#include "internal/platform/future.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/logging.h"
#include "presence/implementation/connection_authenticator.h"
#include "presence/implementation/service_controller.h"
#include "presence/presence_device.h"
#include "presence/proto/presence_frame.pb.h"
namespace nearby {
namespace presence {
namespace {
constexpr int kPresenceVersion = 1;
// TODO(b/317215548): Use Status code rather than custom defined
// authentication status.
std::string AuthenticationErrorToString(AuthenticationStatus status) {
@@ -61,6 +67,33 @@ std::optional<internal::LocalCredential> GetValidCredential(
return std::nullopt;
}
PresenceAuthenticationFrame BuildInitiatorPresenceAuthenticationFrame(
ConnectionAuthenticator::InitiatorData initiator_data_variant) {
// It is expected that the `PresenceAuthenticationFrame` built for the
// initator role always is `TwoWayInitiatorData`, since the local device is
// always expected to have a valid local credential to be used, and this is
// verified in AuthenticateAsInitiator(), which returns failure if no valid
// local credential is found (which is expected to not happen, since valid
// credentials will be generated if needed before the authentiation is
// called).
//
// Note: std::holds_alternative and std::get cannot be used here because
// they are not supported in Chromium.
DCHECK(std::holds_alternative<ConnectionAuthenticator::TwoWayInitiatorData>(
initiator_data_variant));
auto two_way_initiator_data =
std::get<ConnectionAuthenticator::TwoWayInitiatorData>(
initiator_data_variant);
PresenceAuthenticationFrame authentication_frame;
authentication_frame.set_version(kPresenceVersion);
authentication_frame.set_private_key_signature(
two_way_initiator_data.private_key_signature);
authentication_frame.set_shared_credential_id_hash(
two_way_initiator_data.shared_credential_hash);
return authentication_frame;
}
} // namespace
PresenceDeviceProvider::PresenceDeviceProvider(
@@ -87,33 +120,45 @@ AuthenticationStatus PresenceDeviceProvider::AuthenticateAsInitiator(
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;
}
/*callback=*/{.credentials_fetched_cb = [this, &response, &remote_device,
&authentication_transport,
&shared_secret](
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;
}
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);
}});
// 2. Construct the frame and write to the
// |authentication_transport|.
if (!WriteToRemoteDevice(
/*remote_device=*/&remote_device,
/*shared_secret=*/shared_secret,
/*authentication_transport=*/authentication_transport,
/*local_credential=*/credential.value(),
/*response=*/response)) {
response.Set(AuthenticationStatus::kFailure);
return;
}
// TODO(b/282027237): Continue with the following steps, which will
// be done in follow up CL's.
// 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();
@@ -124,5 +169,44 @@ AuthenticationStatus PresenceDeviceProvider::AuthenticateAsInitiator(
return result.result();
}
bool PresenceDeviceProvider::WriteToRemoteDevice(
const NearbyDevice* remote_device, absl::string_view shared_secret,
const AuthenticationTransport& authentication_transport,
const internal::LocalCredential& local_credential,
Future<AuthenticationStatus>& response) const {
// Cast the |remote_device| to a `PresenceDevice` in order to retrieve
// it's shared credentials, which is safe to do since the |remote_device|
// passed to the `PresenceDeviceProvider` will always be a `PresenceDevice`.
const PresenceDevice* remote_presence_device =
static_cast<const PresenceDevice*>(remote_device);
auto shared_credential = remote_presence_device->GetDecryptSharedCredential();
if (!shared_credential.has_value()) {
NEARBY_LOGS(INFO)
<< __func__
<< ": failure due to no decrypt shared credential from remote device";
return false;
}
auto status_or_initiator_data =
connection_authenticator_.BuildSignedMessageAsInitiator(
/*ukey2_secret=*/shared_secret, /*local_credential=*/local_credential,
/*shared_credential=*/shared_credential.value());
// TODO(b/317219088): Add test covereage for when
// `ConnectionAuthenticator::BuildSignedMessageAsInitiator()` fails.
if (!status_or_initiator_data.ok()) {
NEARBY_LOGS(INFO) << __func__
<< ": failure to build signed message as initiator";
return false;
}
// Once the initiator data has been built, construct the Presence frame
// which will be written to the device with the built data.
authentication_transport.WriteMessage(
BuildInitiatorPresenceAuthenticationFrame(
status_or_initiator_data.value())
.SerializeAsString());
return true;
}
} // namespace presence
} // namespace nearby
+12
View File
@@ -18,9 +18,13 @@
#include <string>
#include "absl/strings/string_view.h"
#include "internal/interop/authentication_transport.h"
#include "internal/interop/device.h"
#include "internal/interop/device_provider.h"
#include "internal/platform/future.h"
#include "internal/proto/local_credential.pb.h"
#include "internal/proto/metadata.pb.h"
#include "presence/implementation/connection_authenticator.h"
#include "presence/presence_device.h"
namespace nearby {
@@ -65,10 +69,18 @@ class PresenceDeviceProvider : public NearbyDeviceProvider {
}
private:
bool WriteToRemoteDevice(
const NearbyDevice* remote_device, absl::string_view shared_secret,
const AuthenticationTransport& authentication_transport,
const internal::LocalCredential& local_credential,
Future<AuthenticationStatus>& response) const;
ServiceController& service_controller_;
PresenceDevice device_;
std::string manager_app_id_;
ConnectionAuthenticator connection_authenticator_;
};
} // namespace presence
} // namespace nearby
+69 -17
View File
@@ -24,16 +24,20 @@
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/crypto/ed25519.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/credential.pb.h"
#include "internal/proto/local_credential.pb.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"
#include "presence/proto/presence_frame.pb.h"
namespace nearby {
namespace presence {
@@ -42,6 +46,9 @@ using ::nearby::internal::Metadata;
constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1";
constexpr absl::string_view kManagerAppId = "test_app_id";
constexpr char kUkey2Secret[] = {0x34, 0x56, 0x78, 0x90};
constexpr char kKeySeed[] = {1, 2, 3, 4, 5, 6, 7, 8};
constexpr int kPresenceVersion = 1;
Metadata CreateTestMetadata() {
Metadata metadata;
@@ -54,15 +61,19 @@ Metadata CreateTestMetadata() {
return metadata;
}
nearby::internal::LocalCredential CreateValidLocalCredential() {
nearby::internal::LocalCredential CreateValidLocalCredential(
const crypto::Ed25519KeyPair& key_pair) {
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)));
credential.mutable_connection_signing_key()->set_key(
absl::StrCat(key_pair.private_key, key_pair.public_key));
credential.set_key_seed(kKeySeed);
return credential;
}
nearby::internal::LocalCredential CreateInvalidLocalCredential() {
nearby::internal::LocalCredential CreateExpiredLocalCredential() {
nearby::internal::LocalCredential credential;
absl::Time now = SystemClock::ElapsedRealtime();
credential.set_start_time_millis(absl::ToUnixMillis(now - absl::Minutes(30)));
@@ -70,7 +81,17 @@ nearby::internal::LocalCredential CreateInvalidLocalCredential() {
return credential;
}
internal::SharedCredential BuildSharedCredential(
const crypto::Ed25519KeyPair& key_pair) {
internal::SharedCredential shared_credential;
shared_credential.set_connection_signature_verification_key(
key_pair.public_key);
shared_credential.set_key_seed(kKeySeed);
return shared_credential;
}
class MockAuthenticationTransport : public AuthenticationTransport {
public:
MOCK_METHOD(void, WriteMessage, (absl::string_view), (const override));
MOCK_METHOD(std::string, ReadMessage, (), (const override));
};
@@ -84,9 +105,15 @@ class PresenceDeviceProviderTest : public ::testing::Test {
std::make_unique<PresenceDeviceProvider>(&mock_service_controller_);
}
void SetUp() override {
auto key_pair_or_status = crypto::Ed25519Signer::CreateNewKeyPair();
ASSERT_OK_AND_ASSIGN(key_pair_, key_pair_or_status);
}
protected:
MockServiceController mock_service_controller_;
std::unique_ptr<PresenceDeviceProvider> provider_;
crypto::Ed25519KeyPair key_pair_;
};
TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) {
@@ -125,13 +152,13 @@ TEST_F(PresenceDeviceProviderTest,
.WillOnce([&](const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
std::move(callback.credentials_fetched_cb)(
absl::Status(absl::StatusCode::kCancelled, /*msg=*/""));
absl::Status(absl::StatusCode::kCancelled, /*msg=*/std::string()));
});
PresenceDevice remote_device{CreateTestMetadata()};
PresenceDevice remote_device(CreateTestMetadata());
MockAuthenticationTransport authentication_transport;
auto status = provider_->AuthenticateAsInitiator(
/*remote_device=*/remote_device, /*shared_secret=*/"",
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
/*authentication_transport=*/authentication_transport);
EXPECT_EQ(AuthenticationStatus::kFailure, status);
}
@@ -142,34 +169,59 @@ TEST_F(PresenceDeviceProviderTest,
.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());
credentials.push_back(CreateExpiredLocalCredential());
std::move(callback.credentials_fetched_cb)(credentials);
});
PresenceDevice remote_device{CreateTestMetadata()};
PresenceDevice remote_device(CreateTestMetadata());
MockAuthenticationTransport authentication_transport;
auto status = provider_->AuthenticateAsInitiator(
/*remote_device=*/remote_device, /*shared_secret=*/"",
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
/*authentication_transport=*/authentication_transport);
EXPECT_EQ(AuthenticationStatus::kFailure, status);
}
TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiatorSuccess) {
TEST_F(PresenceDeviceProviderTest,
AuthenticateAsInitiator_NoRemoteSharedCredential) {
EXPECT_CALL(mock_service_controller_, GetLocalCredentials)
.WillOnce([&](const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
std::vector<nearby::internal::LocalCredential> credentials = {
CreateInvalidLocalCredential(), CreateValidLocalCredential(),
CreateInvalidLocalCredential()};
std::vector<nearby::internal::LocalCredential> credentials;
credentials.push_back(CreateValidLocalCredential(key_pair_));
std::move(callback.credentials_fetched_cb)(credentials);
});
PresenceDevice remote_device{CreateTestMetadata()};
PresenceDevice remote_device(CreateTestMetadata());
MockAuthenticationTransport authentication_transport;
auto status = provider_->AuthenticateAsInitiator(
/*remote_device=*/remote_device, /*shared_secret=*/"",
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
/*authentication_transport=*/authentication_transport);
EXPECT_EQ(AuthenticationStatus::kFailure, status);
}
TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiator_Success) {
EXPECT_CALL(mock_service_controller_, GetLocalCredentials)
.WillOnce([&](const CredentialSelector& credential_selector,
GetLocalCredentialsResultCallback callback) {
std::vector<nearby::internal::LocalCredential> credentials;
credentials.push_back(CreateValidLocalCredential(key_pair_));
std::move(callback.credentials_fetched_cb)(credentials);
});
PresenceDevice remote_device(CreateTestMetadata());
remote_device.SetDecryptSharedCredential(BuildSharedCredential(key_pair_));
MockAuthenticationTransport authentication_transport;
EXPECT_CALL(authentication_transport, WriteMessage)
.WillOnce([&](absl::string_view message) {
PresenceAuthenticationFrame authentication_frame;
EXPECT_TRUE(authentication_frame.ParseFromString(message));
EXPECT_EQ(kPresenceVersion, authentication_frame.version());
});
auto status = provider_->AuthenticateAsInitiator(
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
/*authentication_transport=*/authentication_transport);
// TODO(b/282027237): Once additional logic is added in follow up CL's