mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 07:06:11 -04:00
297 lines
13 KiB
C++
297 lines
13 KiB
C++
// 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 <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/str_cat.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/time/time.h"
|
|
#include "internal/crypto/ed25519.h"
|
|
#include "internal/interop/authentication_status.h"
|
|
#include "internal/interop/authentication_transport.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 "presence/implementation/connection_authenticator.h"
|
|
#include "presence/implementation/mock_connection_authenticator.h"
|
|
#include "presence/implementation/mock_service_controller.h"
|
|
#include "presence/presence_device.h"
|
|
#include "presence/proto/presence_frame.pb.h"
|
|
|
|
namespace nearby {
|
|
namespace presence {
|
|
namespace {
|
|
using ::nearby::internal::DeviceIdentityMetaData;
|
|
|
|
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;
|
|
constexpr absl::string_view kSharedCredentialHash = "shared_cred_hash";
|
|
constexpr absl::string_view kPrivateKeySignature = "private_key_signature";
|
|
|
|
DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() {
|
|
DeviceIdentityMetaData device_identity_metadata;
|
|
device_identity_metadata.set_device_type(
|
|
internal::DeviceType::DEVICE_TYPE_PHONE);
|
|
device_identity_metadata.set_device_name("NP test device");
|
|
device_identity_metadata.set_bluetooth_mac_address(kMacAddr);
|
|
device_identity_metadata.set_device_id("\x12\xab\xcd");
|
|
return device_identity_metadata;
|
|
}
|
|
|
|
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 CreateExpiredLocalCredential() {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ConnectionAuthenticator::TwoWayInitiatorData BuildDefaultInitiatorData() {
|
|
ConnectionAuthenticator::TwoWayInitiatorData data;
|
|
data.shared_credential_hash = kSharedCredentialHash;
|
|
data.private_key_signature = kPrivateKeySignature;
|
|
return data;
|
|
}
|
|
|
|
class MockAuthenticationTransport : public AuthenticationTransport {
|
|
public:
|
|
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_, GetDeviceIdentityMetaData)
|
|
.WillByDefault(testing::Return(CreateTestDeviceIdentityMetaData()));
|
|
provider_ = std::make_unique<PresenceDeviceProvider>(
|
|
&mock_service_controller_, &mock_connection_authenticator_);
|
|
}
|
|
|
|
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_;
|
|
MockConnectionAuthenticator mock_connection_authenticator_;
|
|
};
|
|
|
|
TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) {
|
|
EXPECT_FALSE(std::is_trivially_constructible<PresenceDeviceProvider>::value);
|
|
}
|
|
|
|
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->GetDeviceIdentityMetadata().SerializeAsString(),
|
|
CreateTestDeviceIdentityMetaData().SerializeAsString());
|
|
}
|
|
|
|
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->GetDeviceIdentityMetadata().SerializeAsString(),
|
|
CreateTestDeviceIdentityMetaData().SerializeAsString());
|
|
auto new_metadata = CreateTestDeviceIdentityMetaData();
|
|
new_metadata.set_device_name("NP interop device");
|
|
provider_->UpdateDeviceIdentityMetaData(new_metadata);
|
|
EXPECT_EQ(presence_device->GetDeviceIdentityMetadata().SerializeAsString(),
|
|
new_metadata.SerializeAsString());
|
|
}
|
|
|
|
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=*/std::string()));
|
|
});
|
|
|
|
PresenceDevice remote_device(CreateTestDeviceIdentityMetaData());
|
|
MockAuthenticationTransport authentication_transport;
|
|
auto status = provider_->AuthenticateAsInitiator(
|
|
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
|
|
/*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(CreateExpiredLocalCredential());
|
|
std::move(callback.credentials_fetched_cb)(credentials);
|
|
});
|
|
|
|
PresenceDevice remote_device(CreateTestDeviceIdentityMetaData());
|
|
MockAuthenticationTransport authentication_transport;
|
|
auto status = provider_->AuthenticateAsInitiator(
|
|
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
|
|
/*authentication_transport=*/authentication_transport);
|
|
EXPECT_EQ(AuthenticationStatus::kFailure, status);
|
|
}
|
|
|
|
TEST_F(PresenceDeviceProviderTest,
|
|
AuthenticateAsInitiator_NoRemoteSharedCredential) {
|
|
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(CreateTestDeviceIdentityMetaData());
|
|
MockAuthenticationTransport authentication_transport;
|
|
auto status = provider_->AuthenticateAsInitiator(
|
|
/*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret,
|
|
/*authentication_transport=*/authentication_transport);
|
|
|
|
EXPECT_EQ(AuthenticationStatus::kFailure, status);
|
|
}
|
|
|
|
TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiator_FailureToVerify) {
|
|
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);
|
|
});
|
|
EXPECT_CALL(mock_service_controller_, GetLocalPublicCredentials)
|
|
.WillOnce([&](const CredentialSelector& credential_selector,
|
|
GetPublicCredentialsResultCallback callback) {
|
|
std::vector<nearby::internal::SharedCredential> credentials;
|
|
credentials.push_back(BuildSharedCredential(key_pair_));
|
|
std::move(callback.credentials_fetched_cb)(credentials);
|
|
});
|
|
|
|
PresenceDevice remote_device(CreateTestDeviceIdentityMetaData());
|
|
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());
|
|
});
|
|
EXPECT_CALL(authentication_transport, ReadMessage).WillOnce([&]() {
|
|
PresenceAuthenticationFrame authentication_frame;
|
|
return authentication_frame.SerializeAsString();
|
|
});
|
|
|
|
EXPECT_CALL(mock_connection_authenticator_, BuildSignedMessageAsInitiator)
|
|
.WillOnce(testing::Return(BuildDefaultInitiatorData()));
|
|
EXPECT_CALL(mock_connection_authenticator_, VerifyMessageAsInitiator)
|
|
.WillOnce(testing::Return(
|
|
absl::Status(absl::StatusCode::kCancelled, /*msg=*/std::string())));
|
|
|
|
auto status = provider_->AuthenticateAsInitiator(
|
|
/*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);
|
|
});
|
|
EXPECT_CALL(mock_service_controller_, GetLocalPublicCredentials)
|
|
.WillOnce([&](const CredentialSelector& credential_selector,
|
|
GetPublicCredentialsResultCallback callback) {
|
|
std::vector<nearby::internal::SharedCredential> credentials;
|
|
credentials.push_back(BuildSharedCredential(key_pair_));
|
|
std::move(callback.credentials_fetched_cb)(std::move(credentials));
|
|
});
|
|
|
|
PresenceDevice remote_device(CreateTestDeviceIdentityMetaData());
|
|
remote_device.SetDecryptSharedCredential(BuildSharedCredential(key_pair_));
|
|
|
|
ON_CALL(mock_connection_authenticator_, BuildSignedMessageAsInitiator)
|
|
.WillByDefault(testing::Return(BuildDefaultInitiatorData()));
|
|
|
|
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);
|
|
|
|
EXPECT_EQ(AuthenticationStatus::kSuccess, status);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace presence
|
|
} // namespace nearby
|