blob: 5abacbe64b227835e853bad46a3688596f1e6bf0 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/functional/bind.h>
#include <base/test/bind.h>
#include <base/test/mock_callback.h>
#include <base/test/task_environment.h>
#include <base/test/test_future.h>
#include <brillo/cryptohome.h>
#include <brillo/secure_blob.h>
#include <cryptohome/proto_bindings/auth_factor.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/factory/factory_impl.h>
#include <libhwsec/frontend/cryptohome/mock_frontend.h>
#include <libhwsec/frontend/pinweaver/mock_frontend.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "cryptohome/auth_blocks/auth_block.h"
#include "cryptohome/auth_blocks/auth_block_utility_impl.h"
#include "cryptohome/auth_blocks/mock_auth_block_utility.h"
#include "cryptohome/auth_blocks/scrypt_auth_block.h"
#include "cryptohome/auth_factor/auth_factor_manager.h"
#include "cryptohome/auth_factor/auth_factor_storage_type.h"
#include "cryptohome/auth_session_manager.h"
#include "cryptohome/challenge_credentials/mock_challenge_credentials_helper.h"
#include "cryptohome/cleanup/mock_user_oldest_activity_timestamp_manager.h"
#include "cryptohome/credentials.h"
#include "cryptohome/crypto.h"
#include "cryptohome/filesystem_layout.h"
#include "cryptohome/key_objects.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_install_attributes.h"
#include "cryptohome/mock_key_challenge_service_factory.h"
#include "cryptohome/mock_le_credential_manager.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/mock_vault_keyset_factory.h"
#include "cryptohome/pkcs11/mock_pkcs11_token_factory.h"
#include "cryptohome/storage/mock_homedirs.h"
#include "cryptohome/user_secret_stash.h"
#include "cryptohome/user_secret_stash_storage.h"
#include "cryptohome/user_session/mock_user_session_factory.h"
#include "cryptohome/user_session/user_session_map.h"
#include "cryptohome/userdataauth.h"
#include "cryptohome/vault_keyset.h"
namespace cryptohome {
namespace {
using ::testing::_;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Optional;
using ::testing::Return;
using base::test::TaskEnvironment;
using base::test::TestFuture;
using brillo::cryptohome::home::SanitizeUserName;
using cryptohome::error::CryptohomeCryptoError;
using hwsec_foundation::error::testing::IsOk;
using hwsec_foundation::error::testing::NotOk;
using hwsec_foundation::error::testing::ReturnValue;
using hwsec_foundation::status::OkStatus;
constexpr char kUsername[] = "foo@example.com";
constexpr char kPassword[] = "password";
constexpr char kPasswordLabel[] = "label";
constexpr char kPassword2[] = "password2";
constexpr char kPasswordLabel2[] = "label2";
constexpr char kDefaultLabel[] = "legacy-0";
constexpr char kSalt[] = "salt";
constexpr char kPublicHash[] = "public key hash";
constexpr char kPublicHash2[] = "public key hash2";
constexpr int kAuthValueRounds = 5;
// TODO(b/233700483): Replace this with the mock auth block.
class FallbackVaultKeyset : public VaultKeyset {
protected:
std::unique_ptr<cryptohome::SyncAuthBlock> GetAuthBlockForCreation()
const override {
auto auth_block_for_creation = VaultKeyset::GetAuthBlockForCreation();
if (!auth_block_for_creation) {
return std::make_unique<ScryptAuthBlock>();
}
return auth_block_for_creation;
}
};
// Helper function to create a mock vault keyset with some useful default
// functions to create basic minimal VKs.
std::unique_ptr<VaultKeysetFactory> CreateMockVaultKeysetFactory() {
auto factory = std::make_unique<MockVaultKeysetFactory>();
ON_CALL(*factory, New(_, _))
.WillByDefault([](Platform* platform, Crypto* crypto) {
auto* vk = new FallbackVaultKeyset();
vk->Initialize(platform, crypto);
return vk;
});
ON_CALL(*factory, NewBackup(_, _))
.WillByDefault([](Platform* platform, Crypto* crypto) {
auto* vk = new VaultKeyset();
vk->InitializeAsBackup(platform, crypto);
return vk;
});
return factory;
}
} // namespace
class AuthSessionTestWithKeysetManagement : public ::testing::Test {
public:
AuthSessionTestWithKeysetManagement() {
// Setting HWSec Expectations.
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()));
crypto_.set_le_manager_for_testing(
std::make_unique<MockLECredentialManager>());
crypto_.Init();
auth_block_utility_.InitializeChallengeCredentialsHelper(
&challenge_credentials_helper_, &key_challenge_service_factory_);
auth_session_manager_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
// Initializing UserData class.
userdataauth_.set_platform(&platform_);
userdataauth_.set_homedirs(&homedirs_);
userdataauth_.set_user_session_factory(&user_session_factory_);
userdataauth_.set_keyset_management(&keyset_management_);
userdataauth_.set_auth_factor_manager_for_testing(&auth_factor_manager_);
userdataauth_.set_user_secret_stash_storage_for_testing(
&user_secret_stash_storage_);
userdataauth_.set_auth_session_manager(auth_session_manager_.get());
userdataauth_.set_pkcs11_token_factory(&pkcs11_token_factory_);
userdataauth_.set_user_activity_timestamp_manager(
&user_activity_timestamp_manager_);
userdataauth_.set_install_attrs(&install_attrs_);
userdataauth_.set_mount_task_runner(
task_environment_.GetMainThreadTaskRunner());
userdataauth_.set_auth_block_utility(&auth_block_utility_);
file_system_keyset_ = FileSystemKeyset::CreateRandom();
AddUser(kUsername, kPassword);
PrepareDirectoryStructure();
}
~AuthSessionTestWithKeysetManagement() override {
// Reset USS experiment test flag.
ResetUserSecretStashExperimentFlagForTesting();
}
protected:
struct UserInfo {
Username name;
ObfuscatedUsername obfuscated;
brillo::SecureBlob passkey;
Credentials credentials;
base::FilePath homedir_path;
base::FilePath user_path;
};
void AddUser(const std::string& name, const std::string& password) {
Username username(name);
ObfuscatedUsername obfuscated = SanitizeUserName(username);
brillo::SecureBlob passkey(password);
Credentials credentials(username, passkey);
UserInfo info = {username,
obfuscated,
passkey,
credentials,
UserPath(obfuscated),
brillo::cryptohome::home::GetHashedUserPath(obfuscated)};
users_.push_back(info);
}
void PrepareDirectoryStructure() {
ASSERT_TRUE(platform_.CreateDirectory(ShadowRoot()));
ASSERT_TRUE(platform_.CreateDirectory(
brillo::cryptohome::home::GetUserPathPrefix()));
// We only need the homedir path, not the vault/mount paths.
for (const auto& user : users_) {
ASSERT_TRUE(platform_.CreateDirectory(user.homedir_path));
}
}
// Configures the mock Hwsec to simulate correct replies for authentication
// (unsealing) requests.
void SetUpHwsecAuthenticationMocks() {
// When sealing, remember the secret and configure the unseal mock to return
// it.
EXPECT_CALL(hwsec_, SealWithCurrentUser(_, _, _))
.WillRepeatedly([this](auto, auto, auto unsealed_value) {
EXPECT_CALL(hwsec_, UnsealWithCurrentUser(_, _, _))
.WillRepeatedly(ReturnValue(unsealed_value));
return brillo::Blob();
});
EXPECT_CALL(hwsec_, PreloadSealedData(_))
.WillRepeatedly(ReturnValue(std::nullopt));
}
void RemoveFactor(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_factor_label(label);
request.set_auth_session_id(auth_session.serialized_token());
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(request, remove_future.GetCallback());
EXPECT_THAT(remove_future.Get(), IsOk());
}
void KeysetSetUpWithoutKeyDataAndKeyBlobs() {
for (auto& user : users_) {
FallbackVaultKeyset vk;
vk.Initialize(&platform_, &crypto_);
vk.CreateFromFileSystemKeyset(file_system_keyset_);
AuthBlockState auth_block_state = {
.state = kTpmState,
};
ASSERT_THAT(vk.EncryptEx(kKeyBlobs, auth_block_state), IsOk());
ASSERT_TRUE(
vk.Save(user.homedir_path.Append(kKeyFile).AddExtension("0")));
}
}
void AddFactorWithMockAuthBlockUtility(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _))
.WillOnce(ReturnValue(AuthBlockType::kTpmEcc));
auto key_blobs = std::make_unique<KeyBlobs>(kKeyBlobs);
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = kTpmState;
EXPECT_CALL(mock_auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs, &auth_block_state](
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_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(label);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(secret);
request.set_auth_session_id(auth_session.serialized_token());
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
EXPECT_THAT(add_future.Get(), IsOk());
}
void AuthenticateAndMigrate(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
EXPECT_CALL(mock_auth_block_utility_,
GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmEcc));
auto key_blobs2 = std::make_unique<KeyBlobs>(kKeyBlobs);
EXPECT_CALL(mock_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;
});
auto key_blobs = std::make_unique<KeyBlobs>(kKeyBlobs);
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = kTpmState;
EXPECT_CALL(mock_auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillRepeatedly([&key_blobs, &auth_block_state](
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_state));
return true;
});
std::string auth_factor_labels[] = {label};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(secret);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
EXPECT_THAT(authenticate_future.Get(), IsOk());
}
void AddFactor(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
user_data_auth::AddAuthFactorRequest request;
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(label);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(secret);
request.set_auth_session_id(auth_session.serialized_token());
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
EXPECT_THAT(add_future.Get(), IsOk());
}
void UpdateFactor(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(label);
request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.mutable_auth_factor()->set_label(label);
request.mutable_auth_factor()->mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(secret);
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(request, update_future.GetCallback());
EXPECT_THAT(update_future.Get(), IsOk());
}
void AuthenticateFactor(AuthSession& auth_session,
const std::string& label,
const std::string& secret) {
std::string auth_factor_labels[] = {label};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(secret);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
EXPECT_THAT(authenticate_future.Get(), IsOk());
}
// Standard key blob and TPM state objects to use in testing.
const brillo::SecureBlob kBlob32{std::string(32, 'A')};
const brillo::SecureBlob kBlob16{std::string(16, 'C')};
const KeyBlobs kKeyBlobs{
.vkk_key = kBlob32, .vkk_iv = kBlob16, .chaps_iv = kBlob16};
const TpmEccAuthBlockState kTpmState = {
.salt = brillo::SecureBlob(kSalt),
.vkk_iv = kBlob32,
.auth_value_rounds = kAuthValueRounds,
.sealed_hvkkm = kBlob32,
.extended_sealed_hvkkm = kBlob32,
.tpm_public_key_hash = brillo::SecureBlob(kPublicHash),
};
base::test::TaskEnvironment task_environment_;
// Mocks and fakes for the test AuthSessions to use.
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
Crypto crypto_{&hwsec_, &pinweaver_, &cryptohome_keys_manager_,
/*recovery_hwsec=*/nullptr};
NiceMock<MockPlatform> platform_;
UserSessionMap user_session_map_;
KeysetManagement keyset_management_{&platform_, &crypto_,
CreateMockVaultKeysetFactory()};
AuthBlockUtilityImpl auth_block_utility_{
&keyset_management_, &crypto_, &platform_,
FingerprintAuthBlockService::MakeNullService()};
NiceMock<MockAuthBlockUtility> mock_auth_block_utility_;
AuthFactorManager auth_factor_manager_{&platform_};
UserSecretStashStorage user_secret_stash_storage_{&platform_};
AuthSession::BackingApis backing_apis_{&crypto_,
&platform_,
&user_session_map_,
&keyset_management_,
&auth_block_utility_,
&auth_factor_manager_,
&user_secret_stash_storage_};
// An AuthSession manager for testing managed creation.
std::unique_ptr<AuthSessionManager> auth_session_manager_;
FileSystemKeyset file_system_keyset_;
MockVaultKeysetFactory* mock_vault_keyset_factory_;
NiceMock<MockHomeDirs> homedirs_;
NiceMock<MockUserSessionFactory> user_session_factory_;
NiceMock<MockChallengeCredentialsHelper> challenge_credentials_helper_;
NiceMock<MockKeyChallengeServiceFactory> key_challenge_service_factory_;
NiceMock<MockPkcs11TokenFactory> pkcs11_token_factory_;
NiceMock<MockUserOldestActivityTimestampManager>
user_activity_timestamp_manager_;
NiceMock<MockInstallAttributes> install_attrs_;
UserDataAuth userdataauth_;
// Store user info for users that will be setup.
std::vector<UserInfo> users_;
};
// This test checks if StartAuthSession can return keydataless keysets
// correctly.
TEST_F(AuthSessionTestWithKeysetManagement, StartAuthSessionWithoutKeyData) {
KeysetSetUpWithoutKeyDataAndKeyBlobs();
user_data_auth::StartAuthSessionRequest start_auth_session_req;
start_auth_session_req.mutable_account_id()->set_account_id(*users_[0].name);
user_data_auth::StartAuthSessionReply auth_session_reply;
userdataauth_.StartAuthSession(
start_auth_session_req,
base::BindOnce(
[](user_data_auth::StartAuthSessionReply* auth_reply_ptr,
const user_data_auth::StartAuthSessionReply& reply) {
*auth_reply_ptr = reply;
},
base::Unretained(&auth_session_reply)));
EXPECT_EQ(auth_session_reply.error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply.auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_.auth_session_manager_->FindAuthSession(
auth_session_id.value());
EXPECT_TRUE(auth_session.AuthSessionStatus().ok());
}
// Test that a VaultKeyset without KeyData migration succeeds during login.
TEST_F(AuthSessionTestWithKeysetManagement,
MigrationToUssWithNoKeyDataAndNewFactor) {
if (!USE_USS_MIGRATION) {
GTEST_SKIP() << "Skipped because this test is valid only when USS "
"migration is enabled.";
}
// Setup
// UserSecretStash is not enabled, setup VaultKeysets for the user.
SetUpHwsecAuthenticationMocks();
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
AuthInput auth_input = {
.user_input = brillo::SecureBlob(kPassword),
.locked_to_single_user = std::nullopt,
.username = users_[0].name,
.obfuscated_username = users_[0].obfuscated,
};
auth_block_utility_.CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kTpmEcc, auth_input,
base::BindLambdaForTesting(
[&](CryptoStatus error, std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_block_state) {
ASSERT_THAT(error, IsOk());
FallbackVaultKeyset vk;
vk.Initialize(&platform_, &crypto_);
KeyData key_data;
key_data.set_label(kDefaultLabel);
vk.SetKeyData(key_data);
vk.CreateFromFileSystemKeyset(file_system_keyset_);
ASSERT_THAT(vk.EncryptEx(*key_blobs, *auth_block_state), IsOk());
ASSERT_TRUE(vk.Save(
users_[0].homedir_path.Append(kKeyFile).AddExtension("0")));
}));
// Set the UserSecretStash experiment for testing to enable USS
// migration with the authentication.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
// Test that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateFactor(*auth_session, kDefaultLabel, kPassword);
// Verify that the vault_keysets still exist and converted to backup and
// migrated VaultKeysets.
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kDefaultLabel);
ASSERT_NE(vk1, nullptr);
ASSERT_TRUE(vk1->IsForBackup());
ASSERT_TRUE(vk1->IsMigrated());
// Verify that migrator created the user_secret_stash and uss_main_key.
UserSecretStashStorage uss_storage(&platform_);
CryptohomeStatusOr<brillo::Blob> uss_serialized_container_status =
uss_storage.LoadPersisted(users_[0].obfuscated);
ASSERT_TRUE(uss_serialized_container_status.ok());
std::optional<brillo::SecureBlob> uss_credential_secret =
kKeyBlobs.DeriveUssCredentialSecret();
ASSERT_TRUE(uss_credential_secret.has_value());
brillo::SecureBlob decrypted_main_key;
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>>
user_secret_stash_status =
UserSecretStash::FromEncryptedContainerWithWrappingKey(
*uss_serialized_container_status, kDefaultLabel,
*uss_credential_secret, &decrypted_main_key);
ASSERT_TRUE(user_secret_stash_status.ok());
// Verify that the user_secret_stash has the wrapped_key_block for
// the default label.
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kDefaultLabel));
// Verify that the AuthFactors are created for the AuthFactor labels and
// storage type is updated in the AuthFactor map for each of them.
std::map<std::string, std::unique_ptr<AuthFactor>> factor_map =
auth_factor_manager_.LoadAllAuthFactors(users_[0].obfuscated);
ASSERT_NE(factor_map.find(kDefaultLabel), factor_map.end());
ASSERT_EQ(auth_session->auth_factor_map().Find(kDefaultLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
// Verify that the authentication succeeds after migration.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session2->status());
// Verify that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateFactor(*auth_session2, kDefaultLabel, kPassword);
// Test that adding a new keyset succeeds
AddFactor(*auth_session2, kPasswordLabel2, kPassword2);
// Verify authentication works with the added keyset
CryptohomeStatusOr<InUseAuthSession> auth_session3_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session3_status.ok());
AuthSession* auth_session3 = auth_session3_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session3->status());
// Verify that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_FALSE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateFactor(*auth_session3, kPasswordLabel2, kPassword2);
// Test that after the USS is disabled backup keysets are used for login.
// TODO(265967183): Disable USS migration rather than USS.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
CryptohomeStatusOr<InUseAuthSession> auth_session4_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session4_status.ok());
AuthSession* auth_session4 = auth_session4_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session4->status());
// Verify that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_TRUE(auth_session4->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session4->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateFactor(*auth_session4, kPasswordLabel2, kPassword2);
AuthenticateFactor(*auth_session4, kDefaultLabel, kPassword);
}
// Test that a VaultKeyset without KeyData migration succeeds during login.
TEST_F(AuthSessionTestWithKeysetManagement, MigrationToUssWithNoKeyData) {
if (!USE_USS_MIGRATION) {
GTEST_SKIP() << "Skipped because this test is valid only when USS "
"migration is enabled.";
}
// Setup
// UserSecretStash is not enabled, setup VaultKeysets for the user.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
KeysetSetUpWithoutKeyDataAndKeyBlobs();
// Set the UserSecretStash experiment for testing to enable USS
// migration with the authentication.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
// Test that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateAndMigrate(*auth_session, kDefaultLabel, kPassword);
// Verify that the vault_keysets still exist and converted to backup and
// migrated VaultKeysets.
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kDefaultLabel);
ASSERT_NE(vk1, nullptr);
ASSERT_TRUE(vk1->IsForBackup());
ASSERT_TRUE(vk1->IsMigrated());
// Verify that migrator created the user_secret_stash and uss_main_key.
UserSecretStashStorage uss_storage(&platform_);
CryptohomeStatusOr<brillo::Blob> uss_serialized_container_status =
uss_storage.LoadPersisted(users_[0].obfuscated);
ASSERT_TRUE(uss_serialized_container_status.ok());
std::optional<brillo::SecureBlob> uss_credential_secret =
kKeyBlobs.DeriveUssCredentialSecret();
ASSERT_TRUE(uss_credential_secret.has_value());
brillo::SecureBlob decrypted_main_key;
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>>
user_secret_stash_status =
UserSecretStash::FromEncryptedContainerWithWrappingKey(
*uss_serialized_container_status, kDefaultLabel,
*uss_credential_secret, &decrypted_main_key);
ASSERT_TRUE(user_secret_stash_status.ok());
// Verify that the user_secret_stash has the wrapped_key_block for
// the default label.
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kDefaultLabel));
// Verify that the AuthFactors are created for the AuthFactor labels and
// storage type is updated in the AuthFactor map for each of them.
std::map<std::string, std::unique_ptr<AuthFactor>> factor_map =
auth_factor_manager_.LoadAllAuthFactors(users_[0].obfuscated);
ASSERT_NE(factor_map.find(kDefaultLabel), factor_map.end());
ASSERT_EQ(auth_session->auth_factor_map().Find(kDefaultLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
// Verify that the authentication succeeds after migration.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session2->status());
// Test that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateAndMigrate(*auth_session2, kDefaultLabel, kPassword);
}
// Test that creating user with USS and adding AuthFactors adds backup
// VautlKeyset
TEST_F(AuthSessionTestWithKeysetManagement, USSEnabledCreatesBackupVKs) {
// Setup
// Set the UserSecretStash experiment for testing to enable USS path in the
// test
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add an initial and an additional factor
AddFactor(*auth_session, kPasswordLabel, kPassword);
AddFactor(*auth_session, kPasswordLabel2, kPassword2);
// Verify
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
EXPECT_TRUE(vk2->IsForBackup());
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
// Verify that AuthSession lists the backup VaultKeysets
// as the current AuthFactors on start, if USS is disabled.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
}
// Test that creating user and adding AuthFactors adds regular non-backup
// VautlKeysets if USS is not enabled
TEST_F(AuthSessionTestWithKeysetManagement, USSDisabledNotCreatesBackupVKs) {
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Test.
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add an initial and an additional factor
AddFactor(*auth_session, kPasswordLabel, kPassword);
AddFactor(*auth_session, kPasswordLabel2, kPassword2);
// Verify
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_FALSE(vk1->IsForBackup());
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
EXPECT_FALSE(vk2->IsForBackup());
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Verify that on AuthSession start it lists the VaultKeysetsas the current
// AuthFactors.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
}
// Test that backup VaultKeysets are removed together with the AuthFactor.
TEST_F(AuthSessionTestWithKeysetManagement, USSEnabledRemovesBackupVKs) {
// Setup
// Set the UserSecretStash experiment for testing to enable USS path in the
// test
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
AuthSession auth_session(
{.username = Username(kUsername),
.obfuscated_username = SanitizeUserName(Username(kUsername)),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = false,
.auth_factor_map = AuthFactorMap(),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
// Add factors and see backup VaultKeysets are also added.
AddFactor(auth_session, kPasswordLabel, kPassword);
AddFactor(auth_session, kPasswordLabel2, kPassword2);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
EXPECT_TRUE(vk2->IsForBackup());
EXPECT_TRUE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Test
RemoveFactor(auth_session, kPasswordLabel2, kPassword2);
// Verify that only the backup VaultKeyset for the removed label is deleted.
std::unique_ptr<VaultKeyset> vk3 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_EQ(vk3, nullptr);
std::unique_ptr<VaultKeyset> vk4 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk4, nullptr);
}
// Test that when user updates their credentials with USS backup VautlKeysets
// are kept as a backup.
TEST_F(AuthSessionTestWithKeysetManagement, USSEnabledUpdateBackupVKs) {
// Setup
// Set the UserSecretStash experiment for testing to enable USS path in the
// test
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
AuthSession auth_session(
{.username = Username(kUsername),
.obfuscated_username = SanitizeUserName(Username(kUsername)),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = false,
.auth_factor_map = AuthFactorMap(),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
// Add an initial factor to USS and backup VK
AddFactor(auth_session, kPasswordLabel, kPassword);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
EXPECT_TRUE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Test
// Update the auth factor and see the backup VaultKeyset is still a backup.
UpdateFactor(auth_session, kPasswordLabel, kPassword2);
// Verify
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk2, nullptr);
EXPECT_TRUE(vk2->IsForBackup());
EXPECT_TRUE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Verify that on AuthSession start it lists the USS-AuthFactors.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_->CreateAuthSession(
Username(kUsername),
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
}
// Test that authentication with backup VK succeeds when USS is rolled back
// after UpdateAuthFactor.
TEST_F(AuthSessionTestWithKeysetManagement,
USSRollbackAuthWithUpdatedBackupVKSuccess) {
// Setup
// Set the UserSecretStash experiment for testing to enable USS path in the
// test
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _))
.WillRepeatedly(ReturnValue(AuthBlockType::kTpmEcc));
// Add an initial factor to USS and backup VK and update password.
auto key_blobs = std::make_unique<KeyBlobs>();
const brillo::SecureBlob kBlob32(32, 'A');
const brillo::SecureBlob kBlob16(16, 'C');
key_blobs->vkk_key = kBlob32;
key_blobs->vkk_iv = kBlob16;
key_blobs->chaps_iv = kBlob16;
TpmEccAuthBlockState tpm_state = {
.salt = brillo::SecureBlob(kSalt),
.vkk_iv = kBlob32,
.auth_value_rounds = kAuthValueRounds,
.sealed_hvkkm = kBlob32,
.extended_sealed_hvkkm = kBlob32,
.tpm_public_key_hash = brillo::SecureBlob(kPublicHash),
};
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = tpm_state;
EXPECT_CALL(mock_auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs, &auth_block_state](
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_state));
return true;
});
AddFactor(*auth_session, kPasswordLabel, kPassword);
// KeyBlobs associated with second password.
auto key_blobs2 = std::make_unique<KeyBlobs>();
const brillo::SecureBlob kNewBlob32(32, 'B');
const brillo::SecureBlob kNewBlob16(16, 'D');
key_blobs2->vkk_key = kNewBlob32;
key_blobs2->vkk_iv = kNewBlob16;
key_blobs2->chaps_iv = kNewBlob16;
TpmEccAuthBlockState tpm_state2 = {
.salt = brillo::SecureBlob(kSalt),
.vkk_iv = kNewBlob32,
.auth_value_rounds = kAuthValueRounds,
.sealed_hvkkm = kNewBlob32,
.extended_sealed_hvkkm = kNewBlob32,
.tpm_public_key_hash = brillo::SecureBlob(kPublicHash2),
};
auto auth_block_state2 = std::make_unique<AuthBlockState>();
auth_block_state2->state = tpm_state2;
EXPECT_CALL(mock_auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs2, &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_blobs2),
std::move(auth_block_state2));
return true;
});
UpdateFactor(*auth_session, kPasswordLabel, kPassword2);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Test
// See that authentication with the backup password succeeds
// if USS is disabled after the update.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_CALL(mock_auth_block_utility_,
GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmEcc));
// The same KeyBlobs associated with the second password.
auto key_blobs3 = std::make_unique<KeyBlobs>();
key_blobs3->vkk_key = kNewBlob32;
key_blobs3->vkk_iv = kNewBlob16;
key_blobs3->chaps_iv = kNewBlob16;
EXPECT_CALL(mock_auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([&key_blobs3](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_blobs3));
return true;
});
AuthenticateFactor(*auth_session2, kPasswordLabel, kPassword2);
// Verify
EXPECT_EQ(auth_session2->status(), AuthStatus::kAuthStatusAuthenticated);
}
// Test that authentication with backup VK succeeds when USS is rolled back.
TEST_F(AuthSessionTestWithKeysetManagement,
USSRollbackAuthWithBackupVKSuccess) {
// Setup
// Set the UserSecretStash experiment for testing to enable USS path in the
// test
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
AuthSession::BackingApis backing_apis = backing_apis_;
backing_apis.auth_block_utility = &mock_auth_block_utility_;
AuthSession auth_session(
{.username = Username(kUsername),
.obfuscated_username = SanitizeUserName(Username(kUsername)),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = false,
.auth_factor_map = AuthFactorMap(),
.migrate_to_user_secret_stash = false},
backing_apis);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _))
.WillOnce(ReturnValue(AuthBlockType::kTpmEcc));
auto key_blobs = std::make_unique<KeyBlobs>();
const brillo::SecureBlob kBlob32(32, 'A');
const brillo::SecureBlob kBlob16(16, 'C');
key_blobs->vkk_key = kBlob32;
key_blobs->vkk_iv = kBlob16;
key_blobs->chaps_iv = kBlob16;
TpmEccAuthBlockState tpm_state = {
.salt = brillo::SecureBlob(kSalt),
.vkk_iv = kBlob32,
.auth_value_rounds = kAuthValueRounds,
.sealed_hvkkm = kBlob32,
.extended_sealed_hvkkm = kBlob32,
.tpm_public_key_hash = brillo::SecureBlob(kPublicHash),
};
auto auth_block_state = std::make_unique<AuthBlockState>();
auth_block_state->state = tpm_state;
EXPECT_CALL(mock_auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&key_blobs, &auth_block_state](
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_state));
return true;
});
// Add an initial factor to USS and backup VK
AddFactor(auth_session, kPasswordLabel, kPassword);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
EXPECT_TRUE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session.auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Test
// See that authentication with the backup password succeeds if USS is
// disabled.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(
Username(kUsername),
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_CALL(mock_auth_block_utility_,
GetAuthBlockStateFromVaultKeyset(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmEcc));
auto key_blobs2 = std::make_unique<KeyBlobs>();
key_blobs2->vkk_key = kBlob32;
key_blobs2->vkk_iv = kBlob16;
key_blobs2->chaps_iv = kBlob16;
EXPECT_CALL(mock_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;
});
AuthenticateFactor(*auth_session2, kPasswordLabel, kPassword);
// Verify
EXPECT_EQ(auth_session2->status(), AuthStatus::kAuthStatusAuthenticated);
}
// Test that AuthSession list the non-backup VKs on session start
TEST_F(AuthSessionTestWithKeysetManagement, USSDisableddNotListBackupVKs) {
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add factors
AddFactor(*auth_session, kPasswordLabel, kPassword);
AddFactor(*auth_session, kPasswordLabel2, kPassword2);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
// Test
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
// Verify
EXPECT_FALSE(vk1->IsForBackup());
EXPECT_FALSE(vk2->IsForBackup());
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_THAT(auth_session2->auth_factor_map().Find(kPasswordLabel),
Optional(_));
EXPECT_THAT(auth_session2->auth_factor_map().Find(kPasswordLabel2),
Optional(_));
}
// Test that AuthSession list the backup VKs on session start if USS is disabled
// after being enabled.
TEST_F(AuthSessionTestWithKeysetManagement, USSRollbackListBackupVKs) {
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add factors
AddFactor(*auth_session, kPasswordLabel, kPassword);
AddFactor(*auth_session, kPasswordLabel2, kPassword2);
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
// Test
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
// Verify
EXPECT_TRUE(vk1->IsForBackup());
EXPECT_TRUE(vk2->IsForBackup());
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_THAT(auth_session2->auth_factor_map().Find(kPasswordLabel),
Optional(_));
EXPECT_THAT(auth_session2->auth_factor_map().Find(kPasswordLabel2),
Optional(_));
}
// Test that VaultKeysets are migrated to UserSecretStash when migration is
// enabled, converting the existing VaultKeysets to migrated VaultKeysets.
TEST_F(AuthSessionTestWithKeysetManagement, MigrationEnabledMigratesToUss) {
if (!USE_USS_MIGRATION) {
GTEST_SKIP() << "Skipped because this test is valid only when USS "
"migration is enabled.";
}
// Setup
// UserSecretStash is not enabled, setup VaultKeysets for the user.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add the first factor with VaultKeyset backing.
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel, kPassword);
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel2, kPassword2);
// Set the UserSecretStash experiment for testing to enable USS
// migration with the authentication.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
// Test that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateAndMigrate(*auth_session2, kPasswordLabel, kPassword);
CryptohomeStatusOr<InUseAuthSession> auth_session3_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session3_status.ok());
AuthSession* auth_session3 = auth_session3_status.value().Get();
AuthenticateAndMigrate(*auth_session3, kPasswordLabel2, kPassword2);
// Verify
// Verify that migrator loaded the user_secret_stash and uss_main_key.
UserSecretStashStorage uss_storage(&platform_);
CryptohomeStatusOr<brillo::Blob> uss_serialized_container_status =
uss_storage.LoadPersisted(users_[0].obfuscated);
ASSERT_TRUE(uss_serialized_container_status.ok());
std::optional<brillo::SecureBlob> uss_credential_secret =
kKeyBlobs.DeriveUssCredentialSecret();
ASSERT_TRUE(uss_credential_secret.has_value());
brillo::SecureBlob decrypted_main_key;
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>>
user_secret_stash_status =
UserSecretStash::FromEncryptedContainerWithWrappingKey(
*uss_serialized_container_status, kPasswordLabel,
*uss_credential_secret, &decrypted_main_key);
ASSERT_TRUE(user_secret_stash_status.ok());
// Verify that the user_secret_stash has the wrapped_key_blocks for the
// AuthFactor labels.
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kPasswordLabel));
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kPasswordLabel2));
// Verify that the AuthFactors are created for the AuthFactor labels and
// storage type is updated in the AuthFactor map for each of them.
std::map<std::string, std::unique_ptr<AuthFactor>> factor_map =
auth_factor_manager_.LoadAllAuthFactors(users_[0].obfuscated);
ASSERT_NE(factor_map.find(kPasswordLabel), factor_map.end());
ASSERT_NE(factor_map.find(kPasswordLabel2), factor_map.end());
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
// Verify that the vault_keysets still exist and converted to migrated
// VaultKeysets.
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
EXPECT_NE(vk1, nullptr);
EXPECT_TRUE(vk1->IsForBackup());
EXPECT_TRUE(vk1->IsMigrated());
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
EXPECT_NE(vk2, nullptr);
EXPECT_TRUE(vk2->IsForBackup());
EXPECT_TRUE(vk2->IsMigrated());
ResetUserSecretStashExperimentFlagForTesting();
}
// Test that after a VaultKeyset is migrated to UserSecretStash the next
// factor is added as migrated VaultKeysets.
TEST_F(AuthSessionTestWithKeysetManagement,
MigrationEnabledAddNextFactorsToUss) {
if (!USE_USS_MIGRATION) {
GTEST_SKIP() << "Skipped because this test is valid only when USS "
"migration is enabled.";
}
// Setup
// UserSecretStash is not enabled, setup VaultKeysets for the user.
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
// Add the first factor with VaultKeyset backing.
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel, kPassword);
// Set the UserSecretStash experiment for testing to enable USS
// migration with the authentication.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
// Test that authenticating the password should migrate VaultKeyset to
// UserSecretStash, converting the VaultKeyset to a backup VaultKeyset.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AuthenticateAndMigrate(*auth_session2, kPasswordLabel, kPassword);
// Verify that the vault_keysets still exist and converted to backup and
// migrated VaultKeysets.
std::unique_ptr<VaultKeyset> vk1 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel);
ASSERT_NE(vk1, nullptr);
ASSERT_TRUE(vk1->IsForBackup());
ASSERT_TRUE(vk1->IsMigrated());
// Test that adding a second factor adds as a USS AuthFactor with a backup &
// migrated VK.
AddFactorWithMockAuthBlockUtility(*auth_session2, kPasswordLabel2,
kPassword2);
// Added vault_keyset should be a backup and migrated VaultKeyset.
std::unique_ptr<VaultKeyset> vk2 =
keyset_management_.GetVaultKeyset(users_[0].obfuscated, kPasswordLabel2);
ASSERT_NE(vk2, nullptr);
ASSERT_TRUE(vk2->IsForBackup());
ASSERT_TRUE(vk2->IsMigrated());
// Verify
// Create a new AuthSession for verifications.
CryptohomeStatusOr<InUseAuthSession> auth_session3_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session3_status.ok());
AuthSession* auth_session3 = auth_session3_status.value().Get();
AuthenticateAndMigrate(*auth_session3, kPasswordLabel2, kPassword2);
// Verify that migrator created the user_secret_stash and uss_main_key.
UserSecretStashStorage uss_storage(&platform_);
CryptohomeStatusOr<brillo::Blob> uss_serialized_container_status =
uss_storage.LoadPersisted(users_[0].obfuscated);
ASSERT_TRUE(uss_serialized_container_status.ok());
std::optional<brillo::SecureBlob> uss_credential_secret =
kKeyBlobs.DeriveUssCredentialSecret();
ASSERT_TRUE(uss_credential_secret.has_value());
brillo::SecureBlob decrypted_main_key;
CryptohomeStatusOr<std::unique_ptr<UserSecretStash>>
user_secret_stash_status =
UserSecretStash::FromEncryptedContainerWithWrappingKey(
*uss_serialized_container_status, kPasswordLabel,
*uss_credential_secret, &decrypted_main_key);
ASSERT_TRUE(user_secret_stash_status.ok());
// Verify that the user_secret_stash has the wrapped_key_blocks for both
// AuthFactor labels.
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kPasswordLabel));
ASSERT_TRUE(
user_secret_stash_status.value()->HasWrappedMainKey(kPasswordLabel2));
// Verify that the AuthFactors are created for the AuthFactor labels and
// storage type is updated in the AuthFactor map for each of them.
std::map<std::string, std::unique_ptr<AuthFactor>> factor_map =
auth_factor_manager_.LoadAllAuthFactors(users_[0].obfuscated);
ASSERT_NE(factor_map.find(kPasswordLabel), factor_map.end());
ASSERT_NE(factor_map.find(kPasswordLabel2), factor_map.end());
ASSERT_EQ(
auth_session2->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
ASSERT_EQ(
auth_session2->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
}
// Test that AuthSession's auth factor map lists the factor from right backing
// store during the migration.
TEST_F(AuthSessionTestWithKeysetManagement,
AuthFactorMapStatusDuringMigration) {
if (!USE_USS_MIGRATION) {
GTEST_SKIP() << "Skipped because this test is valid only when USS "
"migration is enabled.";
}
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel, kPassword);
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel2, kPassword2);
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
// Tests
// 1-Test that enabling UserSecretStash doesn't change the AuthFactorMap when
// there are only regular VaultKeysets.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
// Start a new AuthSession to test with a fresh session.
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
// 2- Test migration of the first factor on auth_session2. Storage type for
// the migrated factor should be KUserSecretStash and non-migrated factor
// should be kVaultKeyset.
AuthenticateAndMigrate(*auth_session2, kPasswordLabel, kPassword);
// auth_session3 should list both the migrated factor and the not migrated VK
CryptohomeStatusOr<InUseAuthSession> auth_session3_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session3_status.ok());
AuthSession* auth_session3 = auth_session3_status.value().Get();
EXPECT_TRUE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
// 3- Test migration of the second factor on auth_session3. Storage type for
// the migrated factors should be KUserSecretStash.
AuthenticateAndMigrate(*auth_session3, kPasswordLabel2, kPassword2);
EXPECT_FALSE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session3->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
ASSERT_EQ(
auth_session3->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
// 4- Test that when UserSecretStash is disabled AuthSession lists the backup
// VaultKeysets on the map.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
CryptohomeStatusOr<InUseAuthSession> auth_session4_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session4_status.ok());
AuthSession* auth_session4 = auth_session4_status.value().Get();
EXPECT_TRUE(auth_session4->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session4->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session4->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
ASSERT_EQ(
auth_session4->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
}
// Test that AuthSession's auth factor map lists the factor from right backing
// store on session start.
TEST_F(AuthSessionTestWithKeysetManagement, AuthFactorMapRegularVaultKeysets) {
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
// Test
// Test that adding regular VaultKeysets update the map to contain
// VaultKeysets.
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel, kPassword);
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel2, kPassword2);
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
// Verify that the |auth_factor_map| contains the two VaultKeyset factor.
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
}
// Test that AuthSession's auth factor map lists the factor from right backing
// store on session start when USS is enabled.
TEST_F(AuthSessionTestWithKeysetManagement, AuthFactorMapUserSecretStash) {
// Setup
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE;
// Attach the mock_auth_block_utility to our AuthSessionManager and created
// AuthSession.
auto auth_session_manager_impl_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
&mock_auth_block_utility_, &auth_factor_manager_,
&user_secret_stash_storage_);
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session->status());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
// Test
// Test that adding AuthFactors update the map to contain
// these AuthFactors with kUserSecretStash backing store.
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel, kPassword);
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
AddFactorWithMockAuthBlockUtility(*auth_session, kPasswordLabel2, kPassword2);
EXPECT_FALSE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_TRUE(auth_session->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
// Verify that the |auth_factor_map| contains the two labels with
// kUserSecretStash backing store.
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
ASSERT_EQ(
auth_session->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kUserSecretStash);
// When UserSecretStash is disabled |auth_factor_map| lists the backup
// VaultKeysets.
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
CryptohomeStatusOr<InUseAuthSession> auth_session2_status =
auth_session_manager_impl_->CreateAuthSession(Username(kUsername), flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session2_status.ok());
AuthSession* auth_session2 = auth_session2_status.value().Get();
EXPECT_TRUE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset));
EXPECT_FALSE(auth_session2->auth_factor_map().HasFactorWithStorage(
AuthFactorStorageType::kUserSecretStash));
ASSERT_EQ(
auth_session2->auth_factor_map().Find(kPasswordLabel)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
ASSERT_EQ(
auth_session2->auth_factor_map().Find(kPasswordLabel2)->storage_type(),
AuthFactorStorageType::kVaultKeyset);
}
// Test the scenario of adding a new factor when the authenticated factor's
// backup VaultKeyset was corrupted. The operation should succeed, but you will
// no longer get backup VKs.
TEST_F(AuthSessionTestWithKeysetManagement, AddFactorAfterBackupVkCorruption) {
// Setup.
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
SetUpHwsecAuthenticationMocks();
// Creating the user with a password factor.
{
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(
Username(kUsername), user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(auth_session_status, IsOk());
AuthSession& auth_session = *auth_session_status.value().Get();
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
AddFactor(auth_session, kPasswordLabel, kPassword);
}
// Corrupt the backup VK (it's the user's only VK) by truncating it.
const base::FilePath vk_path =
VaultKeysetPath(SanitizeUserName(Username(kUsername)), /*index=*/0);
EXPECT_TRUE(platform_.FileExists(vk_path));
EXPECT_TRUE(platform_.WriteFile(vk_path, brillo::Blob()));
// Creating a new AuthSession for authentication.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_->CreateAuthSession(
Username(kUsername), user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(auth_session_status, IsOk());
AuthSession& auth_session = *auth_session_status.value().Get();
// Authenticating the AuthSession via the password.
std::string auth_factor_labels[] = {kPasswordLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kPassword);
TestFuture<CryptohomeStatus> auth_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
auth_future.GetCallback());
EXPECT_THAT(auth_future.Get(), IsOk());
// Test.
user_data_auth::AddAuthFactorRequest add_request;
add_request.set_auth_session_id(auth_session.serialized_token());
add_request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
add_request.mutable_auth_factor()->set_label(kPasswordLabel2);
add_request.mutable_auth_factor()->mutable_password_metadata();
add_request.mutable_auth_input()->mutable_password_input()->set_secret(
kPassword2);
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(add_request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
const base::FilePath added_factor_vk_path =
VaultKeysetPath(SanitizeUserName(Username(kUsername)), /*index=*/1);
EXPECT_FALSE(platform_.FileExists(added_factor_vk_path));
}
} // namespace cryptohome