mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
[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:
committed by
Copybara-Service
parent
6b2f7fa94e
commit
58452f4049
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user