blob: 5cc8b51fc982da723652d0eb5eae1e28d9f9b251 [file] [log] [blame]
// Copyright 2023 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`. Unlike auth_session_unittest.cc, uses TPM
// simulator and minimal mocking in order to be able to verify inter-class
// interactions.
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include <base/files/scoped_temp_dir.h>
#include <base/functional/callback_helpers.h>
#include <base/memory/scoped_refptr.h>
#include <base/strings/stringprintf.h>
#include <base/test/bind.h>
#include <base/test/task_environment.h>
#include <base/test/test_future.h>
#include <brillo/secure_blob.h>
#include <cryptohome/cryptorecovery/cryptorecovery.pb.h>
#include <cryptohome/proto_bindings/auth_factor.pb.h>
#include <cryptohome/proto_bindings/UserDataAuth.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/backend/mock_backend.h>
#include <libhwsec/factory/tpm2_simulator_factory_for_test.h>
#include <libhwsec/frontend/cryptohome/frontend.h>
#include <libhwsec/frontend/pinweaver/frontend.h>
#include <libhwsec/frontend/recovery_crypto/frontend.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "cryptohome/auth_blocks/auth_block_utility_impl.h"
#include "cryptohome/auth_blocks/fp_service.h"
#include "cryptohome/auth_factor/auth_factor_manager.h"
#include "cryptohome/auth_factor/auth_factor_storage_type.h"
#include "cryptohome/auth_factor/flatbuffer.h"
#include "cryptohome/auth_factor/types/manager.h"
#include "cryptohome/auth_input_utils.h"
#include "cryptohome/auth_intent.h"
#include "cryptohome/auth_session.h"
#include "cryptohome/crypto.h"
#include "cryptohome/cryptohome_keys_manager.h"
#include "cryptohome/cryptorecovery/fake_recovery_mediator_crypto.h"
#include "cryptohome/error/action.h"
#include "cryptohome/error/cryptohome_error.h"
#include "cryptohome/fake_features.h"
#include "cryptohome/fake_platform.h"
#include "cryptohome/features.h"
#include "cryptohome/filesystem_layout.h"
#include "cryptohome/pinweaver_manager/le_credential_manager_impl.h"
#include "cryptohome/user_secret_stash/storage.h"
#include "cryptohome/user_secret_stash/user_secret_stash.h"
#include "cryptohome/user_session/user_session_map.h"
#include "cryptohome/vault_keyset.h"
#include "cryptohome/vault_keyset_factory.h"
namespace cryptohome {
namespace {
using ::base::test::TestFuture;
using ::cryptohome::cryptorecovery::FakeRecoveryMediatorCrypto;
using ::cryptohome::error::CryptohomeError;
using ::cryptohome::error::ErrorActionSet;
using ::hwsec_foundation::error::testing::IsOk;
using ::hwsec_foundation::error::testing::NotOk;
using ::hwsec_foundation::error::testing::ReturnValue;
using ::hwsec_foundation::status::MakeStatus;
using ::testing::Combine;
using ::testing::NiceMock;
using ::testing::ValuesIn;
constexpr int kPinResetCounter = 6;
constexpr char kPasswordLabel[] = "fake-password-label";
constexpr char kPassword[] = "fake-password";
constexpr char kNewPassword[] = "fake-new-password";
constexpr char kPinLabel[] = "fake-pin-label";
constexpr char kPin[] = "1234";
constexpr char kNewPin[] = "1111";
constexpr char kRecoveryLabel[] = "fake-recovery-label";
constexpr char kUserGaiaId[] = "fake-gaia-id";
constexpr char kDeviceUserId[] = "fake-device-user-id";
// 01 Jan 2020 13:00:41 GMT+0000.
constexpr base::Time kFakeTimestamp = base::Time::FromTimeT(1577883641);
CryptohomeStatus MakeFakeCryptohomeError() {
CryptohomeError::ErrorLocationPair fake_error_location(
static_cast<CryptohomeError::ErrorLocation>(1), "FakeErrorLocation");
return MakeStatus<CryptohomeError>(fake_error_location, ErrorActionSet());
}
CryptohomeStatus RunAddAuthFactor(
const user_data_auth::AddAuthFactorRequest& request,
AuthSession& auth_session) {
TestFuture<CryptohomeStatus> future;
auth_session.GetAuthForDecrypt()->AddAuthFactor(request,
future.GetCallback());
return future.Take();
}
CryptohomeStatus RunAuthenticateAuthFactor(
const user_data_auth::AuthenticateAuthFactorRequest& request,
AuthSession& auth_session) {
// Convert |auth_factor_label| or |auth_factor_labels| into an array.
std::vector<std::string> auth_factor_labels;
if (!request.auth_factor_label().empty()) {
auth_factor_labels.push_back(request.auth_factor_label());
} else {
for (auto label : request.auth_factor_labels()) {
auth_factor_labels.push_back(label);
}
}
TestFuture<const AuthSession::PostAuthAction&, CryptohomeStatus> future;
AuthSession::AuthenticateAuthFactorRequest auth_request{
.auth_factor_labels = std::move(auth_factor_labels),
.auth_input_proto = request.auth_input(),
.flags = {.force_full_auth = AuthSession::ForceFullAuthFlag::kNone},
};
SerializedUserAuthFactorTypePolicy auth_factor_type_policy(
{.type = *SerializeAuthFactorType(
*DetermineFactorTypeFromAuthInput(request.auth_input())),
.enabled_intents = {},
.disabled_intents = {}});
auth_session.AuthenticateAuthFactor(auth_request, auth_factor_type_policy,
future.GetCallback());
return std::get<1>(future.Take());
}
CryptohomeStatus RunUpdateAuthFactor(
const user_data_auth::UpdateAuthFactorRequest& request,
AuthSession& auth_session) {
TestFuture<CryptohomeStatus> future;
auth_session.GetAuthForDecrypt()->UpdateAuthFactor(request,
future.GetCallback());
return future.Take();
}
CryptohomeStatus AuthenticatePasswordFactor(const std::string& label,
const std::string& password,
AuthSession& auth_session) {
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(label);
request.mutable_auth_input()->mutable_password_input()->set_secret(password);
return RunAuthenticateAuthFactor(request, auth_session);
}
CryptohomeStatus UpdatePasswordFactor(const std::string& label,
const std::string& new_password,
AuthSession& auth_session) {
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(label);
user_data_auth::AuthFactor& factor = *request.mutable_auth_factor();
factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
factor.set_label(label);
factor.mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(
new_password);
return RunUpdateAuthFactor(request, auth_session);
}
CryptohomeStatus UpdatePinFactor(const std::string& label,
const std::string& new_pin,
AuthSession& auth_session) {
user_data_auth::UpdateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(label);
user_data_auth::AuthFactor& factor = *request.mutable_auth_factor();
factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
factor.set_label(label);
factor.mutable_pin_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(new_pin);
return RunUpdateAuthFactor(request, auth_session);
}
CryptohomeStatus AuthenticatePinFactor(const std::string& label,
const std::string& pin,
AuthSession& auth_session) {
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(label);
request.mutable_auth_input()->mutable_pin_input()->set_secret(pin);
return RunAuthenticateAuthFactor(request, auth_session);
}
CryptohomeStatus AddRecoveryFactor(AuthSession& auth_session) {
brillo::SecureBlob mediator_pub_key;
EXPECT_TRUE(
FakeRecoveryMediatorCrypto::GetFakeMediatorPublicKey(&mediator_pub_key));
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
user_data_auth::AuthFactor& factor = *request.mutable_auth_factor();
factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY);
factor.set_label(kRecoveryLabel);
factor.mutable_cryptohome_recovery_metadata();
user_data_auth::CryptohomeRecoveryAuthInput& input =
*request.mutable_auth_input()->mutable_cryptohome_recovery_input();
input.set_mediator_pub_key(mediator_pub_key.to_string());
input.set_user_gaia_id(kUserGaiaId);
input.set_device_user_id(kDeviceUserId);
return RunAddAuthFactor(request, auth_session);
}
CryptohomeStatus AuthenticateRecoveryFactor(AuthSession& auth_session) {
// Retrieve fake server parameters.
brillo::SecureBlob epoch_pub_key;
EXPECT_TRUE(
FakeRecoveryMediatorCrypto::GetFakeEpochPublicKey(&epoch_pub_key));
brillo::SecureBlob epoch_priv_key;
EXPECT_TRUE(
FakeRecoveryMediatorCrypto::GetFakeEpochPrivateKey(&epoch_priv_key));
brillo::SecureBlob mediator_priv_key;
EXPECT_TRUE(FakeRecoveryMediatorCrypto::GetFakeMediatorPrivateKey(
&mediator_priv_key));
cryptorecovery::CryptoRecoveryEpochResponse epoch_response;
EXPECT_TRUE(
FakeRecoveryMediatorCrypto::GetFakeEpochResponse(&epoch_response));
// Obtain request for the server.
user_data_auth::GetRecoveryRequestRequest get_recovery_request;
get_recovery_request.set_auth_session_id(auth_session.serialized_token());
get_recovery_request.set_auth_factor_label(kRecoveryLabel);
get_recovery_request.set_epoch_response(epoch_response.SerializeAsString());
TestFuture<user_data_auth::GetRecoveryRequestReply> recovery_request_future;
auth_session.GetRecoveryRequest(
get_recovery_request,
recovery_request_future
.GetCallback<const user_data_auth::GetRecoveryRequestReply&>());
EXPECT_FALSE(recovery_request_future.Get().has_error_info());
cryptorecovery::CryptoRecoveryRpcRequest recovery_request;
EXPECT_TRUE(recovery_request.ParseFromString(
recovery_request_future.Get().recovery_request()));
// Create fake server.
std::unique_ptr<FakeRecoveryMediatorCrypto> recovery_crypto =
FakeRecoveryMediatorCrypto::Create();
if (!recovery_crypto) {
ADD_FAILURE() << "FakeRecoveryMediatorCrypto::Create failed";
return MakeFakeCryptohomeError();
}
// Generate fake server reply.
cryptorecovery::CryptoRecoveryRpcResponse recovery_response;
EXPECT_TRUE(recovery_crypto->MediateRequestPayload(
epoch_pub_key, epoch_priv_key, mediator_priv_key, recovery_request,
&recovery_response));
// Authenticate auth factor.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(kRecoveryLabel);
user_data_auth::CryptohomeRecoveryAuthInput& input =
*request.mutable_auth_input()->mutable_cryptohome_recovery_input();
input.set_epoch_response(epoch_response.SerializeAsString());
input.set_recovery_response(recovery_response.SerializeAsString());
auto ledger_info = FakeRecoveryMediatorCrypto::GetFakeLedgerInfo();
input.mutable_ledger_info()->set_name(ledger_info.name);
input.mutable_ledger_info()->set_key_hash(ledger_info.key_hash.value());
input.mutable_ledger_info()->set_public_key(
ledger_info.public_key.value().to_string());
return RunAuthenticateAuthFactor(request, auth_session);
}
// Fixture for testing `AuthSession` against TPM simulator and real
// implementations of auth blocks, UserSecretStash and VaultKeysets.
//
// This integration-like test is more expensive, but allows to check the code
// passes data and uses other class APIs correctly.
class AuthSessionWithTpmSimulatorTest : public ::testing::Test {
protected:
void SetUp() override {
// TODO(b/254864841): Remove this after le_credential code is migrated to
// use `Platform` instead of direct file operations in system-global paths.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
crypto_.set_le_manager_for_testing(
std::make_unique<LECredentialManagerImpl>(
hwsec_pinweaver_frontend_.get(),
temp_dir_.GetPath().AppendASCII("low_entropy_creds")));
// TODO(b/266217791): The simulator factory should instead do it itself.
ON_CALL(hwsec_simulator_factory_.GetMockBackend().GetMock().vendor,
GetManufacturer)
.WillByDefault(ReturnValue(0x43524F53));
crypto_.Init();
ASSERT_TRUE(platform_.CreateDirectory(ShadowRoot()));
ASSERT_TRUE(platform_.CreateDirectory(
brillo::cryptohome::home::GetUserPathPrefix()));
ASSERT_TRUE(
platform_.CreateDirectory(UserPath(SanitizeUserName(kUsername))));
}
const Username kUsername{"foo@example.com"};
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
// TPM simulator objects.
hwsec::Tpm2SimulatorFactoryForTest hwsec_simulator_factory_;
std::unique_ptr<const hwsec::CryptohomeFrontend> hwsec_cryptohome_frontend_ =
hwsec_simulator_factory_.GetCryptohomeFrontend();
std::unique_ptr<const hwsec::PinWeaverFrontend> hwsec_pinweaver_frontend_ =
hwsec_simulator_factory_.GetPinWeaverFrontend();
std::unique_ptr<const hwsec::RecoveryCryptoFrontend>
hwsec_recovery_crypto_frontend_ =
hwsec_simulator_factory_.GetRecoveryCryptoFrontend();
// TODO(b/254864841): Remove this after le_credential code is migrated to use
// `Platform` instead of direct file operations.
base::ScopedTempDir temp_dir_;
// AuthSession dependencies.
FakePlatform platform_;
CryptohomeKeysManager cryptohome_keys_manager_{
hwsec_cryptohome_frontend_.get(), &platform_};
Crypto crypto_{hwsec_cryptohome_frontend_.get(),
hwsec_pinweaver_frontend_.get(), &cryptohome_keys_manager_,
hwsec_recovery_crypto_frontend_.get()};
UserSessionMap user_session_map_;
KeysetManagement keyset_management_{&platform_, &crypto_,
std::make_unique<VaultKeysetFactory>()};
FakeFeaturesForTesting features_;
std::unique_ptr<FingerprintAuthBlockService> fp_service_{
FingerprintAuthBlockService::MakeNullService()};
AuthBlockUtilityImpl auth_block_utility_{
&keyset_management_,
&crypto_,
&platform_,
&features_.async,
AsyncInitPtr<ChallengeCredentialsHelper>(nullptr),
nullptr,
AsyncInitPtr<BiometricsAuthBlockService>(nullptr)};
AuthFactorDriverManager auth_factor_driver_manager_{
&platform_,
&crypto_,
AsyncInitPtr<ChallengeCredentialsHelper>(nullptr),
nullptr,
fp_service_.get(),
AsyncInitPtr<BiometricsAuthBlockService>(nullptr),
nullptr};
AuthFactorManager auth_factor_manager_{&platform_};
UssStorage uss_storage_{&platform_};
UserMetadataReader user_metadata_reader_{&uss_storage_};
AuthSession::BackingApis backing_apis_{&crypto_,
&platform_,
&user_session_map_,
&keyset_management_,
&auth_block_utility_,
&auth_factor_driver_manager_,
&auth_factor_manager_,
&uss_storage_,
&user_metadata_reader_,
&features_.async};
};
class AuthSessionWithTpmSimulatorUssMigrationTest
: public AuthSessionWithTpmSimulatorTest {
protected:
void AddPasswordVaultKeyset(const std::string& label,
const std::string& password) {
AuthInput auth_input = {
.user_input = brillo::SecureBlob(password),
.locked_to_single_user = std::nullopt,
.username = kUsername,
.obfuscated_username = SanitizeUserName(kUsername),
};
auth_block_utility_.CreateKeyBlobsWithAuthBlock(
AuthBlockType::kTpmEcc, auth_input,
base::BindLambdaForTesting(
[&](CryptohomeStatus error, std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_block_state) {
ASSERT_THAT(error, IsOk());
VaultKeyset vk;
vk.Initialize(&platform_, &crypto_);
KeyData key_data;
key_data.set_label(label);
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
vk.SetKeyData(key_data);
vk.CreateFromFileSystemKeyset(file_system_keyset_);
ASSERT_THAT(vk.EncryptEx(*key_blobs, *auth_block_state), IsOk());
ASSERT_TRUE(vk.Save(UserPath(SanitizeUserName(kUsername))
.Append(kKeyFile)
.AddExtension("0")));
}));
}
void AddPasswordAndPinVaultKeyset(const std::string& password_label,
const std::string& password,
const std::string& pin_label,
const std::string& pin) {
VaultKeyset password_vk;
password_vk.Initialize(&platform_, &crypto_);
KeyData key_data;
key_data.set_label(password_label);
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
password_vk.SetKeyData(key_data);
password_vk.CreateFromFileSystemKeyset(file_system_keyset_);
VaultKeyset pin_vk;
pin_vk.Initialize(&platform_, &crypto_);
key_data.set_label(pin_label);
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data.mutable_policy()->set_low_entropy_credential(true);
pin_vk.InitializeToAdd(password_vk);
pin_vk.SetKeyData(key_data);
AuthInput password_auth_input = {
.user_input = brillo::SecureBlob(password),
.locked_to_single_user = std::nullopt,
.username = kUsername,
.obfuscated_username = SanitizeUserName(kUsername),
};
AuthInput pin_auth_input = {
.user_input = brillo::SecureBlob(pin),
.locked_to_single_user = std::nullopt,
.username = kUsername,
.obfuscated_username = SanitizeUserName(kUsername),
.reset_seed = password_vk.GetResetSeed(),
};
auth_block_utility_.CreateKeyBlobsWithAuthBlock(
AuthBlockType::kTpmEcc, password_auth_input,
base::BindLambdaForTesting(
[&](CryptohomeStatus error, std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_block_state) {
ASSERT_THAT(error, IsOk());
ASSERT_THAT(password_vk.EncryptEx(*key_blobs, *auth_block_state),
IsOk());
ASSERT_TRUE(password_vk.Save(UserPath(SanitizeUserName(kUsername))
.Append(kKeyFile)
.AddExtension("0")));
}));
auth_block_utility_.CreateKeyBlobsWithAuthBlock(
AuthBlockType::kPinWeaver, pin_auth_input,
base::BindLambdaForTesting(
[&](CryptohomeStatus error, std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_block_state) {
ASSERT_THAT(error, IsOk());
ASSERT_THAT(pin_vk.EncryptEx(*key_blobs, *auth_block_state),
IsOk());
ASSERT_TRUE(pin_vk.Save(UserPath(SanitizeUserName(kUsername))
.Append(kKeyFile)
.AddExtension("1")));
}));
}
FileSystemKeyset file_system_keyset_ = FileSystemKeyset::CreateRandom();
};
// Test that it's possible to migrate PIN from VaultKeyset to UserSecretStash
// even after the password was already migrated and recovery (a USS-only factor)
// was added and used as well.
TEST_F(AuthSessionWithTpmSimulatorUssMigrationTest,
CompleteUssMigrationAfterRecoveryMidWay) {
// Move time to `kFakeTimestamp`.
task_environment_.FastForwardBy((kFakeTimestamp - base::Time::Now()));
auto create_auth_session = [this]() {
return AuthSession::Create(kUsername,
user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, backing_apis_);
};
// Arrange. Create a user with password and PIN VKs.
AddPasswordAndPinVaultKeyset(kPasswordLabel, kPassword, kPinLabel, kPin);
// Add recovery (after authenticating with password and
// hence migrating it to USS), and use recovery to update the password.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
EXPECT_THAT(AddRecoveryFactor(*auth_session), IsOk());
}
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(AuthenticateRecoveryFactor(*auth_session), IsOk());
EXPECT_THAT(
UpdatePasswordFactor(kPasswordLabel, kNewPassword, *auth_session),
IsOk());
}
// Assert. Both password (already migrated to USS) and PIN (not migrated yet)
// still work.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kNewPassword, *auth_session),
IsOk());
}
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kPin, *auth_session), IsOk());
}
}
// Test that it's possible to migrate a locked-out PIN from VaultKeyset to
// UserSecretStash even after the password was already migrated and recovery (a
// USS-only factor) was added and used as well.
TEST_F(AuthSessionWithTpmSimulatorUssMigrationTest,
CompleteLockedPinUssMigrationAfterRecoveryMidWay) {
// Move time to `kFakeTimestamp`.
task_environment_.FastForwardBy((kFakeTimestamp - base::Time::Now()));
constexpr char kWrongPin[] = "0000";
static_assert(std::string_view(kWrongPin) != std::string_view(kPin),
"Bad kWrongPin");
auto create_auth_session = [this]() {
return AuthSession::Create(kUsername,
user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, backing_apis_);
};
// Arrange. Create a user with password and PIN VKs.
AddPasswordAndPinVaultKeyset(kPasswordLabel, kPassword, kPinLabel, kPin);
// Act. Enable USS experiment, add recovery (after using the password and
// hence migrating it to USS).
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
EXPECT_THAT(AddRecoveryFactor(*auth_session), IsOk());
}
// Lock out the PIN factor.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
for (int i = 0; i < kPinResetCounter; ++i) {
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, "0", *auth_session),
NotOk());
}
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kPin, *auth_session), NotOk());
}
// Authenticate via password.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
}
// Assert. The PIN (not migrated yet) still works.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kPin, *auth_session), IsOk());
}
}
// Test that updating via a previously added password works correctly: you can
// authenticate via the new password but not via the old one.
TEST_F(AuthSessionWithTpmSimulatorUssMigrationTest, UpdatePassword) {
auto create_auth_session = [this]() {
return AuthSession::Create(kUsername,
user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, backing_apis_);
};
// Arrange. Configure the creation of a VK.
AddPasswordVaultKeyset(kPasswordLabel, kPassword);
// Act.
// Update the password factor after authenticating via the old password.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
EXPECT_THAT(
UpdatePasswordFactor(kPasswordLabel, kNewPassword, *auth_session),
IsOk());
}
// Assert.
auto try_authenticate = [&](const std::string& password) {
std::unique_ptr<AuthSession> auth_session = create_auth_session();
if (!auth_session) {
ADD_FAILURE() << "Failed to create AuthSession";
return MakeFakeCryptohomeError();
}
return AuthenticatePasswordFactor(kPasswordLabel, password, *auth_session);
};
// Check the old password isn't accepted, but the new one is.
EXPECT_THAT(try_authenticate(kPassword), NotOk());
EXPECT_THAT(try_authenticate(kNewPassword), IsOk());
}
// Test that updating via a previously added password works correctly: you can
// authenticate via the new password but not via the old one. All this while Pin
// is not migrated.
TEST_F(AuthSessionWithTpmSimulatorUssMigrationTest,
UpdatePasswordPartialMigration) {
auto create_auth_session = [this]() {
return AuthSession::Create(kUsername,
user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, backing_apis_);
};
// Arrange. Configure a password and a PIN VK.
AddPasswordAndPinVaultKeyset(kPasswordLabel, kPassword, kPinLabel, kPin);
// Act.
// Update the password factor after authenticating via the old password.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
EXPECT_THAT(
UpdatePasswordFactor(kPasswordLabel, kNewPassword, *auth_session),
IsOk());
}
// Assert.
auto try_authenticate = [&](const std::string& password) {
std::unique_ptr<AuthSession> auth_session = create_auth_session();
if (!auth_session) {
ADD_FAILURE() << "Failed to create AuthSession";
return MakeFakeCryptohomeError();
}
return AuthenticatePasswordFactor(kPasswordLabel, password, *auth_session);
};
// Check the old password isn't accepted, but the new one is.
EXPECT_THAT(try_authenticate(kPassword), NotOk());
EXPECT_THAT(try_authenticate(kNewPassword), IsOk());
// Expect Pin can be authenticated still.
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kPin, *auth_session), IsOk());
}
// Test that updating via a previously added PIN works correctly: you can
// authenticate via the new PIN but not via the old one. Update migrates the
// PIN.
TEST_F(AuthSessionWithTpmSimulatorUssMigrationTest, UpdatePinPartialMigration) {
auto create_auth_session = [this]() {
return AuthSession::Create(kUsername,
user_data_auth::AUTH_SESSION_FLAGS_NONE,
AuthIntent::kDecrypt, backing_apis_);
};
// Arrange. Configure a password and a PIN VK.
AddPasswordAndPinVaultKeyset(kPasswordLabel, kPassword, kPinLabel, kPin);
// Act.
// Update the PIN factor after authenticating via the password.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
EXPECT_THAT(UpdatePinFactor(kPinLabel, kNewPin, *auth_session), IsOk());
}
// Assert.
auto try_authenticate = [&](const std::string& pin) {
std::unique_ptr<AuthSession> auth_session = create_auth_session();
if (!auth_session) {
ADD_FAILURE() << "Failed to create AuthSession";
return MakeFakeCryptohomeError();
}
return AuthenticatePinFactor(kPinLabel, pin, *auth_session);
};
// Check the old PIN isn't accepted, but the new one is.
EXPECT_THAT(try_authenticate(kPin), NotOk());
EXPECT_THAT(try_authenticate(kNewPin), IsOk());
// Lockout PIN by attempting authenticate with wrong PINs.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
for (int i = 0; i < kPinResetCounter; i++) {
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kPin, *auth_session),
NotOk());
}
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kNewPin, *auth_session),
NotOk());
}
// Test that password resets the counter.
{
std::unique_ptr<AuthSession> auth_session = create_auth_session();
ASSERT_TRUE(auth_session);
EXPECT_THAT(
AuthenticatePasswordFactor(kPasswordLabel, kPassword, *auth_session),
IsOk());
// Verify that authenticate with correct PIN succeed after the counter is
// reset.
EXPECT_THAT(AuthenticatePinFactor(kPinLabel, kNewPin, *auth_session),
IsOk());
}
}
} // namespace
} // namespace cryptohome