blob: 4ba6b9fdb2da8bbe46d58fc486435f44d2b53012 [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Unit tests for AuthSession.
#include "cryptohome/auth_session.h"
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <base/callback_helpers.h>
#include <base/run_loop.h>
#include <base/task/sequenced_task_runner.h>
#include <base/test/bind.h>
#include <base/test/task_environment.h>
#include <base/test/test_future.h>
#include <base/threading/sequenced_task_runner_handle.h>
#include <base/timer/mock_timer.h>
#include <brillo/cryptohome.h>
#include <cryptohome/proto_bindings/auth_factor.pb.h>
#include <cryptohome/proto_bindings/UserDataAuth.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/frontend/cryptohome/mock_frontend.h>
#include <libhwsec/frontend/pinweaver/mock_frontend.h>
#include <libhwsec-foundation/crypto/aes.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "cryptohome/auth_blocks/auth_block_utility_impl.h"
#include "cryptohome/auth_blocks/mock_auth_block_utility.h"
#include "cryptohome/auth_factor/auth_factor.h"
#include "cryptohome/auth_factor/auth_factor_manager.h"
#include "cryptohome/auth_factor/auth_factor_metadata.h"
#include "cryptohome/auth_factor/auth_factor_type.h"
#include "cryptohome/credential_verifier_test_utils.h"
#include "cryptohome/crypto_error.h"
#include "cryptohome/cryptohome_common.h"
#include "cryptohome/error/converter.h"
#include "cryptohome/flatbuffer_schemas/auth_block_state.h"
#include "cryptohome/key_objects.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_keyset_management.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/user_secret_stash.h"
#include "cryptohome/user_secret_stash_storage.h"
#include "cryptohome/user_session/mock_user_session.h"
#include "cryptohome/user_session/user_session_map.h"
namespace cryptohome {
namespace {
using base::test::TestFuture;
using brillo::cryptohome::home::SanitizeUserName;
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::CryptohomeError;
using cryptohome::error::CryptohomeMountError;
using hwsec_foundation::error::testing::IsOk;
using hwsec_foundation::error::testing::NotOk;
using hwsec_foundation::error::testing::ReturnError;
using hwsec_foundation::error::testing::ReturnValue;
using hwsec_foundation::status::MakeStatus;
using hwsec_foundation::status::OkStatus;
using hwsec_foundation::status::StatusChain;
using ::testing::_;
using ::testing::ByMove;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::Matcher;
using ::testing::NiceMock;
using ::testing::Pair;
using ::testing::Return;
using ::testing::UnorderedElementsAre;
using ::testing::VariantWith;
// Fake labels to be in used in this test suite.
constexpr char kFakeLabel[] = "test_label";
constexpr char kFakeOtherLabel[] = "test_other_label";
constexpr char kFakePinLabel[] = "test_pin_label";
// Fake passwords to be in used in this test suite.
constexpr char kFakePass[] = "test_pass";
constexpr char kFakePin[] = "123456";
constexpr char kFakeOtherPass[] = "test_other_pass";
// Fake username to be used in this test suite.
constexpr char kFakeUsername[] = "test_username";
// Returns a blob "derived" from provided blob to generate fake vkk_key from
// user secret in tests.
brillo::SecureBlob GetFakeDerivedSecret(const brillo::SecureBlob& blob) {
return brillo::SecureBlob::Combine(blob,
brillo::SecureBlob(" derived secret"));
}
// A matcher that checks if an auth block state has a particular type.
template <typename StateType>
Matcher<const AuthBlockState&> AuthBlockStateTypeIs() {
return Field(&AuthBlockState::state, VariantWith<StateType>(_));
}
SerializedVaultKeyset CreateFakePasswordVk(const std::string& label) {
SerializedVaultKeyset serialized_vk;
serialized_vk.set_flags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::PCR_BOUND |
SerializedVaultKeyset::ECC);
serialized_vk.set_password_rounds(1);
serialized_vk.set_tpm_key("tpm-key");
serialized_vk.set_extended_tpm_key("tpm-extended-key");
serialized_vk.set_vkk_iv("iv");
serialized_vk.mutable_key_data()->set_type(KeyData::KEY_TYPE_PASSWORD);
serialized_vk.mutable_key_data()->set_label(label);
return serialized_vk;
}
} // namespace
class AuthSessionTest : public ::testing::Test {
public:
AuthSessionTest() = default;
AuthSessionTest(const AuthSessionTest&) = delete;
AuthSessionTest& operator=(const AuthSessionTest&) = delete;
~AuthSessionTest() override = default;
void SetUp() override {
EXPECT_CALL(hwsec_, IsEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsSealingSupported()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, GetManufacturer())
.WillRepeatedly(ReturnValue(0x43524f53));
EXPECT_CALL(hwsec_, GetAuthValue(_, _))
.WillRepeatedly(ReturnValue(brillo::SecureBlob()));
EXPECT_CALL(hwsec_, SealWithCurrentUser(_, _, _))
.WillRepeatedly(ReturnValue(brillo::Blob()));
EXPECT_CALL(hwsec_, GetPubkeyHash(_))
.WillRepeatedly(ReturnValue(brillo::Blob()));
EXPECT_CALL(pinweaver_, IsEnabled()).WillRepeatedly(ReturnValue(true));
crypto_.Init();
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<base::SequencedTaskRunner> task_runner_ =
base::SequencedTaskRunnerHandle::Get();
// Mock and fake objects, will be passed to AuthSession for its internal use.
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
NiceMock<MockPlatform> platform_;
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
Crypto crypto_{&hwsec_, &pinweaver_, &cryptohome_keys_manager_, nullptr};
NiceMock<MockKeysetManagement> keyset_management_;
NiceMock<MockAuthBlockUtility> auth_block_utility_;
AuthFactorManager auth_factor_manager_{&platform_};
UserSecretStashStorage user_secret_stash_storage_{&platform_};
UserSessionMap user_session_map_;
};
const CryptohomeError::ErrorLocationPair kErrorLocationForTestingAuthSession =
CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1),
std::string("MockErrorLocationAuthSession"));
TEST_F(AuthSessionTest, InitiallyNotAuthenticated) {
AuthSession auth_session(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
}
TEST_F(AuthSessionTest, InitiallyNotAuthenticatedForExistingUser) {
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
}
TEST_F(AuthSessionTest, Username) {
AuthSession auth_session(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(auth_session.username(), kFakeUsername);
EXPECT_EQ(auth_session.obfuscated_username(),
SanitizeUserName(kFakeUsername));
}
TEST_F(AuthSessionTest, Intent) {
AuthSession decryption_auth_session(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
AuthSession verification_auth_session(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(decryption_auth_session.auth_intent(), AuthIntent::kDecrypt);
EXPECT_EQ(verification_auth_session.auth_intent(), AuthIntent::kVerifyOnly);
}
TEST_F(AuthSessionTest, TimeoutTest) {
bool called = false;
auto on_timeout = base::BindOnce(
[](bool* called, const base::UnguessableToken&) { *called = true; },
base::Unretained(&called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
auth_session.SetAuthSessionAsAuthenticated(kAllAuthIntents);
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
auth_session.timeout_timer_.FireNow();
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusTimedOut);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_TRUE(called);
}
TEST_F(AuthSessionTest, SerializedStringFromNullToken) {
base::UnguessableToken token = base::UnguessableToken::Null();
std::optional<std::string> serialized_token =
AuthSession::GetSerializedStringFromToken(token);
EXPECT_FALSE(serialized_token.has_value());
}
TEST_F(AuthSessionTest, TokenFromEmptyString) {
std::string serialized_string = "";
std::optional<base::UnguessableToken> unguessable_token =
AuthSession::GetTokenFromSerializedString(serialized_string);
EXPECT_FALSE(unguessable_token.has_value());
}
TEST_F(AuthSessionTest, TokenFromUnexpectedSize) {
std::string serialized_string = "unexpected_sized_string";
std::optional<base::UnguessableToken> unguessable_token =
AuthSession::GetTokenFromSerializedString(serialized_string);
EXPECT_FALSE(unguessable_token.has_value());
}
TEST_F(AuthSessionTest, TokenFromString) {
base::UnguessableToken original_token = base::UnguessableToken::Create();
std::optional<std::string> serialized_token =
AuthSession::GetSerializedStringFromToken(original_token);
EXPECT_TRUE(serialized_token.has_value());
std::optional<base::UnguessableToken> deserialized_token =
AuthSession::GetTokenFromSerializedString(serialized_token.value());
EXPECT_TRUE(deserialized_token.has_value());
EXPECT_EQ(deserialized_token.value(), original_token);
}
// This test check AuthSession::GetCredential function for a regular user and
// ensures that the fields are set as they should be.
TEST_F(AuthSessionTest, GetCredentialRegularUser) {
// SETUP
bool called = false;
auto on_timeout = base::BindOnce(
[](bool* called, const base::UnguessableToken&) { *called = true; },
base::Unretained(&called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
// TEST
EXPECT_FALSE(called);
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePass);
authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel);
MountStatusOr<std::unique_ptr<Credentials>> test_creds =
auth_session.GetCredentials(authorization_request);
ASSERT_TRUE(test_creds.ok());
// VERIFY
// SerializeToString is used in the absence of a comparator for KeyData
// protobuf.
std::string key_data_serialized1 = "1";
std::string key_data_serialized2 = "2";
test_creds.value()->key_data().SerializeToString(&key_data_serialized1);
authorization_request.mutable_key()->data().SerializeToString(
&key_data_serialized2);
EXPECT_EQ(key_data_serialized1, key_data_serialized2);
}
// This test check AuthSession::GetCredential function for a kiosk user and
// ensures that the fields are set as they should be.
TEST_F(AuthSessionTest, GetCredentialKioskUser) {
// SETUP
bool called = false;
auto on_timeout = base::BindOnce(
[](bool* called, const base::UnguessableToken&) { *called = true; },
base::Unretained(&called));
// SecureBlob for kFakePass above
const brillo::SecureBlob fake_pass_blob(
brillo::BlobFromString(kFakeUsername));
AuthSession auth_session(
kFakeUsername, 0, AuthIntent::kDecrypt, std::move(on_timeout), &crypto_,
&platform_, &user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_CALL(keyset_management_, GetPublicMountPassKey(_))
.WillOnce(Return(ByMove(fake_pass_blob)));
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
// TEST
EXPECT_FALSE(called);
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel);
authorization_request.mutable_key()->mutable_data()->set_type(
KeyData::KEY_TYPE_KIOSK);
MountStatusOr<std::unique_ptr<Credentials>> test_creds =
auth_session.GetCredentials(authorization_request);
ASSERT_TRUE(test_creds.ok());
// VERIFY
// SerializeToString is used in the absence of a comparator for KeyData
// protobuf.
std::string key_data_serialized1 = "1";
std::string key_data_serialized2 = "2";
test_creds.value()->key_data().SerializeToString(&key_data_serialized1);
authorization_request.mutable_key()->data().SerializeToString(
&key_data_serialized2);
EXPECT_EQ(key_data_serialized1, key_data_serialized2);
EXPECT_EQ(test_creds.value()->passkey(), fake_pass_blob);
}
// Test if AuthSession correctly adds new credentials for a new user.
TEST_F(AuthSessionTest, AddCredentialNewUser) {
// Setup.
bool called = false;
auto on_timeout = base::BindOnce(
[](bool& called, const base::UnguessableToken&) { called = true; },
std::ref(called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
// For AuthSession::AddInitialKeyset/AddKeyset callback to properly
// execute auth_block_utility_ cannot be a mock
std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ =
std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_,
&platform_);
// Setting the expectation that the user does not exist.
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
std::move(on_timeout), &crypto_, &platform_,
&user_session_map_, &keyset_management_,
auth_block_utility_impl_.get(),
&auth_factor_manager_, &user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_FALSE(auth_session.user_exists());
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce([](const std::string&, const std::string&) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(CreateFakePasswordVk(kFakeLabel));
return vk;
});
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
user_data_auth::AddCredentialsRequest add_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
add_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddCredentials(add_cred_request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test if AuthSession correctly adds new credentials for a new user, even when
// called twice. The first credential gets added as an initial keyset, and the
// second as a regular one.
TEST_F(AuthSessionTest, AddCredentialNewUserTwice) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// For AuthSession::AddInitialKeyset/AddKeyset callback to properly
// execute auth_block_utility_ cannot be a mock
std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ =
std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_,
&platform_);
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
auth_block_utility_impl_.get(),
&auth_factor_manager_, &user_secret_stash_storage_);
// Test adding the first credential.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_FALSE(auth_session.user_exists());
user_data_auth::AddCredentialsRequest add_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
add_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillRepeatedly([](const std::string&, const std::string& label) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(CreateFakePasswordVk(label));
return vk;
});
EXPECT_TRUE(auth_session.OnUserCreated().ok());
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
TestFuture<CryptohomeStatus> add_future;
auth_session.AddCredentials(add_cred_request, add_future.GetCallback());
// Verify.
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Test adding the second credential.
// Set up expectation in callback for success.
user_data_auth::AddCredentialsRequest add_other_cred_request;
cryptohome::AuthorizationRequest* other_authorization_request =
add_other_cred_request.mutable_authorization();
other_authorization_request->mutable_key()->set_secret(kFakeOtherPass);
other_authorization_request->mutable_key()->mutable_data()->set_label(
kFakeOtherLabel);
EXPECT_CALL(keyset_management_, AddKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(CRYPTOHOME_ERROR_NOT_SET));
// Test.
TestFuture<CryptohomeStatus> add_other_future;
auth_session.AddCredentials(add_other_cred_request,
add_other_future.GetCallback());
// Verify.
ASSERT_THAT(add_other_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test if AuthSession correctly authenticates existing credentials for a
// user.
TEST_F(AuthSessionTest, AuthenticateExistingUser) {
// Setup.
bool called_timeout = false;
auto on_timeout = base::BindOnce(
[](bool& called_timeout, const base::UnguessableToken&) {
called_timeout = true;
},
std::ref(called_timeout));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePass);
authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel);
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
auto key_blobs = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.Authenticate(authorization_request,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_EQ(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session.TakeCredentialVerifier()->Verify(
brillo::SecureBlob(kFakePass)));
// Cleanup.
auth_session.timeout_timer_.FireNow();
EXPECT_THAT(AuthStatus::kAuthStatusTimedOut, auth_session.GetStatus());
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
}
// Test Authenticate() authenticates the existing user with PIN credentials.
TEST_F(AuthSessionTest, AuthenticateWithPIN) {
// Setup.
bool called_timeout = false;
auto on_timeout = base::BindOnce(
[](bool& called_timeout, const base::UnguessableToken&) {
called_timeout = true;
},
std::ref(called_timeout));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePin);
authorization_request.mutable_key()->mutable_data()->set_label(kFakePinLabel);
authorization_request.mutable_key()
->mutable_data()
->mutable_policy()
->set_low_entropy_credential(true);
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
auto key_blobs = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.Authenticate(authorization_request,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_EQ(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session.TakeCredentialVerifier()->Verify(
brillo::SecureBlob(kFakePin)));
// Cleanup.
auth_session.timeout_timer_.FireNow();
EXPECT_THAT(AuthStatus::kAuthStatusTimedOut, auth_session.GetStatus());
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
}
// Test whether PIN is locked out right after the last workable wrong attempt.
TEST_F(AuthSessionTest, AuthenticateFailsOnPINLock) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePin);
authorization_request.mutable_key()->mutable_data()->set_label(kFakePinLabel);
authorization_request.mutable_key()
->mutable_data()
->mutable_policy()
->set_low_entropy_credential(true);
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
auto vk = std::make_unique<VaultKeyset>();
vk->Initialize(&platform_, &crypto_);
vk->SetAuthLocked(false);
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakePinLabel))
.WillOnce(Return(ByMove(std::move(vk))));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
CryptoStatus status = MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet({error::ErrorAction::kAuth}),
CryptoError::CE_CREDENTIAL_LOCKED);
std::move(derive_callback)
.Run(std::move(status), std::make_unique<KeyBlobs>());
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.Authenticate(authorization_request,
authenticate_future.GetCallback());
// Verify.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED);
EXPECT_NE(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus());
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test whether PIN is locked out when TpmLockout action is received.
TEST_F(AuthSessionTest, AuthenticateFailsAfterPINLock) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePin);
authorization_request.mutable_key()->mutable_data()->set_label(kFakePinLabel);
authorization_request.mutable_key()
->mutable_data()
->mutable_policy()
->set_low_entropy_credential(true);
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
CryptoStatus status = MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet({error::ErrorAction::kTpmLockout}),
CryptoError::CE_TPM_DEFEND_LOCK);
std::move(derive_callback)
.Run(std::move(status), std::make_unique<KeyBlobs>());
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.Authenticate(authorization_request,
authenticate_future.GetCallback());
// Verify.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK);
EXPECT_NE(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus());
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// AuthSession fails authentication, test for failure reply code
// and ensure |credential_verifier_| is not set.
TEST_F(AuthSessionTest, AuthenticateExistingUserFailure) {
// Setup.
auto on_timeout = base::DoNothing();
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
std::string obfuscated_username = SanitizeUserName(kFakeUsername);
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_,
GetVaultKeysetLabelsAndData(obfuscated_username, _));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
cryptohome::AuthorizationRequest authorization_request;
authorization_request.mutable_key()->set_secret(kFakePass);
authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel);
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
// Failure is achieved by having the callback return an empty key_blobs
// and a CryptohomeCryptoError.
auto key_blobs = nullptr;
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet(
{error::ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_TPM_FATAL),
std::move(key_blobs));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.Authenticate(authorization_request,
authenticate_future.GetCallback());
// Verify, should not be authenticated and CredentialVerifier should not be
// set.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_VAULT_UNRECOVERABLE);
ASSERT_FALSE(auth_session.timeout_timer_.IsRunning());
EXPECT_EQ(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test if AuthSession::Addcredentials skips adding/saving credential to disk
// for an ephemeral user.
TEST_F(AuthSessionTest, AddCredentialNewEphemeralUser) {
// Setup.
bool called = false;
auto on_timeout = base::BindOnce(
[](bool& called, const base::UnguessableToken&) { called = true; },
std::ref(called));
int flags =
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
user_data_auth::AddCredentialsRequest add_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
add_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.Times(0);
// Test.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddCredentials(add_cred_request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
// Test if AuthSession correctly updates existing credentials for a new user.
TEST_F(AuthSessionTest, UpdateCredentialUnauthenticatedAuthSession) {
// Setup.
bool called = false;
auto on_timeout = base::BindOnce(
[](bool& called, const base::UnguessableToken&) { called = true; },
std::ref(called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
user_data_auth::UpdateCredentialRequest update_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
update_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
update_cred_request.set_old_credential_label(kFakeLabel);
// Test.
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateCredential(update_cred_request,
update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
EXPECT_EQ(update_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION);
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test if AuthSession correctly updates existing credentials for a new user.
TEST_F(AuthSessionTest, UpdateCredentialSuccess) {
// Setup.
bool called = false;
auto on_timeout = base::BindOnce(
[](bool& called, const base::UnguessableToken&) { called = true; },
std::ref(called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// For AuthSession::UpdateKeyset callback to properly
// execute auth_block_utility_ cannot be a mock
std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ =
std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_,
&platform_);
// Setting the expectation that the user does exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
std::move(on_timeout), &crypto_, &platform_,
&user_session_map_, &keyset_management_,
auth_block_utility_impl_.get(),
&auth_factor_manager_, &user_secret_stash_storage_);
auth_session.SetStatus(AuthStatus::kAuthStatusAuthenticated);
user_data_auth::UpdateCredentialRequest update_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
update_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
update_cred_request.set_old_credential_label(kFakeLabel);
// Test.
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateCredential(update_cred_request,
update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), IsOk());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test if UpdateAuthSession fails for not matching label.
TEST_F(AuthSessionTest, UpdateCredentialInvalidLabel) {
// Setup.
bool called = false;
auto on_timeout = base::BindOnce(
[](bool& called, const base::UnguessableToken&) { called = true; },
std::ref(called));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt, std::move(on_timeout),
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
user_data_auth::UpdateCredentialRequest update_cred_request;
cryptohome::AuthorizationRequest* authorization_request =
update_cred_request.mutable_authorization();
authorization_request->mutable_key()->set_secret(kFakePass);
authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel);
update_cred_request.set_old_credential_label("wrong-label");
// Test.
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateCredential(update_cred_request,
update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
EXPECT_EQ(update_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test that the UserSecretStash isn't created by default when a new user is
// created.
TEST_F(AuthSessionTest, NoUssByDefault) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
EXPECT_TRUE(auth_session.OnUserCreated().ok());
// Verify.
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK.
TEST_F(AuthSessionTest, AuthenticateAuthFactorExistingVKUserNoResave) {
// Setup AuthSession.
AuthBlockState auth_block_state;
auth_block_state.state = TpmBoundToPcrAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// Called within the converter_.PopulateKeyDataForVK()
KeyData key_data;
key_data.set_label(kFakeLabel);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce(Return(ByMove(std::move(vk))));
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
EXPECT_CALL(keyset_management_, AddResetSeedIfMissing(_))
.WillOnce(Return(false));
auto key_blobs = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK and resaves it.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorExistingVKUserAndResaveForUpdate) {
// Setup AuthSession.
AuthBlockState auth_block_state;
auth_block_state.state = LibScryptCompatAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// Called within the converter_.PopulateKeyDataForVK()
KeyData key_data;
key_data.set_label(kFakeLabel);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce(Return(ByMove(std::move(vk))));
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kLibScryptCompat));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_)).WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _, _))
.WillOnce(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(keyset_management_, ReSaveKeysetWithKeyBlobs(_, _, _));
auto key_blobs = std::make_unique<KeyBlobs>();
auto auth_block_state2 = std::make_unique<AuthBlockState>();
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs, &auth_block_state2](
AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state2));
return true;
});
auto key_blobs2 = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs2](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs2));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK and resaves it.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorExistingVKUserAndResaveForResetSeed) {
// Setup AuthSession.
AuthBlockState auth_block_state;
auth_block_state.state = LibScryptCompatAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// Called within the converter_.PopulateKeyDataForVK()
KeyData key_data;
key_data.set_label(kFakeLabel);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce(Return(ByMove(std::move(vk))));
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kLibScryptCompat));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
EXPECT_CALL(keyset_management_, AddResetSeedIfMissing(_))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _, _))
.WillOnce(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(keyset_management_, ReSaveKeysetWithKeyBlobs(_, _, _));
auto key_blobs = std::make_unique<KeyBlobs>();
auto auth_block_state2 = std::make_unique<AuthBlockState>();
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs, &auth_block_state2](
AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state2));
return true;
});
auto key_blobs2 = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs2](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs2));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test that AuthenticateAuthFactor doesn't add reset seed to LECredentials.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorNotAddingResetSeedToPINVaultKeyset) {
// Setup AuthSession.
AuthBlockState auth_block_state;
auth_block_state.state = PinWeaverAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakePinLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPin, kFakePinLabel,
AuthFactorMetadata(), auth_block_state));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakePinLabel);
request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin);
// Called within the converter_.PopulateKeyDataForVK()
KeyData key_data;
key_data.set_label(kFakePinLabel);
key_data.mutable_policy()->set_low_entropy_credential(true);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakePinLabel))
.WillOnce(Return(ByMove(std::move(vk))));
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
EXPECT_CALL(keyset_management_, AddResetSeedIfMissing(_))
.WillOnce(Return(false));
auto key_blobs2 = std::make_unique<KeyBlobs>();
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs2](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs2));
return true;
});
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor returns an error when supplied label and
// type mismatch.
TEST_F(AuthSessionTest, AuthenticateAuthFactorMismatchLabelAndType) {
// Setup AuthSession.
AuthBlockState auth_block_state;
auth_block_state.state = PinWeaverAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakePinLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPin, kFakePinLabel,
AuthFactorMetadata(), auth_block_state));
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakePinLabel);
// Note: Intentially creating a missmatch in type and label.
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_FALSE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
}
// Test if AddAuthFactor correctly adds initial VaultKeyset password AuthFactor
// for a new user.
TEST_F(AuthSessionTest, AddAuthFactorNewUser) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl =
std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_,
&platform_);
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
auth_block_utility_impl.get(), &auth_factor_manager_,
&user_secret_stash_storage_);
// Setting the expectation that the user does not exist.
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_FALSE(auth_session.user_exists());
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session.user_exists());
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce([](const std::string&, const std::string&) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(CreateFakePasswordVk(kFakeLabel));
return vk;
});
// Test.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test that AddAuthFactor can add multiple VaultKeyset-AuthFactor. The first
// one is added as initial factor, the second is added as the second password
// factor, and the third one as added as a PIN factor.
TEST_F(AuthSessionTest, AddMultipleAuthFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Setting the expectation that the user does not exist.
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_FALSE(auth_session.user_exists());
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session.user_exists());
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// GetauthBlockTypeForCreation() and CreateKeyBlobsWithAuthBlockAsync() are
// called for each of the key addition operations below.
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _, _))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillRepeatedly([](AuthBlockType auth_block_type,
const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(),
std::make_unique<KeyBlobs>(),
std::make_unique<AuthBlockState>());
return true;
});
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillRepeatedly([](const std::string&, const std::string& label) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(CreateFakePasswordVk(label));
return vk;
});
// Test.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
// Test adding new password AuthFactor
user_data_auth::AddAuthFactorRequest request2;
request2.set_auth_session_id(auth_session.serialized_token());
request2.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request2.mutable_auth_factor()->set_label(kFakeOtherLabel);
request2.mutable_auth_factor()->mutable_password_metadata();
request2.mutable_auth_input()->mutable_password_input()->set_secret(
kFakeOtherPass);
EXPECT_CALL(keyset_management_, AddKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(CRYPTOHOME_ERROR_NOT_SET));
// Test.
TestFuture<CryptohomeStatus> add_future2;
auth_session.AddAuthFactor(request2, add_future2.GetCallback());
// Verify.
ASSERT_THAT(add_future2.Get(), IsOk());
// The credential verifier should still use the original password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
// TODO(b:223222440) Add test to for adding a PIN after reset secret
// generation function is updated.
}
// Test that AddAuthFactor succeeds for an ephemeral user and creates a
// credential verifier.
TEST_F(AuthSessionTest, AddPasswordFactorToEphemeral) {
// Setup.
AuthSession auth_session(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Test.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
user_data_auth::AuthFactor& request_factor = *request.mutable_auth_factor();
request_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request_factor.set_label(kFakeLabel);
request_factor.mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test that AddAuthFactor fails for an ephemeral user when PIN is added.
TEST_F(AuthSessionTest, AddPinFactorToEphemeralFails) {
// Setup.
AuthSession auth_session(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Test.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
user_data_auth::AuthFactor& request_factor = *request.mutable_auth_factor();
request_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
request_factor.set_label(kFakePinLabel);
request_factor.mutable_pin_metadata();
request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
ASSERT_THAT(add_future.Get(), NotOk());
EXPECT_EQ(add_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE);
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test that AddAuthFactor fails for an ephemeral user when a second password is
// added.
TEST_F(AuthSessionTest, AddSecondPasswordFactorToEphemeralFails) {
// Setup.
AuthSession auth_session(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Add the first password.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
user_data_auth::AuthFactor& request_factor = *request.mutable_auth_factor();
request_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request_factor.set_label(kFakeLabel);
request_factor.mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> first_add_future;
auth_session.AddAuthFactor(request, first_add_future.GetCallback());
EXPECT_THAT(first_add_future.Get(), IsOk());
// Test.
request_factor.set_label(kFakeOtherLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(
kFakeOtherPass);
TestFuture<CryptohomeStatus> second_add_future;
auth_session.AddAuthFactor(request, second_add_future.GetCallback());
// Verify.
ASSERT_THAT(second_add_future.Get(), NotOk());
EXPECT_EQ(second_add_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE);
// The verifier still uses the first password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// UpdateAuthFactor request success when updating authenticated password VK.
TEST_F(AuthSessionTest, UpdateAuthFactorSucceedsForPasswordVK) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
AuthBlockState auth_block_state;
auth_block_state.state = TpmBoundToPcrAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_TRUE(auth_session.user_exists());
// GetAuthBlockTypeForCreation() and CreateKeyBlobsWithAuthBlockAsync() are
// called for the key update operations below.
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _, _))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillRepeatedly([&](AuthBlockType auth_block_type,
const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(),
std::make_unique<KeyBlobs>(),
std::make_unique<AuthBlockState>(auth_block_state));
return true;
});
EXPECT_CALL(keyset_management_, UpdateKeysetWithKeyBlobs(_, _, _, _, _))
.WillOnce(Return(CRYPTOHOME_ERROR_NOT_SET));
// Set a valid |vault_keyset_| to update.
auto vk = std::make_unique<VaultKeyset>();
vk->Initialize(&platform_, &crypto_);
vk->CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
vk->SetAuthBlockState(auth_block_state);
auth_session.set_vault_keyset_for_testing(std::move(vk));
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), IsOk());
EXPECT_TRUE(auth_session.TakeCredentialVerifier()->Verify(
brillo::SecureBlob(kFakePass)));
}
// UpdateAuthFactor fails if label doesn't exist.
TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotMatchForVK) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
AuthBlockState auth_block_state;
auth_block_state.state = TpmBoundToPcrAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_TRUE(auth_session.user_exists());
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeOtherLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(
kFakeOtherPass);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
// Verify that the credential_verifier is not updated on failure.
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// UpdateAuthFactor fails if label doesn't exist in the existing keysets.
TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotFoundForVK) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.GetStatus());
EXPECT_TRUE(auth_session.user_exists());
AuthBlockState auth_block_state;
auth_block_state.state = TpmBoundToPcrAuthBlockState();
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state));
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_TRUE(auth_session.user_exists());
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeOtherLabel);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeOtherLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(
kFakeOtherPass);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
// Verify that the credential_verifier is not updated on failure.
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// A variant of the auth session test that has the UserSecretStash experiment
// enabled.
class AuthSessionWithUssExperimentTest : public AuthSessionTest {
protected:
AuthSessionWithUssExperimentTest() {
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
}
~AuthSessionWithUssExperimentTest() {
// Reset this global variable to avoid affecting unrelated test cases.
SetUserSecretStashExperimentForTesting(/*enabled=*/std::nullopt);
}
user_data_auth::CryptohomeErrorCode AddPasswordAuthFactor(
const std::string& password, AuthSession& auth_session) {
EXPECT_CALL(
auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false,
AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key =
GetFakeDerivedSecret(auth_input.user_input.value());
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = TpmBoundToPcrAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
user_data_auth::AddAuthFactorRequest request;
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(
password);
request.set_auth_session_id(auth_session.serialized_token());
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
if (add_future.Get().ok()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return add_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode AuthenticatePasswordAuthFactor(
const std::string& password, AuthSession& auth_session) {
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(AuthBlockType::kTpmBoundToPcr,
_, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key =
GetFakeDerivedSecret(auth_input.user_input.value());
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(
password);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(request,
authenticate_future.GetCallback());
// Verify.
if (authenticate_future.Get().ok() ||
!authenticate_future.Get()->local_legacy_error().has_value()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return authenticate_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode UpdatePasswordAuthFactor(
const std::string& new_password, AuthSession& auth_session) {
EXPECT_CALL(
auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false,
AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key =
GetFakeDerivedSecret(auth_input.user_input.value());
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = TpmBoundToPcrAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(
new_password);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
if (update_future.Get().ok()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return update_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode AddPinAuthFactor(
const std::string& pin, AuthSession& auth_session) {
EXPECT_CALL(
auth_block_utility_,
GetAuthBlockTypeForCreation(true, false, false,
AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kPinWeaver, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key =
GetFakeDerivedSecret(auth_input.user_input.value());
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = PinWeaverAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest add_pin_request;
add_pin_request.set_auth_session_id(auth_session.serialized_token());
add_pin_request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PIN);
add_pin_request.mutable_auth_factor()->set_label(kFakePinLabel);
add_pin_request.mutable_auth_factor()->mutable_pin_metadata();
add_pin_request.mutable_auth_input()->mutable_pin_input()->set_secret(pin);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(add_pin_request, add_future.GetCallback());
if (add_future.Get().ok()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return add_future.Get()->local_legacy_error().value();
}
};
// Test that the UserSecretStash is created on the user creation, in case the
// UserSecretStash experiment is on.
TEST_F(AuthSessionWithUssExperimentTest, UssCreation) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
EXPECT_TRUE(auth_session.OnUserCreated().ok());
// Verify.
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test that no UserSecretStash is created for an ephemeral user.
TEST_F(AuthSessionWithUssExperimentTest, NoUssForEphemeral) {
// Setup.
int flags =
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
// Verify.
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
}
// Test that a new auth factor can be added to the newly created user, in case
// the UserSecretStash experiment is on.
TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAuthFactorViaUss) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// Test.
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
false, false, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob("fake vkk key");
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = TpmBoundToPcrAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify
EXPECT_THAT(add_future.Get(), IsOk());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword)));
EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakeLabel),
auth_session.label_to_auth_factor_.end());
}
// Test that a new auth factor can be added to the newly created user using
// asynchronous key creation.
TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAuthFactorViaAsyncUss) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// Test.
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
false, false, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([this](AuthBlockType, const AuthInput&,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state, but schedule it to run later to
// simulate an proper async key creation.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob("fake vkk key");
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = TpmBoundToPcrAuthBlockState();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(create_callback),
OkStatus<CryptohomeCryptoError>(),
std::move(key_blobs), std::move(auth_block_state)));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword)));
EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakeLabel),
auth_session.label_to_auth_factor_.end());
}
// Test the new auth factor failure path when asynchronous key creation fails.
TEST_F(AuthSessionWithUssExperimentTest,
AddPasswordAuthFactorViaAsyncUssFails) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// Test.
// Setting the expectation that the auth block utility will be called an that
// key blob creation will fail.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
false, false, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([this](AuthBlockType, const AuthInput&,
AuthBlock::CreateCallback create_callback) {
// Have the creation callback report an error.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
std::move(create_callback),
MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet(
{error::ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
ASSERT_THAT(add_future.Get(), NotOk());
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
ASSERT_EQ(add_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED);
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors, IsEmpty());
}
// Test that a new auth factor cannot be added for an unauthenticated
// authsession.
TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAuthFactorUnAuthenticated) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// Test and Verify.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
ASSERT_THAT(add_future.Get(), NotOk());
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
ASSERT_EQ(add_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION);
}
// Test that a new auth factor and a pin can be added to the newly created user,
// in case the UserSecretStash experiment is on.
TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAndPinAuthFactorViaUss) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// Add a password first.
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
false, false, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob("fake vkk key");
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = TpmBoundToPcrAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
// Test and Verify.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
true, false, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(AuthBlockType::kPinWeaver, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob("fake vkk key");
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = PinWeaverAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest add_pin_request;
add_pin_request.set_auth_session_id(auth_session.serialized_token());
add_pin_request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PIN);
add_pin_request.mutable_auth_factor()->set_label(kFakePinLabel);
add_pin_request.mutable_auth_factor()->mutable_pin_metadata();
add_pin_request.mutable_auth_input()->mutable_pin_input()->set_secret(
kFakePin);
// Test and Verify.
TestFuture<CryptohomeStatus> add_pin_future;
auth_session.AddAuthFactor(add_pin_request, add_pin_future.GetCallback());
// Verify.
ASSERT_THAT(add_pin_future.Get(), IsOk());
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword),
Pair(kFakePinLabel, AuthFactorType::kPin)));
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
// Ensure that a reset secret for the PIN was added.
const auto reset_secret =
auth_session.user_secret_stash_for_testing()->GetResetSecretForLabel(
kFakePinLabel);
EXPECT_TRUE(reset_secret.has_value());
EXPECT_EQ(CRYPTOHOME_RESET_SECRET_LENGTH, reset_secret->size());
}
// Test that an existing user with an existing password auth factor can be
// authenticated, in case the UserSecretStash experiment is on.
TEST_F(AuthSessionWithUssExperimentTest, AuthenticatePasswordAuthFactorViaUss) {
// Setup.
const std::string obfuscated_username = SanitizeUserName(kFakeUsername);
const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk");
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the USS.
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status =
UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom());
ASSERT_TRUE(uss_status.ok());
std::unique_ptr<UserSecretStash> uss = std::move(uss_status).value();
std::optional<brillo::SecureBlob> uss_main_key =
UserSecretStash::CreateRandomMainKey();
ASSERT_TRUE(uss_main_key.has_value());
// Creating the auth factor. An arbitrary auth block state is used in this
// test.
AuthFactor auth_factor(
AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()},
AuthBlockState{.state = TpmBoundToPcrAuthBlockState()});
EXPECT_TRUE(
auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor)
.ok());
// Adding the auth factor into the USS and persisting the latter.
const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret};
std::optional<brillo::SecureBlob> wrapping_key =
key_blobs.DeriveUssCredentialSecret();
ASSERT_TRUE(wrapping_key.has_value());
EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakeLabel,
wrapping_key.value())
.ok());
CryptohomeStatusOr<brillo::Blob> encrypted_uss =
uss->GetEncryptedContainer(uss_main_key.value());
ASSERT_TRUE(encrypted_uss.ok());
EXPECT_TRUE(user_secret_stash_storage_
.Persist(encrypted_uss.value(), obfuscated_username)
.ok());
// Creating the auth session.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_TRUE(auth_session.user_exists());
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _, _))
.WillOnce([&kFakePerCredentialSecret](
AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = kFakePerCredentialSecret;
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test that an existing user with an existing password auth factor can be
// authenticated, using asynchronous key derivation.
TEST_F(AuthSessionWithUssExperimentTest,
AuthenticatePasswordAuthFactorViaAsyncUss) {
// Setup.
const std::string obfuscated_username = SanitizeUserName(kFakeUsername);
const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk");
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the USS.
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status =
UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom());
ASSERT_TRUE(uss_status.ok());
std::unique_ptr<UserSecretStash> uss = std::move(uss_status).value();
std::optional<brillo::SecureBlob> uss_main_key =
UserSecretStash::CreateRandomMainKey();
ASSERT_TRUE(uss_main_key.has_value());
// Creating the auth factor. An arbitrary auth block state is used in this
// test.
AuthFactor auth_factor(
AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()},
AuthBlockState{.state = TpmBoundToPcrAuthBlockState()});
EXPECT_TRUE(
auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor)
.ok());
// Adding the auth factor into the USS and persisting the latter.
const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret};
std::optional<brillo::SecureBlob> wrapping_key =
key_blobs.DeriveUssCredentialSecret();
ASSERT_TRUE(wrapping_key.has_value());
EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakeLabel,
wrapping_key.value())
.ok());
CryptohomeStatusOr<brillo::Blob> encrypted_uss =
uss->GetEncryptedContainer(uss_main_key.value());
ASSERT_TRUE(encrypted_uss.ok());
EXPECT_TRUE(user_secret_stash_storage_
.Persist(encrypted_uss.value(), obfuscated_username)
.ok());
// Creating the auth session.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_TRUE(auth_session.user_exists());
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _, _))
.WillOnce([this, &kFakePerCredentialSecret](
AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = kFakePerCredentialSecret;
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(derive_callback),
OkStatus<CryptohomeCryptoError>(),
std::move(key_blobs)));
return true;
});
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// Test then failure path with an existing user with an existing password auth
// factor when the asynchronous derivation fails.
TEST_F(AuthSessionWithUssExperimentTest,
AuthenticatePasswordAuthFactorViaAsyncUssFails) {
// Setup.
const std::string obfuscated_username = SanitizeUserName(kFakeUsername);
const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk");
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the USS.
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status =
UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom());
ASSERT_TRUE(uss_status.ok());
std::unique_ptr<UserSecretStash> uss = std::move(uss_status).value();
std::optional<brillo::SecureBlob> uss_main_key =
UserSecretStash::CreateRandomMainKey();
ASSERT_TRUE(uss_main_key.has_value());
// Creating the auth factor. An arbitrary auth block state is used in this
// test.
AuthFactor auth_factor(
AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()},
AuthBlockState{.state = TpmBoundToPcrAuthBlockState()});
EXPECT_TRUE(
auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor)
.ok());
// Adding the auth factor into the USS and persisting the latter.
const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret};
std::optional<brillo::SecureBlob> wrapping_key =
key_blobs.DeriveUssCredentialSecret();
ASSERT_TRUE(wrapping_key.has_value());
EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakeLabel,
wrapping_key.value())
.ok());
CryptohomeStatusOr<brillo::Blob> encrypted_uss =
uss->GetEncryptedContainer(uss_main_key.value());
ASSERT_TRUE(encrypted_uss.ok());
EXPECT_TRUE(user_secret_stash_storage_
.Persist(encrypted_uss.value(), obfuscated_username)
.ok());
// Creating the auth session.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_TRUE(auth_session.user_exists());
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmBoundToPcr, _, _, _))
.WillOnce([this](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
std::move(derive_callback),
MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet(
{error::ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr));
return true;
});
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED);
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
}
// Test that an existing user with an existing pin auth factor can be
// authenticated, in case the UserSecretStash experiment is on.
TEST_F(AuthSessionWithUssExperimentTest, AuthenticatePinAuthFactorViaUss) {
// Setup.
const std::string obfuscated_username = SanitizeUserName(kFakeUsername);
const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk");
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the USS.
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status =
UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom());
ASSERT_TRUE(uss_status.ok());
std::unique_ptr<UserSecretStash> uss = std::move(uss_status).value();
std::optional<brillo::SecureBlob> uss_main_key =
UserSecretStash::CreateRandomMainKey();
ASSERT_TRUE(uss_main_key.has_value());
// Creating the auth factor. An arbitrary auth block state is used in this
// test.
AuthFactor auth_factor(
AuthFactorType::kPin, kFakePinLabel,
AuthFactorMetadata{.metadata = PinAuthFactorMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState()});
EXPECT_TRUE(
auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor)
.ok());
// Adding the auth factor into the USS and persisting the latter.
const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret};
std::optional<brillo::SecureBlob> wrapping_key =
key_blobs.DeriveUssCredentialSecret();
ASSERT_TRUE(wrapping_key.has_value());
EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakePinLabel,
wrapping_key.value())
.ok());
CryptohomeStatusOr<brillo::Blob> encrypted_uss =
uss->GetEncryptedContainer(uss_main_key.value());
ASSERT_TRUE(encrypted_uss.ok());
EXPECT_TRUE(user_secret_stash_storage_
.Persist(encrypted_uss.value(), obfuscated_username)
.ok());
// Creating the auth session.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_TRUE(auth_session.user_exists());
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<PinWeaverAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kPinWeaver));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kPinWeaver, _, _, _))
.WillOnce([&kFakePerCredentialSecret](
AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = kFakePerCredentialSecret;
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakePinLabel);
request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
}
TEST_F(AuthSessionWithUssExperimentTest, AddCryptohomeRecoveryAuthFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(
false, true, false, AuthFactorStorageType::kUserSecretStash))
.WillRepeatedly(Return(AuthBlockType::kCryptohomeRecovery));
EXPECT_CALL(auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kCryptohomeRecovery, _, _))
.WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob("fake vkk key");
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = CryptohomeRecoveryAuthBlockState();
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs),
std::move(auth_block_state));
return true;
});
// Calling AddAuthFactor.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_cryptohome_recovery_metadata();
request.mutable_auth_input()
->mutable_cryptohome_recovery_input()
->set_mediator_pub_key("mediator pub key");
// Test and Verify.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(
stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kCryptohomeRecovery)));
// There should be no verifier for the recovery factor.
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
TEST_F(AuthSessionWithUssExperimentTest,
AuthenticateCryptohomeRecoveryAuthFactor) {
// Setup.
const std::string obfuscated_username = SanitizeUserName(kFakeUsername);
const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk");
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the USS.
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status =
UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom());
ASSERT_TRUE(uss_status.ok());
std::unique_ptr<UserSecretStash> uss = std::move(uss_status).value();
std::optional<brillo::SecureBlob> uss_main_key =
UserSecretStash::CreateRandomMainKey();
ASSERT_TRUE(uss_main_key.has_value());
// Creating the auth factor.
AuthFactor auth_factor(
AuthFactorType::kCryptohomeRecovery, kFakeLabel,
AuthFactorMetadata{.metadata = CryptohomeRecoveryAuthFactorMetadata()},
AuthBlockState{.state = CryptohomeRecoveryAuthBlockState()});
EXPECT_TRUE(
auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor)
.ok());
// Adding the auth factor into the USS and persisting the latter.
const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret};
std::optional<brillo::SecureBlob> wrapping_key =
key_blobs.DeriveUssCredentialSecret();
ASSERT_TRUE(wrapping_key.has_value());
EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakeLabel,
wrapping_key.value())
.ok());
CryptohomeStatusOr<brillo::Blob> encrypted_uss =
uss->GetEncryptedContainer(uss_main_key.value());
ASSERT_TRUE(encrypted_uss.ok());
EXPECT_TRUE(user_secret_stash_storage_
.Persist(encrypted_uss.value(), obfuscated_username)
.ok());
// Creating the auth session.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
EXPECT_TRUE(auth_session.user_exists());
// Test.
// Setting the expectation that the auth block utility will generate recovery
// request.
EXPECT_CALL(auth_block_utility_, GenerateRecoveryRequest(_, _, _, _, _, _, _))
.WillOnce([](const std::string& obfuscated_username,
const cryptorecovery::RequestMetadata& request_metadata,
const brillo::Blob& epoch_response,
const CryptohomeRecoveryAuthBlockState& state,
hwsec::RecoveryCryptoFrontend* recovery_hwsec,
brillo::SecureBlob* out_recovery_request,
brillo::SecureBlob* out_ephemeral_pub_key) {
*out_ephemeral_pub_key = brillo::SecureBlob("test");
return OkStatus<CryptohomeCryptoError>();
});
EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr);
// Calling GetRecoveryRequest.
user_data_auth::GetRecoveryRequestRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
bool called = false;
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
EXPECT_TRUE(auth_session.GetRecoveryRequest(
request, base::BindOnce(
[](bool& called, user_data_auth::CryptohomeErrorCode& error,
const user_data_auth::GetRecoveryRequestReply& reply) {
called = true;
error = reply.error();
},
std::ref(called), std::ref(error))));
// Verify.
EXPECT_TRUE(called);
EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, error);
EXPECT_EQ(auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_TRUE(auth_session.cryptohome_recovery_ephemeral_pub_key_for_testing()
.has_value());
EXPECT_EQ(
auth_session.cryptohome_recovery_ephemeral_pub_key_for_testing().value(),
brillo::SecureBlob("test"));
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<CryptohomeRecoveryAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kCryptohomeRecovery));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kCryptohomeRecovery, _, _, _))
.WillOnce([&kFakePerCredentialSecret](
AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = kFakePerCredentialSecret;
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
return true;
});
// Calling AuthenticateAuthFactor.
user_data_auth::AuthenticateAuthFactorRequest authenticate_request;
authenticate_request.set_auth_session_id(auth_session.serialized_token());
authenticate_request.set_auth_factor_label(kFakeLabel);
authenticate_request.mutable_auth_input()
->mutable_cryptohome_recovery_input()
->mutable_recovery_response();
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
authenticate_request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
// There should be no verifier created for the recovery factor.
EXPECT_EQ(auth_session.TakeCredentialVerifier(), nullptr);
}
// Test that AuthenticateAuthFactor succeeds for the `AuthIntent::kVerifyOnly`
// scenario, using a credential verifier.
TEST_F(AuthSessionWithUssExperimentTest, LightweightPasswordAuthentication) {
// Setup.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Add the user session. Configure the credential verifier mock to succeed.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, VerifyCredentials(_)).WillOnce(Return(true));
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession with a fake factor. No authentication mocks are set
// up, because the lightweight authentication should be used in the test.
AuthSession auth_session(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), AuthBlockState()));
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Test.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor succeeds and doesn't use the credential
// verifier in the `AuthIntent::kDecrypt` scenario.
TEST_F(AuthSessionWithUssExperimentTest, NoLightweightAuthForDecryption) {
// Setup.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Add the user session. Expect that no verification calls are made.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, VerifyCredentials(_)).Times(0);
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession with a fake factor.
AuthSession auth_session(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
std::map<std::string, std::unique_ptr<AuthFactor>> auth_factor_map;
auth_factor_map.emplace(
kFakeLabel,
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), AuthBlockState()));
auth_session.set_label_to_auth_factor_for_testing(std::move(auth_factor_map));
// Set up VaultKeyset authentication mock.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
EXPECT_CALL(auth_block_utility_, GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([](AuthBlockType, const AuthInput&, const AuthBlockState&,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(),
std::make_unique<KeyBlobs>());
return true;
});
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
// Test.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
EXPECT_TRUE(auth_session.AuthenticateAuthFactor(
request, authenticate_future.GetCallback()));
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
TEST_F(AuthSessionWithUssExperimentTest, RemoveAuthFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
error = AddPinAuthFactor(kFakePin, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Both password and pin are available.
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword),
Pair(kFakePinLabel, AuthFactorType::kPin)));
EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakeLabel),
auth_session.label_to_auth_factor_.end());
EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakePinLabel),
auth_session.label_to_auth_factor_.end());
// Test.
// Calling RemoveAuthFactor for pin.
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakePinLabel);
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(request, remove_future.GetCallback());
EXPECT_THAT(remove_future.Get(), IsOk());
// Only password is available.
std::map<std::string, AuthFactorType> stored_factors_1 =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors_1,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword)));
EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakeLabel),
auth_session.label_to_auth_factor_.end());
EXPECT_EQ(auth_session.label_to_auth_factor_.find(kFakePinLabel),
auth_session.label_to_auth_factor_.end());
// Calling AuthenticateAuthFactor for password succeeds.
error = AuthenticatePasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Calling AuthenticateAuthFactor for pin fails.
user_data_auth::AuthenticateAuthFactorRequest auth_request;
auth_request.set_auth_session_id(auth_session.serialized_token());
auth_request.set_auth_factor_label(kFakePinLabel);
auth_request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_request,
authenticate_future.GetCallback());
// Verify.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
// The verifier still uses the password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
// The test adds, removes and adds the same auth factor again.
TEST_F(AuthSessionWithUssExperimentTest, RemoveAndReAddAuthFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
error = AddPinAuthFactor(kFakePin, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Calling RemoveAuthFactor for pin.
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakePinLabel);
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(request, remove_future.GetCallback());
EXPECT_THAT(remove_future.Get(), IsOk());
// Add the same pin auth factor again.
error = AddPinAuthFactor(kFakePin, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// The verifier still uses the original password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
TEST_F(AuthSessionWithUssExperimentTest, RemoveAuthFactorFailsForLastFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Calling RemoveAuthFactor for password.
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(request, remove_future.GetCallback());
// Verify.
ASSERT_THAT(remove_future.Get(), NotOk());
EXPECT_EQ(remove_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED);
// The verifier is still set after the removal failed.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
TEST_F(AuthSessionTest, RemoveAuthFactorFailsForUnauthenticatedAuthSession) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Test.
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(request, remove_future.GetCallback());
ASSERT_THAT(remove_future.Get(), NotOk());
EXPECT_EQ(remove_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION);
}
TEST_F(AuthSessionWithUssExperimentTest, UpdateAuthFactor) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::string new_pass = "update fake pass";
// Test.
// Calling UpdateAuthFactor.
error = UpdatePasswordAuthFactor(new_pass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
AuthSession new_auth_session(
kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_, &platform_,
&user_session_map_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
EXPECT_EQ(new_auth_session.GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(new_auth_session.authorized_intents(), IsEmpty());
// Verify.
// The credential verifier uses the new password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(new_pass));
// AuthenticateAuthFactor should succeed using the new password.
error = AuthenticatePasswordAuthFactor(new_pass, new_auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(new_auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
new_auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
TEST_F(AuthSessionWithUssExperimentTest, UpdateAuthFactorFailsForWrongLabel) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::string new_pass = "update fake pass";
// Test.
// Calling UpdateAuthFactor.
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label("different new label");
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(new_pass);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
EXPECT_EQ(update_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
// The verifier still uses the original password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
TEST_F(AuthSessionWithUssExperimentTest, UpdateAuthFactorFailsForWrongType) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Calling UpdateAuthFactor.
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kFakeLabel);
request.mutable_auth_factor()->set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_pin_metadata();
request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
EXPECT_EQ(update_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
// The verifier still uses the original password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
TEST_F(AuthSessionWithUssExperimentTest,
UpdateAuthFactorFailsWhenLabelDoesntExist) {
// Setup.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
AuthSession auth_session(kFakeUsername, flags, AuthIntent::kDecrypt,
/*on_timeout=*/base::DoNothing(), &crypto_,
&platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr);
EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(),
std::nullopt);
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakePass, auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Calling UpdateAuthFactor.
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label("label doesn't exist");
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(kFakeLabel);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
// Verify.
ASSERT_THAT(update_future.Get(), NotOk());
EXPECT_EQ(update_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
// The verifier still uses the original password.
EXPECT_THAT(auth_session.TakeCredentialVerifier(),
IsVerifierPtrForPassword(kFakePass));
}
} // namespace cryptohome