blob: 6f548ff2d1a1dab6b0cefa812e279e8e847d0495 [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 <vector>
#include <base/functional/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 <base/unguessable_token.h>
#include <brillo/cryptohome.h>
#include <brillo/secure_blob.h>
#include <cryptohome/proto_bindings/auth_factor.pb.h>
#include <cryptohome/proto_bindings/UserDataAuth.pb.h>
#include <gmock/gmock-matchers.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_storage_type.h"
#include "cryptohome/auth_factor/auth_factor_type.h"
#include "cryptohome/auth_session_manager.h"
#include "cryptohome/challenge_credentials/mock_challenge_credentials_helper.h"
#include "cryptohome/credential_verifier_test_utils.h"
#include "cryptohome/crypto_error.h"
#include "cryptohome/error/cryptohome_error.h"
#include "cryptohome/flatbuffer_schemas/auth_block_state.h"
#include "cryptohome/key_objects.h"
#include "cryptohome/mock_credential_verifier.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_key_challenge_service_factory.h"
#include "cryptohome/mock_keyset_management.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/pkcs11/mock_pkcs11_token_factory.h"
#include "cryptohome/scrypt_verifier.h"
#include "cryptohome/smart_card_verifier.h"
#include "cryptohome/storage/homedirs.h"
#include "cryptohome/storage/mock_mount.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/real_user_session.h"
#include "cryptohome/user_session/user_session_map.h"
#include "cryptohome/username.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::ReturnOk;
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::DoAll;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Matcher;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Optional;
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";
constexpr char kLegacyLabel[] = "legacy-0";
constexpr char kRecoveryLabel[] = "recovery";
// 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";
constexpr char kFakeRecoverySecret[] = "test_recovery_secret";
// Set to match the 5 minute timer and a 1 minute extension in AuthSession.
constexpr int kAuthSessionExtensionDuration = 60;
constexpr auto kAuthSessionTimeout = base::Minutes(5);
constexpr auto kAuthSessionExtension =
base::Seconds(kAuthSessionExtensionDuration);
// 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>(_));
}
std::unique_ptr<VaultKeyset> CreatePasswordVaultKeyset(
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);
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized_vk);
return vk;
}
std::unique_ptr<VaultKeyset> CreateBackupVaultKeyset(const std::string& label) {
auto backup_vk = CreatePasswordVaultKeyset(label);
backup_vk->set_backup_vk_for_testing(true);
backup_vk->SetResetSeed(brillo::SecureBlob(32, 'A'));
backup_vk->SetWrappedResetSeed(brillo::SecureBlob(32, 'B'));
return backup_vk;
}
// Create an auth factor map with a single password factor. Takes as a template
// parameter the type of auth block state that the factor should have, or void
// for no state.
template <typename StateType>
AuthFactorMap FactorMapWithPassword(std::string label) {
AuthBlockState auth_block_state;
if constexpr (!std::is_void_v<StateType>) {
auth_block_state.state = StateType();
}
AuthFactorMap map;
map.Add(
std::make_unique<AuthFactor>(AuthFactorType::kPassword, std::move(label),
AuthFactorMetadata(), auth_block_state),
AuthFactorStorageType::kVaultKeyset);
return map;
}
// Create an auth factor map with a single PIN factor.
AuthFactorMap FactorMapWithPin(std::string label) {
AuthBlockState auth_block_state;
auth_block_state.state = PinWeaverAuthBlockState();
AuthFactorMap map;
map.Add(std::make_unique<AuthFactor>(AuthFactorType::kPin, std::move(label),
AuthFactorMetadata(), auth_block_state),
AuthFactorStorageType::kVaultKeyset);
return map;
}
} // namespace
class AuthSessionTest : public ::testing::Test {
public:
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();
EXPECT_CALL(auth_block_utility_, CreateCredentialVerifier(_, _, _))
.WillRepeatedly(
[](AuthFactorType type, const std::string& label,
const AuthInput& input) -> std::unique_ptr<CredentialVerifier> {
if (type == AuthFactorType::kPassword) {
return ScryptVerifier::Create(
label, brillo::SecureBlob(*input.user_input));
}
return nullptr;
});
}
protected:
// Fake username to be used in this test suite.
const Username kFakeUsername{"test_username"};
user_data_auth::CryptohomeErrorCode AuthenticateAuthFactorVK(
const std::string& label,
const std::string& passkey,
AuthSession& auth_session) {
// Used to mock out keyset factories with something that returns a
// vanilla keyset with the supplied label.
auto make_vk_with_label = [label](auto...) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyDataLabel(label);
return vk;
};
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, label))
.WillRepeatedly(make_vk_with_label);
EXPECT_CALL(auth_block_utility_,
GetAuthBlockStateFromVaultKeyset(label, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillRepeatedly(make_vk_with_label);
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(keyset_management_, AddResetSeedIfMissing(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillRepeatedly([](AuthBlockType auth_block_type,
const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(),
std::make_unique<KeyBlobs>());
});
std::string auth_factor_labels[] = {label};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(passkey);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
if (authenticate_future.Get().ok()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return authenticate_future.Get()->local_legacy_error().value();
}
// Get a UserSession for the given user, creating a minimal stub one if
// necessary.
UserSession* FindOrCreateUserSession(const Username& username) {
if (UserSession* session = user_session_map_.Find(username)) {
return session;
}
user_session_map_.Add(
username, std::make_unique<RealUserSession>(
username, &homedirs_, &keyset_management_,
&user_activity_timestamp_manager_, &pkcs11_token_factory_,
new NiceMock<MockMount>()));
return user_session_map_.Find(username);
}
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<base::SequencedTaskRunner> task_runner_ =
base::SequencedTaskRunnerHandle::Get();
// 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_, nullptr};
NiceMock<MockPlatform> platform_;
UserSessionMap user_session_map_;
NiceMock<MockKeysetManagement> keyset_management_;
NiceMock<MockAuthBlockUtility> auth_block_utility_;
AuthBlockUtilityImpl auth_block_utility_impl_{
&keyset_management_, &crypto_, &platform_,
FingerprintAuthBlockService::MakeNullService()};
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.
AuthSessionManager auth_session_manager_{&crypto_,
&platform_,
&user_session_map_,
&keyset_management_,
&auth_block_utility_,
&auth_factor_manager_,
&user_secret_stash_storage_};
// Mocks needed for challenge credential tests.
NiceMock<MockChallengeCredentialsHelper> challenge_credentials_helper_;
NiceMock<MockKeyChallengeServiceFactory> key_challenge_service_factory_;
// Mocks and fakes for UserSession to use.
HomeDirs homedirs_{&platform_,
std::make_unique<policy::PolicyProvider>(nullptr),
HomeDirs::RemoveCallback(),
/*vault_factory=*/nullptr};
UserOldestActivityTimestampManager user_activity_timestamp_manager_{
&platform_};
NiceMock<MockPkcs11TokenFactory> pkcs11_token_factory_;
};
const CryptohomeError::ErrorLocationPair kErrorLocationForTestingAuthSession =
CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1),
std::string("MockErrorLocationAuthSession"));
TEST_F(AuthSessionTest, InitiallyNotAuthenticated) {
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session->authorized_intents(), IsEmpty());
}
TEST_F(AuthSessionTest, InitiallyNotAuthenticatedForExistingUser) {
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session->authorized_intents(), IsEmpty());
}
TEST_F(AuthSessionTest, Username) {
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->username(), kFakeUsername);
EXPECT_EQ(auth_session->obfuscated_username(),
SanitizeUserName(kFakeUsername));
}
TEST_F(AuthSessionTest, DecryptionIntent) {
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->auth_intent(), AuthIntent::kDecrypt);
}
TEST_F(AuthSessionTest, VerfyIntent) {
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->auth_intent(), AuthIntent::kVerifyOnly);
}
TEST_F(AuthSessionTest, WebAuthnIntent) {
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kWebAuthn);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_EQ(auth_session->auth_intent(), AuthIntent::kWebAuthn);
}
TEST_F(AuthSessionTest, TimeoutTest) {
TestFuture<base::UnguessableToken> timeout_future;
// AuthSession must be constructed without using AuthSessionManager,
// because during cleanup the AuthSession must stay valid after
// timing out for verification.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout =
timeout_future.GetCallback<const base::UnguessableToken&>(),
.user_exists = false,
.auth_factor_map = AuthFactorMap(),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_EQ(auth_session.status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
auth_session.SetAuthSessionAsAuthenticated(kAuthorizedIntentsForFullAuth);
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
auth_session.timeout_timer_.FireNow();
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusTimedOut);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
EXPECT_TRUE(timeout_future.IsReady());
EXPECT_EQ(timeout_future.Get(), auth_session.token());
}
// Test the scenario when `kCrOSLateBootMigrateToUserSecretStash` feature cannot
// be checked due to the feature lib unavailability. AuthSession should fall
// back to the default value (and not crash).
TEST_F(AuthSessionTest, UssMigrationFlagCheckFailure) {
// AuthSession must be constructed without using AuthSessionManager,
// as we need to have a nullptr for feature_lib.
auto auth_session = AuthSession::Create(
kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, base::DoNothing(),
/*feature_lib=*/nullptr, backing_apis_);
// Verify.
ASSERT_THAT(auth_session, NotNull());
EXPECT_FALSE(auth_session->migrate_to_user_secret_stash_);
}
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 = platform_.CreateUnguessableToken();
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);
}
// Test that `GetSerializedStringFromToken()` refuses a string containing only
// zero bytes (but doesn't crash). Note: such a string would've corresponded to
// `base::UnguessableToken::Null()` if the latter would be allowed.
TEST_F(AuthSessionTest, TokenFromAllZeroesString) {
// Setup. To avoid hardcoding the length of the string in the test, first
// serialize an arbitrary token and then replace its contents with zeroes.
const base::UnguessableToken some_token = base::UnguessableToken::Create();
const std::optional<std::string> serialized_some_token =
AuthSession::GetSerializedStringFromToken(some_token);
ASSERT_TRUE(serialized_some_token.has_value());
const std::string all_zeroes_token(serialized_some_token->length(), '\0');
// Test.
std::optional<base::UnguessableToken> deserialized_token =
AuthSession::GetTokenFromSerializedString(all_zeroes_token);
// Verify.
EXPECT_EQ(deserialized_token, std::nullopt);
}
// Test that AuthenticateAuthFactor succeeds and doesn't use the credential
// verifier in the `AuthIntent::kDecrypt` scenario.
TEST_F(AuthSessionTest, NoLightweightAuthForDecryption) {
// Add the user session. It will have no verifiers.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession with a fake factor.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = FactorMapWithPassword<void>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
SetUserSecretStashExperimentForTesting(/*enabled=*/false);
// 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>());
});
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// Test.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
// Test if AuthSession reports the correct attributes on an already-existing
// ephemeral user.
TEST_F(AuthSessionTest, ExistingEphemeralUser) {
// Setup.
int flags =
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER;
// Setting the expectation that there is no persistent user but there is an
// active ephemeral one.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive()).WillRepeatedly(Return(true));
user_session_map_.Add(kFakeUsername, std::move(user_session));
// Test.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Verify.
EXPECT_TRUE(auth_session->user_exists());
}
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Test.
EXPECT_FALSE(auth_session->has_user_secret_stash());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
// Verify.
EXPECT_FALSE(auth_session->has_user_secret_stash());
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK.
TEST_F(AuthSessionTest, AuthenticateAuthFactorExistingVKUserNoResave) {
// Setup AuthSession.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Test
// Calling AuthenticateAuthFactor.
EXPECT_EQ(AuthenticateAuthFactorVK(kFakeLabel, kFakePass, auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK and resaves it.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorExistingVKUserAndResaveForUpdate) {
// Setup AuthSession.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<ScryptAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Test
// 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::kScrypt));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_)).WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _))
.WillOnce(ReturnValue(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));
});
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));
});
// Calling AuthenticateAuthFactor.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// Test if AuthenticateAuthFactor authenticates existing credentials for a
// user with VK and resaves it.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorExistingVKUserAndResaveForResetSeed) {
// Setup AuthSession.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<ScryptAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Test
// 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::kScrypt));
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
EXPECT_CALL(keyset_management_, ShouldReSaveKeyset(_))
.WillOnce(Return(false));
EXPECT_CALL(keyset_management_, AddResetSeedIfMissing(_))
.WillOnce(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeForCreation(_, _, _))
.WillOnce(ReturnValue(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));
});
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));
});
// Calling AuthenticateAuthFactor.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// Test that AuthenticateAuthFactor doesn't add reset seed to LECredentials.
TEST_F(AuthSessionTest,
AuthenticateAuthFactorNotAddingResetSeedToPINVaultKeyset) {
// Setup AuthSession.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = FactorMapWithPin(kFakePinLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Test
// 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([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
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));
});
// Calling AuthenticateAuthFactor.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakePinLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_pin_input()->set_secret(kFakePin);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session.status(), 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.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = FactorMapWithPin(kFakePinLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Test
// Calling AuthenticateAuthFactor.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakePinLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePin);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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.status(),
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));
// Use this new auth_session_manager to make the AuthSession, need a to use
// |auth_block_utility_impl_|.
AuthSessionManager auth_session_manager_impl_{&crypto_,
&platform_,
&user_session_map_,
&keyset_management_,
&auth_block_utility_impl_,
&auth_factor_manager_,
&user_secret_stash_storage_};
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_impl_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Setting the expectation that the user does not exist.
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_FALSE(auth_session->user_exists());
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), 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(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce([](const ObfuscatedUsername&, const std::string&) {
return CreatePasswordVaultKeyset(kFakeLabel);
});
// Test.
TestFuture<CryptohomeStatus> add_future;
auth_session->AddAuthFactor(request, add_future.GetCallback());
// Verify.
EXPECT_THAT(add_future.Get(), IsOk());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Setting the expectation that the user does not exist.
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_FALSE(auth_session->user_exists());
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_EQ(auth_session->status(), 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(ReturnValue(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>());
});
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillRepeatedly([](const ObfuscatedUsername&, const std::string& label) {
return CreatePasswordVaultKeyset(label);
});
// 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(_, _, _, _, _, _, _, _));
// Test.
TestFuture<CryptohomeStatus> add_future2;
auth_session->AddAuthFactor(request2, add_future2.GetCallback());
// Verify.
ASSERT_THAT(add_future2.Get(), IsOk());
// There should be credential verifiers for both passwords.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(
user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass),
IsVerifierPtrWithLabelAndPassword(kFakeOtherLabel, kFakeOtherPass)));
// 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.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// Test that AddAuthFactor fails for an ephemeral user when PIN is added.
TEST_F(AuthSessionTest, AddPinFactorToEphemeralFails) {
// Setup.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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);
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
TEST_F(AuthSessionTest, AddSecondPasswordFactorToEphemeral) {
// Setup.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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(), IsOk());
// There should be two verifiers.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(
user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass),
IsVerifierPtrWithLabelAndPassword(kFakeOtherLabel, kFakeOtherPass)));
}
// UpdateAuthFactor request success when updating authenticated password VK.
TEST_F(AuthSessionTest, UpdateAuthFactorSucceedsForPasswordVK) {
// Setup.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
AuthBlockState auth_block_state = auth_session.auth_factor_map()
.Find(kFakeLabel)
->auth_factor()
.auth_block_state();
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), 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(ReturnValue(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));
});
EXPECT_CALL(keyset_management_, UpdateKeysetWithKeyBlobs(_, _, _, _, _, _));
// Set a valid |vault_keyset_| to update.
KeyData key_data;
key_data.set_label(kFakeLabel);
auto vk = std::make_unique<VaultKeyset>();
vk->Initialize(&platform_, &crypto_);
vk->SetKeyData(key_data);
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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// UpdateAuthFactor fails if label doesn't exist.
TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotMatchForVK) {
// Setup.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), 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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
// UpdateAuthFactor fails if label doesn't exist in the existing keysets.
TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotFoundForVK) {
// Setup.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map =
FactorMapWithPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired,
auth_session.status());
EXPECT_TRUE(auth_session.user_exists());
// Creating the user.
EXPECT_TRUE(auth_session.OnUserCreated().ok());
EXPECT_EQ(auth_session.status(), 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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
TEST_F(AuthSessionTest, ExtensionTest) {
// AuthSession must be constructed without using AuthSessionManager,
// because during cleanup the AuthSession must stay valid after
// timing out for verification.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.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_EQ(auth_session.status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
auth_session.SetAuthSessionAsAuthenticated(kAuthorizedIntentsForFullAuth);
ASSERT_TRUE(auth_session.timeout_timer_.IsRunning());
EXPECT_TRUE(auth_session.ExtendTimeoutTimer(kAuthSessionExtension).ok());
// Verify that timer has changed, within a resaonsable degree of error.
auto requested_delay = kAuthSessionTimeout + kAuthSessionExtension;
EXPECT_EQ(auth_session.timeout_timer_.GetCurrentDelay(), requested_delay);
auth_session.timeout_timer_.FireNow();
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusTimedOut);
EXPECT_THAT(auth_session.authorized_intents(), IsEmpty());
}
// Test that AuthenticateAuthFactor succeeds in the `AuthIntent::kWebAuthn`
// scenario.
TEST_F(AuthSessionTest, AuthenticateAuthFactorWebAuthnIntent) {
// 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, PrepareWebAuthnSecret(_, _));
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession with a fake factor.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kWebAuthn,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = FactorMapWithPassword<void>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
// 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>());
});
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// Test.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(
auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly,
AuthIntent::kWebAuthn));
}
// Test that AuthFactor map is updated after successful RemoveAuthFactor and
// not updated after unsuccessful RemoveAuthFactor.
TEST_F(AuthSessionTest, RemoveAuthFactorUpdatesAuthFactorMap) {
// Setup.
// Prepare the AuthFactor.
AuthBlockState auth_block_state;
auth_block_state.state = TpmBoundToPcrAuthBlockState();
AuthFactorMap auth_factor_map;
auth_factor_map.Add(
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata(), auth_block_state),
AuthFactorStorageType::kVaultKeyset);
auth_factor_map.Add(
std::make_unique<AuthFactor>(AuthFactorType::kPassword, kFakeOtherLabel,
AuthFactorMetadata(), auth_block_state),
AuthFactorStorageType::kVaultKeyset);
// Create AuthSession.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = std::move(auth_factor_map),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_EQ(auth_session.status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_TRUE(auth_session.user_exists());
EXPECT_EQ(AuthenticateAuthFactorVK(kFakeLabel, kFakePass, auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
// Test that RemoveAuthFactor success removes the factor from the map.
user_data_auth::RemoveAuthFactorRequest remove_request;
remove_request.set_auth_session_id(auth_session.serialized_token());
remove_request.set_auth_factor_label(kFakeOtherLabel);
// RemoveauthFactor loads the VK to remove.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeOtherLabel))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
TestFuture<CryptohomeStatus> remove_future;
auth_session.RemoveAuthFactor(remove_request, remove_future.GetCallback());
// Verify that AuthFactor is removed and the Authentication doesn't succeed
// with the removed factor.
ASSERT_THAT(remove_future.Get(), IsOk());
EXPECT_EQ(AuthenticateAuthFactorVK(kFakeOtherLabel, kFakePass, auth_session),
user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
// Test that RemoveAuthFactor failure doesn't remove the factor from the map.
user_data_auth::RemoveAuthFactorRequest remove_request2;
remove_request2.set_auth_session_id(auth_session.serialized_token());
remove_request2.set_auth_factor_label(kFakeLabel);
TestFuture<CryptohomeStatus> remove_future2;
auth_session.RemoveAuthFactor(remove_request2, remove_future2.GetCallback());
// Verify that AuthFactor is not removed and the Authentication doesn't
// succeed with the removed factor.
ASSERT_THAT(remove_future2.Get(), NotOk());
EXPECT_EQ(remove_future2.Get()->local_legacy_error().value(),
user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED);
EXPECT_EQ(AuthenticateAuthFactorVK(kFakeLabel, kFakePass, auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(auth_session.status(), AuthStatus::kAuthStatusAuthenticated);
}
// A variant of the auth session test that has the UserSecretStash experiment
// enabled.
class AuthSessionWithUssExperimentTest : public AuthSessionTest {
protected:
AuthSessionWithUssExperimentTest() {
SetUserSecretStashExperimentForTesting(/*enabled=*/true);
}
~AuthSessionWithUssExperimentTest() override {
// Reset this global variable to avoid affecting unrelated test cases.
SetUserSecretStashExperimentForTesting(/*enabled=*/std::nullopt);
}
struct ReplyToVerifyKey {
void operator()(const Username& account_id,
const structure::ChallengePublicKeyInfo& public_key_info,
std::unique_ptr<KeyChallengeService> key_challenge_service,
ChallengeCredentialsHelper::VerifyKeyCallback callback) {
if (is_key_valid) {
std::move(callback).Run(OkStatus<error::CryptohomeTPMError>());
} else {
const error::CryptohomeError::ErrorLocationPair
kErrorLocationPlaceholder =
error::CryptohomeError::ErrorLocationPair(
static_cast<
::cryptohome::error::CryptohomeError::ErrorLocation>(1),
"Testing1");
std::move(callback).Run(MakeStatus<error::CryptohomeTPMError>(
kErrorLocationPlaceholder,
error::ErrorActionSet({error::ErrorAction::kIncorrectAuth}),
hwsec::TPMRetryAction::kUserAuth));
}
}
bool is_key_valid = false;
};
user_data_auth::CryptohomeErrorCode AddRecoveryAuthFactor(
const std::string& label,
const std::string& secret,
AuthSession& auth_session) {
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, true, false))
.WillRepeatedly(ReturnValue(AuthBlockType::kCryptohomeRecovery));
EXPECT_CALL(auth_block_utility_,
CreateKeyBlobsWithAuthBlockAsync(
AuthBlockType::kCryptohomeRecovery, _, _))
.WillOnce([&secret](auto auth_block_type, auto auth_input,
auto create_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob(secret);
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));
});
// Prepare recovery add request.
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(label);
request.mutable_auth_factor()->mutable_cryptohome_recovery_metadata();
request.mutable_auth_input()
->mutable_cryptohome_recovery_input()
->set_mediator_pub_key("mediator pub key");
// Add recovery AuthFactor.
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(request, add_future.GetCallback());
if (add_future.Get().ok() ||
!add_future.Get()->local_legacy_error().has_value()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return add_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode AddPasswordAuthFactor(
const std::string& label,
const std::string& password,
bool first_factor,
AuthSession& auth_session) {
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false))
.WillRepeatedly(ReturnValue(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));
});
// Setting the expectation that a backup VaultKeyset will be created.
if (first_factor) {
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
}
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(
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() ||
!add_future.Get()->local_legacy_error().has_value()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return add_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode AuthenticateRecoveryAuthFactor(
const std::string& auth_factor_label,
const std::string& secret,
AuthSession& auth_session) {
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<CryptohomeRecoveryAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kCryptohomeRecovery));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kCryptohomeRecovery, _, _, _))
.WillOnce([&secret](auto auth_block_type, auto auth_input,
auto auth_state, auto derive_callback) {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob(secret);
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
});
// Prepare recovery authentication request.
std::string auth_factor_labels[] = {auth_factor_label};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_cryptohome_recovery_input()
->mutable_recovery_response();
TestFuture<CryptohomeStatus> authenticate_future;
// Authenticate using recovery.
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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 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));
});
// Setting the expectation that backup password VaultKeyset is decrypted.
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(password);
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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))
.WillRepeatedly(ReturnValue(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));
});
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() ||
!update_future.Get()->local_legacy_error().has_value()) {
return user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
}
return update_future.Get()->local_legacy_error().value();
}
user_data_auth::CryptohomeErrorCode AddPinAuthFactor(
bool backup_keyset_enabled,
const std::string& pin,
AuthSession& auth_session) {
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(true, false, false))
.WillRepeatedly(ReturnValue(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));
});
// Setting the expectation that a backup VaultKeyset will be created if it
// is not explicitly disabled by adding a USS-only factor.
if (backup_keyset_enabled) {
EXPECT_CALL(keyset_management_,
AddKeysetWithKeyBlobs(_, _, _, _, _, _, _, _));
}
// 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() ||
!add_future.Get()->local_legacy_error().has_value()) {
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Test.
EXPECT_FALSE(auth_session->has_user_secret_stash());
EXPECT_TRUE(auth_session->OnUserCreated().ok());
// Verify.
EXPECT_TRUE(auth_session->has_user_secret_stash());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Test.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
// Verify.
EXPECT_FALSE(auth_session->has_user_secret_stash());
}
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
// Test.
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false))
.WillRepeatedly(ReturnValue(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));
});
// Setting the expectation that a backup VaultKeyset will be created.
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
// 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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword)));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
}
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
// Test.
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false))
.WillRepeatedly(ReturnValue(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)));
});
// Setting the expectation that a backup VaultKeyset will be created.
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
// 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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword)));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
}
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
// 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))
.WillRepeatedly(ReturnValue(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));
});
// 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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
// 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))
.WillRepeatedly(ReturnValue(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));
});
// Setting the expectation that a backup VaultKeyset will be created.
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
// 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());
std::unique_ptr<VaultKeyset> backup_vk = CreateBackupVaultKeyset(kFakeLabel);
auth_session->set_vault_keyset_for_testing(std::move(backup_vk));
// 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))
.WillRepeatedly(ReturnValue(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));
});
// Setting the expectation that a backup VaultKeyset will be created.
EXPECT_CALL(keyset_management_,
AddKeysetWithKeyBlobs(_, _, _, _, _, _, _, _));
// 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)));
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// 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 ObfuscatedUsername 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());
// Generating the backup VK.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce([](const ObfuscatedUsername&, const std::string& label) {
KeyData key_data;
key_data.set_label(label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// 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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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));
});
// Setting the expectation that backup password VaultKeyset is decrypted.
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// Calling AuthenticateAuthFactor.
TestFuture<CryptohomeStatus> authenticate_future;
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session->has_user_secret_stash());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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 ObfuscatedUsername 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());
// Generating the backup VK.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce([](const ObfuscatedUsername&, const std::string& label) {
KeyData key_data;
key_data.set_label(label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// 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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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)));
});
// Setting the expectation that backup password VaultKeyset is decrypted.
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([](const ObfuscatedUsername&, KeyBlobs,
const std::optional<std::string>& label) {
KeyData key_data;
key_data.set_label(*label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
// Calling AuthenticateAuthFactor.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session->has_user_secret_stash());
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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 ObfuscatedUsername 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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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));
});
// Calling AuthenticateAuthFactor.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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);
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
EXPECT_FALSE(auth_session->has_user_secret_stash());
}
// 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 ObfuscatedUsername 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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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));
});
// Calling AuthenticateAuthFactor.
std::string auth_factor_labels[] = {kFakePinLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session->has_user_secret_stash());
}
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
// Setting the expectation that the auth block utility will create key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, true, false))
.WillRepeatedly(ReturnValue(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));
});
// 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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
TEST_F(AuthSessionWithUssExperimentTest,
AuthenticateCryptohomeRecoveryAuthFactor) {
// Setup.
const ObfuscatedUsername 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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
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 ObfuscatedUsername& 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_FALSE(auth_session->has_user_secret_stash());
// Calling GetRecoveryRequest.
user_data_auth::GetRecoveryRequestRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kFakeLabel);
TestFuture<user_data_auth::GetRecoveryRequestReply> reply_future;
auth_session->GetRecoveryRequest(
request,
reply_future
.GetCallback<const user_data_auth::GetRecoveryRequestReply&>());
// Verify.
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session->authorized_intents(), IsEmpty());
// 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) {
EXPECT_THAT(
auth_input.cryptohome_recovery_auth_input->ephemeral_pub_key,
Optional(brillo::SecureBlob("test")));
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = kFakePerCredentialSecret;
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs));
});
// Calling AuthenticateAuthFactor.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_cryptohome_recovery_input()
->mutable_recovery_response();
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session->has_user_secret_stash());
// There should be no verifier created for the recovery factor.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(), IsEmpty());
}
// Test scenario where we add a Smart Card/Challenge Response credential,
// and go through the authentication flow twice. On the second authentication,
// AuthSession should use the lightweight verify check.
TEST_F(AuthSessionWithUssExperimentTest, AuthenticateSmartCardAuthFactor) {
// Setup.
brillo::Blob public_key_spki_der = brillo::BlobFromString("public_key");
const ObfuscatedUsername 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::kSmartCard, kFakeLabel,
AuthFactorMetadata{
.metadata = SmartCardAuthFactorMetadata{.public_key_spki_der =
public_key_spki_der}},
AuthBlockState{.state = ChallengeCredentialAuthBlockState()});
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;
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_TRUE(auth_session->user_exists());
EXPECT_FALSE(auth_session->has_user_secret_stash());
// Verify.
EXPECT_EQ(auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(auth_session->authorized_intents(), IsEmpty());
// Test.
// Setting the expectation that the auth block utility will derive key blobs.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeFromState(
AuthBlockStateTypeIs<ChallengeCredentialAuthBlockState>()))
.WillRepeatedly(Return(AuthBlockType::kChallengeCredential));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(
AuthBlockType::kChallengeCredential, _, _, _))
.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));
});
EXPECT_CALL(auth_block_utility_, CreateCredentialVerifier(_, kFakeLabel, _))
.WillOnce(Return(ByMove(SmartCardVerifier::Create(
kFakeLabel, public_key_spki_der, &challenge_credentials_helper_,
&key_challenge_service_factory_))));
// Calling AuthenticateAuthFactor.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_smart_card_input()->add_signature_algorithms(
user_data_auth::CHALLENGE_RSASSA_PKCS1_V1_5_SHA256);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_EQ(auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(auth_session->has_user_secret_stash());
// There should be a verifier created for the smart card factor.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(IsVerifierPtrWithLabel(kFakeLabel)));
CryptohomeStatusOr<InUseAuthSession> verify_auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(verify_auth_session_status.ok());
AuthSession* verify_auth_session = verify_auth_session_status.value().Get();
// Expect that next authentication will go through lightweight
// verification.
EXPECT_CALL(auth_block_utility_,
IsVerifyWithAuthFactorSupported(AuthIntent::kVerifyOnly,
AuthFactorType::kSmartCard))
.WillRepeatedly(Return(true));
// Simulate a successful key verification.
EXPECT_CALL(challenge_credentials_helper_, VerifyKey(_, _, _, _))
.WillOnce(ReplyToVerifyKey{/*is_key_valid=*/true});
// Call AuthenticateAuthFactor again.
TestFuture<CryptohomeStatus> verify_authenticate_future;
verify_auth_session->AuthenticateAuthFactor(
auth_factor_labels, auth_input_proto,
verify_authenticate_future.GetCallback());
EXPECT_THAT(verify_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor succeeds for the `AuthIntent::kVerifyOnly`
// scenario, using a credential verifier.
TEST_F(AuthSessionWithUssExperimentTest, LightweightPasswordAuthentication) {
// Setup.
// Add the user session along with a verifier that's configured to pass.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, VerifyUser(SanitizeUserName(kFakeUsername)))
.WillOnce(Return(true));
auto verifier = std::make_unique<MockCredentialVerifier>(
AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()});
EXPECT_CALL(*verifier, VerifySync(_)).WillOnce(ReturnOk<CryptohomeError>());
user_session->AddCredentialVerifier(std::move(verifier));
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(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kVerifyOnly,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = FactorMapWithPassword<void>(kFakeLabel),
.migrate_to_user_secret_stash = false},
backing_apis_);
EXPECT_CALL(auth_block_utility_,
IsVerifyWithAuthFactorSupported(AuthIntent::kVerifyOnly,
AuthFactorType::kPassword))
.WillRepeatedly(Return(true));
// Test.
std::string auth_factor_labels[] = {kFakeLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakePass);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(auth_session.authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor succeeds for the `AuthIntent::kVerifyOnly`
// scenario, using the legacy fingerprint.
TEST_F(AuthSessionWithUssExperimentTest, LightweightFingerprintAuthentication) {
// 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, VerifyUser(SanitizeUserName(kFakeUsername)))
.WillOnce(Return(true));
auto verifier = std::make_unique<MockCredentialVerifier>(
AuthFactorType::kLegacyFingerprint, "", AuthFactorMetadata{});
EXPECT_CALL(*verifier, VerifySync(_)).WillOnce(ReturnOk<CryptohomeError>());
user_session->AddCredentialVerifier(std::move(verifier));
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession with no factors. No authentication mocks are set
// up, because the lightweight authentication should be used in the test.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_CALL(auth_block_utility_,
IsVerifyWithAuthFactorSupported(
AuthIntent::kVerifyOnly, AuthFactorType::kLegacyFingerprint))
.WillRepeatedly(Return(true));
// Test.
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_legacy_fingerprint_input();
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor({}, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly));
}
// Test that PrepareAuthFactor succeeds for the legacy fingerprint with the
// purpose of authentication.
TEST_F(AuthSessionWithUssExperimentTest, PrepareLegacyFingerprintAuth) {
// Add the user session. Configure the credential verifier mock to succeed.
auto user_session = std::make_unique<MockUserSession>();
// Create an AuthSession and add a mock for a successful auth block prepare.
auto auth_session = std::make_unique<AuthSession>(
AuthSession::Params{
.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kVerifyOnly,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = AuthFactorMap(),
.migrate_to_user_secret_stash = false},
backing_apis_);
TrackedPreparedAuthFactorToken::WasCalled token_was_called;
auto token = std::make_unique<TrackedPreparedAuthFactorToken>(
AuthFactorType::kLegacyFingerprint, OkStatus<CryptohomeError>(),
&token_was_called);
EXPECT_CALL(auth_block_utility_,
IsPrepareAuthFactorRequired(AuthFactorType::kLegacyFingerprint))
.WillOnce(Return(true));
EXPECT_CALL(
auth_block_utility_,
PrepareAuthFactorForAuth(AuthFactorType::kLegacyFingerprint, _, _))
.WillOnce([&](AuthFactorType, const ObfuscatedUsername&,
PreparedAuthFactorToken::Consumer callback) {
std::move(callback).Run(std::move(token));
});
// Test.
TestFuture<CryptohomeStatus> prepare_future;
user_data_auth::PrepareAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT);
request.set_purpose(user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
auth_session->PrepareAuthFactor(request, prepare_future.GetCallback());
auth_session.reset();
// Verify.
ASSERT_THAT(prepare_future.Get(), IsOk());
EXPECT_TRUE(token_was_called.terminate);
EXPECT_TRUE(token_was_called.destructor);
}
// Test that PrepareAuthFactor succeeded for password.
TEST_F(AuthSessionWithUssExperimentTest, PreparePasswordFailure) {
// 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>();
// Create an AuthSession
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_CALL(auth_block_utility_,
IsPrepareAuthFactorRequired(AuthFactorType::kPassword))
.WillOnce(Return(false));
// Test.
user_data_auth::PrepareAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request.set_purpose(user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
TestFuture<CryptohomeStatus> prepare_future;
auth_session->PrepareAuthFactor(request, prepare_future.GetCallback());
// Verify.
ASSERT_EQ(prepare_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(AuthSessionWithUssExperimentTest, TerminateAuthFactorBadTypeFailure) {
// 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>();
// Create an AuthSession
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_CALL(auth_block_utility_,
IsPrepareAuthFactorRequired(AuthFactorType::kPassword))
.WillOnce(Return(false));
// Test.
user_data_auth::TerminateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
TestFuture<CryptohomeStatus> terminate_future;
auth_session->TerminateAuthFactor(request, terminate_future.GetCallback());
// Verify.
ASSERT_EQ(terminate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(AuthSessionWithUssExperimentTest,
TerminateAuthFactorInactiveFactorFailure) {
// 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>();
// Create an AuthSession
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_CALL(auth_block_utility_,
IsPrepareAuthFactorRequired(AuthFactorType::kLegacyFingerprint))
.WillOnce(Return(true));
// Test.
user_data_auth::TerminateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT);
TestFuture<CryptohomeStatus> terminate_future;
auth_session->TerminateAuthFactor(request, terminate_future.GetCallback());
// Verify.
ASSERT_EQ(terminate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(AuthSessionWithUssExperimentTest,
TerminateAuthFactorLegacyFingerprintSuccess) {
// 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>();
// Create an AuthSession
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kVerifyOnly);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
TrackedPreparedAuthFactorToken::WasCalled token_was_called;
auto token = std::make_unique<TrackedPreparedAuthFactorToken>(
AuthFactorType::kLegacyFingerprint, OkStatus<CryptohomeError>(),
&token_was_called);
EXPECT_CALL(auth_block_utility_,
IsPrepareAuthFactorRequired(AuthFactorType::kLegacyFingerprint))
.WillRepeatedly(Return(true));
EXPECT_CALL(
auth_block_utility_,
PrepareAuthFactorForAuth(AuthFactorType::kLegacyFingerprint, _, _))
.WillOnce([&](AuthFactorType, const ObfuscatedUsername&,
PreparedAuthFactorToken::Consumer callback) {
std::move(callback).Run(std::move(token));
});
TestFuture<CryptohomeStatus> prepare_future;
user_data_auth::PrepareAuthFactorRequest prepare_request;
prepare_request.set_auth_session_id(auth_session->serialized_token());
prepare_request.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT);
prepare_request.set_purpose(user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
auth_session->PrepareAuthFactor(prepare_request,
prepare_future.GetCallback());
ASSERT_THAT(prepare_future.Get(), IsOk());
// Test.
user_data_auth::TerminateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT);
TestFuture<CryptohomeStatus> terminate_future;
auth_session->TerminateAuthFactor(request, terminate_future.GetCallback());
// Verify.
ASSERT_THAT(terminate_future.Get(), IsOk());
EXPECT_TRUE(token_was_called.terminate);
EXPECT_TRUE(token_was_called.destructor);
}
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::unique_ptr<VaultKeyset> backup_vk = CreateBackupVaultKeyset(kFakeLabel);
auth_session->set_vault_keyset_for_testing(std::move(backup_vk));
error =
AddPinAuthFactor(/*backup_keyset_enabled=*/true, 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_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakePinLabel), Optional(_));
// Setting the expectation that backup VaultKeyset is also removed.
// VaultKeyset is loaded to be removed.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
// 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_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakePinLabel),
Eq(std::nullopt));
// 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.
std::string auth_factor_labels[] = {kFakePinLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_pin_input()->set_secret(kFakePin);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
TEST_F(AuthSessionWithUssExperimentTest,
RemoveAuthFactorRemovesCredentialVerifier) {
// 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
error = AddPasswordAuthFactor(kFakeOtherLabel, kFakeOtherPass,
/*first_factor=*/false, *auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Both passwords are available, the first one should supply a verifier.
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword),
Pair(kFakeOtherLabel, AuthFactorType::kPassword)));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakeOtherLabel),
Optional(_));
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(
user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass),
IsVerifierPtrWithLabelAndPassword(kFakeOtherLabel, kFakeOtherPass)));
// Setting the expectation that backup VaultKeyset is also removed.
// VaultKeyset is loaded to be removed.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
// Test.
// Calling RemoveAuthFactor for the second password.
user_data_auth::RemoveAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kFakeOtherLabel);
TestFuture<CryptohomeStatus> remove_future;
auth_session->RemoveAuthFactor(request, remove_future.GetCallback());
EXPECT_THAT(remove_future.Get(), IsOk());
// Only the first 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_THAT(auth_session->auth_factor_map().Find(kFakeLabel), Optional(_));
EXPECT_THAT(auth_session->auth_factor_map().Find(kFakeOtherLabel),
Eq(std::nullopt));
// Calling AuthenticateAuthFactor for the first password succeeds.
error = AuthenticatePasswordAuthFactor(kFakePass, *auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Calling AuthenticateAuthFactor for the second password fails.
std::string auth_factor_labels[] = {kFakeOtherLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_password_input()->set_secret(kFakeOtherPass);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
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);
// Now only the first password verifier is available.
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::unique_ptr<VaultKeyset> backup_vk = CreateBackupVaultKeyset(kFakeLabel);
auth_session->set_vault_keyset_for_testing(std::move(backup_vk));
error =
AddPinAuthFactor(/*backup_keyset_enabled=*/true, kFakePin, *auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Setting the expectation that backup VaultKeyset is also removed.
// VaultKeyset is loaded to be removed.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
// 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(/*backup_keyset_enabled=*/true, kFakePin, *auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// The verifier still uses the original password.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// 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;
std::string new_pass = "update fake pass";
{
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_))
.WillRepeatedly(Return(false));
// Setting the expectation that the user does not exist.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
// Calling UpdateAuthFactor.
error = UpdatePasswordAuthFactor(new_pass, *auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Force the creation of the user session, otherwise any verifiers added
// will be destroyed when the session is.
FindOrCreateUserSession(kFakeUsername);
}
// Setting the expectation that the user exists.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Generating the backup VK.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.WillOnce([](const ObfuscatedUsername&, const std::string& label) {
KeyData key_data;
key_data.set_label(label);
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(std::move(key_data));
return vk;
});
CryptohomeStatusOr<InUseAuthSession> new_auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(new_auth_session_status.ok());
AuthSession* new_auth_session = new_auth_session_status.value().Get();
EXPECT_EQ(new_auth_session->status(),
AuthStatus::kAuthStatusFurtherFactorRequired);
EXPECT_THAT(new_auth_session->authorized_intents(), IsEmpty());
// Verify.
// The credential verifier uses the new password.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, new_pass)));
// AuthenticateAuthFactor should succeed using the new password.
user_data_auth::CryptohomeErrorCode error =
AuthenticatePasswordAuthFactor(new_pass, *new_auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(new_auth_session->status(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
new_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
// Test that AddauthFactor successfully adds a PIN factor on a
// session that was authenticated via a recovery factor.
TEST_F(AuthSessionWithUssExperimentTest, AddPinAfterRecoveryAuth) {
// Setup.
// Initially the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
{
// Obtain AuthSession for user setup.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(auth_session_status, IsOk());
AuthSession* auth_session = auth_session_status.value().Get();
// Create the user with password and recovery factors.
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
EXPECT_EQ(AddPasswordAuthFactor(kFakeLabel, kFakePass,
/*first_factor=*/true, *auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_EQ(AddRecoveryAuthFactor(kRecoveryLabel, kFakeRecoverySecret,
*auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_FALSE(auth_session->enable_create_backup_vk_with_uss_for_testing());
}
// Set up mocks for the now-existing user.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Obtain AuthSession for authentication.
CryptohomeStatusOr<InUseAuthSession> new_auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(new_auth_session_status, IsOk());
AuthSession* new_auth_session = new_auth_session_status.value().Get();
EXPECT_FALSE(
new_auth_session->enable_create_backup_vk_with_uss_for_testing());
// Authenticate the new auth session with recovery factor.
EXPECT_EQ(AuthenticateRecoveryAuthFactor(kRecoveryLabel, kFakeRecoverySecret,
*new_auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(
new_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(new_auth_session->has_user_secret_stash());
// Test adding a PIN AuthFactor.
user_data_auth::CryptohomeErrorCode error = AddPinAuthFactor(
/*backup_keyset_enabled=*/false, kFakePin, *new_auth_session);
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Verify PIN factor is added.
std::map<std::string, AuthFactorType> stored_factors =
auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername));
EXPECT_THAT(stored_factors,
UnorderedElementsAre(
Pair(kFakeLabel, AuthFactorType::kPassword),
Pair(kRecoveryLabel, AuthFactorType::kCryptohomeRecovery),
Pair(kFakePinLabel, AuthFactorType::kPin)));
// Verify that reset secret for the pin label is added to USS.
EXPECT_TRUE(new_auth_session->HasResetSecretInUssForTesting(kFakePinLabel));
}
// Test that UpdateAuthFactor successfully updates a password factor on a
// session that was authenticated via a recovery factor.
TEST_F(AuthSessionWithUssExperimentTest, UpdatePasswordAfterRecoveryAuth) {
// Setup.
constexpr char kNewFakePass[] = "new fake pass";
// Initially the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
{
// Obtain AuthSession for user setup.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(auth_session_status, IsOk());
AuthSession* auth_session = auth_session_status.value().Get();
// Create the user.
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
// Add password AuthFactor.
EXPECT_EQ(AddPasswordAuthFactor(kFakeLabel, kFakePass,
/*first_factor=*/true, *auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Add recovery AuthFactor.
EXPECT_EQ(AddRecoveryAuthFactor(kRecoveryLabel, kFakeRecoverySecret,
*auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_FALSE(auth_session->enable_create_backup_vk_with_uss_for_testing());
}
// Set up mocks for the now-existing user.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true));
// Obtain AuthSession for authentication.
AuthSession* new_auth_session = nullptr;
{
CryptohomeStatusOr<InUseAuthSession> new_auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
ASSERT_THAT(new_auth_session_status, IsOk());
new_auth_session = new_auth_session_status.value().Get();
EXPECT_FALSE(
new_auth_session->enable_create_backup_vk_with_uss_for_testing());
}
// Authenticate the new auth session with recovery factor.
EXPECT_EQ(AuthenticateRecoveryAuthFactor(kRecoveryLabel, kFakeRecoverySecret,
*new_auth_session),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(
new_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
EXPECT_TRUE(new_auth_session->has_user_secret_stash());
EXPECT_THAT(
new_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Test updating existing password factor.
user_data_auth::CryptohomeErrorCode error =
UpdatePasswordAuthFactor(kNewFakePass, *new_auth_session);
// Verify update succeeded.
EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
}
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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, 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));
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(kFakeUsername, flags,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
// Creating the user.
EXPECT_TRUE(auth_session->OnUserCreated().ok());
EXPECT_TRUE(auth_session->has_user_secret_stash());
user_data_auth::CryptohomeErrorCode error =
user_data_auth::CRYPTOHOME_ERROR_NOT_SET;
// Calling AddAuthFactor.
error = AddPasswordAuthFactor(kFakeLabel, kFakePass, /*first_factor=*/true,
*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.
UserSession* user_session = FindOrCreateUserSession(kFakeUsername);
EXPECT_THAT(user_session->GetCredentialVerifiers(),
UnorderedElementsAre(
IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass)));
}
// Test that `UpdateAuthFactor` fails when the auth block derivation fails (but
// doesn't crash).
TEST_F(AuthSessionTest, UpdateAuthFactorFailsInAuthBlock) {
// Setup.
// Setting the expectation that the user does not exist.
EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false));
// Setting the expectation that the user does not exist.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt);
EXPECT_TRUE(auth_session_status.ok());
AuthSession& auth_session = *auth_session_status.value().Get();
// Creating the user.
EXPECT_THAT(auth_session.OnUserCreated(), IsOk());
// Adding the password VK.
EXPECT_CALL(auth_block_utility_,
GetAuthBlockTypeForCreation(false, false, false))
.WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr));
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([](auto, auto, AuthBlock::CreateCallback create_callback) {
// Make an arbitrary auth block state type can be used in this test.
auto key_blobs = std::make_unique<KeyBlobs>();
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));
})
.RetiresOnSaturation();
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _, _))
.WillOnce(
[](auto, auto, const KeyData& key_data, auto, auto, auto, auto) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetKeyData(key_data);
return vk;
});
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kFakeLabel))
.WillOnce(
[](auto, auto) { return CreatePasswordVaultKeyset(kFakeLabel); });
user_data_auth::AddAuthFactorRequest add_request;
add_request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
add_request.mutable_auth_factor()->set_label(kFakeLabel);
add_request.mutable_auth_factor()->mutable_password_metadata();
add_request.mutable_auth_input()->mutable_password_input()->set_secret(
kFakePass);
add_request.set_auth_session_id(auth_session.serialized_token());
TestFuture<CryptohomeStatus> add_future;
auth_session.AddAuthFactor(add_request, add_future.GetCallback());
EXPECT_THAT(add_future.Get(), IsOk());
// Setting the expectations for the new auth block creation. The mock is set
// to fail.
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([](auto, auto, AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(MakeStatus<CryptohomeCryptoError>(
kErrorLocationForTestingAuthSession,
error::ErrorActionSet(
{error::ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
});
// Test.
// Preparing UpdateAuthFactor parameters.
user_data_auth::UpdateAuthFactorRequest update_request;
update_request.set_auth_session_id(auth_session.serialized_token());
update_request.set_auth_factor_label(kFakeLabel);
update_request.mutable_auth_factor()->set_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
update_request.mutable_auth_factor()->set_label(kFakeLabel);
update_request.mutable_auth_factor()->mutable_password_metadata();
update_request.mutable_auth_input()->mutable_password_input()->set_secret(
kFakePass);
// Calling UpdateAuthFactor.
TestFuture<CryptohomeStatus> update_future;
auth_session.UpdateAuthFactor(update_request, update_future.GetCallback());
// Verify.
EXPECT_THAT(update_future.Get(), NotOk());
}
// Test that AuthenticateAuthFactor succeeds for the `AuthIntent::kWebAuthn`
// scenario, using the legacy fingerprint.
TEST_F(AuthSessionWithUssExperimentTest, FingerprintAuthenticationForWebAuthn) {
// 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, VerifyUser(SanitizeUserName(kFakeUsername)))
.WillOnce(Return(true));
auto verifier = std::make_unique<MockCredentialVerifier>(
AuthFactorType::kLegacyFingerprint, "", AuthFactorMetadata{});
EXPECT_CALL(*verifier, VerifySync(_)).WillOnce(ReturnOk<CryptohomeError>());
user_session->AddCredentialVerifier(std::move(verifier));
EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session)));
// Create an AuthSession and add a mock for a successful auth block verify.
CryptohomeStatusOr<InUseAuthSession> auth_session_status =
auth_session_manager_.CreateAuthSession(
kFakeUsername, user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kWebAuthn);
EXPECT_TRUE(auth_session_status.ok());
AuthSession* auth_session = auth_session_status.value().Get();
EXPECT_CALL(auth_block_utility_,
IsVerifyWithAuthFactorSupported(
AuthIntent::kWebAuthn, AuthFactorType::kLegacyFingerprint))
.WillRepeatedly(Return(true));
// Test.
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_legacy_fingerprint_input();
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->AuthenticateAuthFactor({}, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly, AuthIntent::kWebAuthn));
}
// Test that we can authenticate a old-style kiosk VK, and migrate it to USS
// correctly. These old VKs show up as password VKs and so we need the
// authenticate to successfully convert it to a kiosk based on the input.
TEST_F(AuthSessionWithUssExperimentTest, AuthenticatePasswordVkToKioskUss) {
// Setup.
// Create a factor containing a password that will become a kiosk factor.
AuthFactorMap auth_factor_map;
auth_factor_map.Add(
std::make_unique<AuthFactor>(
AuthFactorType::kPassword, kLegacyLabel,
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()},
AuthBlockState()),
AuthFactorStorageType::kVaultKeyset);
// Start a session with this single factor and USS migration enabled.
AuthSession auth_session(
{.username = kFakeUsername,
.obfuscated_username = SanitizeUserName(kFakeUsername),
.is_ephemeral_user = false,
.intent = AuthIntent::kDecrypt,
.on_timeout = base::DoNothing(),
.user_exists = true,
.auth_factor_map = std::move(auth_factor_map),
.migrate_to_user_secret_stash = true},
backing_apis_);
// Helpers to make keysets and keyblobs in the test.
auto make_vk = [this]() {
auto vk = std::make_unique<VaultKeyset>();
vk->Initialize(backing_apis_.platform, backing_apis_.crypto);
vk->SetLegacyIndex(0);
return vk;
};
auto make_key_blobs = []() {
auto key_blobs = std::make_unique<KeyBlobs>();
key_blobs->vkk_key = brillo::SecureBlob(32, 'J');
return key_blobs;
};
// Called within the converter_.PopulateKeyDataForVK(). We return an empty VK
// with no KeyData, like a legacy kiosk VK would have. We also have to fake
// out the actual authentication calls. Since the point here is to test the
// migration, not the authentication itself, we just respond with "yes, all
// good" everywhere.
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, kLegacyLabel))
.WillRepeatedly([&](auto...) { return make_vk(); });
EXPECT_CALL(auth_block_utility_,
GetAuthBlockStateFromVaultKeyset(kLegacyLabel, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_))
.WillRepeatedly(Return(AuthBlockType::kScrypt));
EXPECT_CALL(auth_block_utility_,
DeriveKeyBlobsWithAuthBlockAsync(AuthBlockType::kScrypt, _, _, _))
.WillOnce([&](AuthBlockType auth_block_type, const AuthInput& auth_input,
const AuthBlockState& auth_state,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(OkStatus<CryptohomeCryptoError>(), make_key_blobs());
});
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce([&](auto...) { return make_vk(); });
// These calls will happen during the migration.
EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([&](AuthBlockType auth_block_type, const AuthInput& auth_input,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(), make_key_blobs(),
std::make_unique<AuthBlockState>());
});
// Test.
std::string auth_factor_labels[] = {kLegacyLabel};
user_data_auth::AuthInput auth_input_proto;
auth_input_proto.mutable_kiosk_input();
TestFuture<CryptohomeStatus> authenticate_future;
auth_session.AuthenticateAuthFactor(auth_factor_labels, auth_input_proto,
authenticate_future.GetCallback());
// Verify.
EXPECT_THAT(authenticate_future.Get(), IsOk());
ASSERT_THAT(auth_session.auth_factor_map().size(), Eq(1));
AuthFactorMap::ValueView stored_auth_factor =
*auth_session.auth_factor_map().begin();
const AuthFactor& auth_factor = stored_auth_factor.auth_factor();
EXPECT_THAT(stored_auth_factor.storage_type(),
Eq(AuthFactorStorageType::kUserSecretStash));
EXPECT_THAT(auth_factor.type(), Eq(AuthFactorType::kKiosk));
EXPECT_THAT(auth_factor.metadata().metadata,
VariantWith<KioskAuthFactorMetadata>(_));
}
} // namespace cryptohome