| // 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/auth_session.h" |
| |
| #include <cstdint> |
| #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/simple_test_clock.h> |
| #include <base/test/task_environment.h> |
| #include <base/test/test_future.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.h> |
| #include <gtest/gtest.h> |
| #include <libhwsec/frontend/cryptohome/mock_frontend.h> |
| #include <libhwsec/frontend/pinweaver_manager/frontend.h> |
| #include <libhwsec/frontend/pinweaver_manager/mock_frontend.h> |
| #include <libhwsec/frontend/recovery_crypto/mock_frontend.h> |
| #include <libhwsec-foundation/crypto/aes.h> |
| #include <libhwsec-foundation/crypto/secure_box.h> |
| #include <libhwsec-foundation/error/testing_helper.h> |
| #include <libstorage/platform/mock_platform.h> |
| |
| #include "cryptohome/auth_blocks/biometrics_auth_block_service.h" |
| #include "cryptohome/auth_blocks/mock_auth_block_utility.h" |
| #include "cryptohome/auth_blocks/mock_biometrics_command_processor.h" |
| #include "cryptohome/auth_blocks/mock_cryptohome_recovery_service.h" |
| #include "cryptohome/auth_blocks/prepare_token.h" |
| #include "cryptohome/auth_factor/auth_factor.h" |
| #include "cryptohome/auth_factor/flatbuffer.h" |
| #include "cryptohome/auth_factor/manager.h" |
| #include "cryptohome/auth_factor/metadata.h" |
| #include "cryptohome/auth_factor/storage_type.h" |
| #include "cryptohome/auth_factor/type.h" |
| #include "cryptohome/auth_io/auth_input.h" |
| #include "cryptohome/auth_session/intent.h" |
| #include "cryptohome/challenge_credentials/challenge_credentials_helper.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/fake_features.h" |
| #include "cryptohome/filesystem_layout.h" |
| #include "cryptohome/flatbuffer_schemas/auth_block_state.h" |
| #include "cryptohome/flatbuffer_schemas/auth_factor.h" |
| #include "cryptohome/fp_migration/utility.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_signalling.h" |
| #include "cryptohome/pkcs11/mock_pkcs11_token_factory.h" |
| #include "cryptohome/recoverable_key_store/backend_cert_provider.h" |
| #include "cryptohome/recoverable_key_store/mock_backend_cert_provider.h" |
| #include "cryptohome/recoverable_key_store/type.h" |
| #include "cryptohome/signalling.h" |
| #include "cryptohome/storage/homedirs.h" |
| #include "cryptohome/storage/mock_mount.h" |
| #include "cryptohome/user_secret_stash/manager.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::AtLeast; |
| using ::testing::ByMove; |
| using ::testing::DoAll; |
| using ::testing::DoDefault; |
| 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; |
| |
| using AuthenticateTestFuture = |
| TestFuture<const AuthSession::PostAuthAction&, CryptohomeStatus>; |
| |
| // 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 kRecoveryLabel[] = "recovery"; |
| constexpr char kFakeFingerprintLabel[] = "test_fp_label"; |
| constexpr char kFakeSecondFingerprintLabel[] = "test_second_fp_label"; |
| |
| // Fake passwords to be in used in this test suite. |
| constexpr char kFakePass[] = "test_pass"; |
| constexpr char kFakePin[] = "123456"; |
| constexpr char kFakeOtherPass[] = "test_other_pass"; |
| constexpr char kFakeRecoverySecret[] = "test_recovery_secret"; |
| |
| // Fingerprint-related constants to be used in this test suite. |
| const uint64_t kFakeRateLimiterLabel = 100; |
| const uint64_t kFakeFpLabel = 200; |
| const uint64_t kFakeSecondFpLabel = 300; |
| constexpr char kFakeVkkKey[] = "fake_vkk_key"; |
| constexpr char kFakeSecondVkkKey[] = "fake_second_vkk_key"; |
| constexpr char kFakeRecordId[] = "fake_record_id"; |
| constexpr char kFakeSecondRecordId[] = "fake_second_record_id"; |
| |
| // Upper limit of the Size of user specified name. |
| constexpr int kUserSpecifiedNameSizeLimit = 256; |
| |
| // The fake recoverable key store service cert list version. |
| constexpr uint64_t kCertListVersion = 1000; |
| |
| // 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>(_)); |
| } |
| |
| AuthSession::AuthenticateAuthFactorRequest ToAuthenticateRequest( |
| std::vector<std::string> labels, user_data_auth::AuthInput auth_input) { |
| return AuthSession::AuthenticateAuthFactorRequest{ |
| .auth_factor_labels = std::move(labels), |
| .auth_input_proto = std::move(auth_input), |
| .flags = {.force_full_auth = AuthSession::ForceFullAuthFlag::kNone}, |
| }; |
| } |
| |
| std::optional<RecoverableKeyStoreBackendCert> GetValidBackendCert() { |
| const brillo::SecureBlob kSeed("seed_123"); |
| std::optional<hwsec_foundation::secure_box::KeyPair> key_pair = |
| hwsec_foundation::secure_box::DeriveKeyPairFromSeed(kSeed); |
| if (!key_pair.has_value()) { |
| return std::nullopt; |
| } |
| return RecoverableKeyStoreBackendCert{ |
| .version = kCertListVersion, |
| .public_key = key_pair->public_key, |
| }; |
| } |
| |
| std::optional<RecoverableKeyStoreState> |
| CreateRecoverableKeyStoreStateWithVersion(uint64_t version) { |
| RecoverableKeyStoreState state; |
| RecoverableKeyStore store; |
| std::string store_proto_string; |
| store.mutable_key_store_metadata()->set_cert_list_version(version); |
| if (!store.SerializeToString(&store_proto_string)) { |
| return std::nullopt; |
| } |
| state.key_store_proto = brillo::BlobFromString(store_proto_string); |
| return state; |
| } |
| |
| // A helpful utility for setting up AuthFactorMaps for testing. This provides a |
| // very concise way to construct them with a variety of configurable options. |
| // The way you use this is something like: |
| // |
| // auto auth_factor_map = AfMapBuilder().WithUss().AddPin("label").Consume(); |
| // |
| // The end result of this will a map that contains a USS-backed PIN. |
| class AfMapBuilder { |
| public: |
| AfMapBuilder() = default; |
| |
| AfMapBuilder(const AfMapBuilder&) = delete; |
| AfMapBuilder& operator=(const AfMapBuilder&) = delete; |
| |
| // Set the storage type of any subsequent factors. |
| AfMapBuilder& WithVk() { |
| storage_type_ = AuthFactorStorageType::kVaultKeyset; |
| return *this; |
| } |
| AfMapBuilder& WithUss() { |
| storage_type_ = AuthFactorStorageType::kUserSecretStash; |
| return *this; |
| } |
| |
| // Helpers to add different kinds of auth factors. |
| template <typename StateType> |
| AfMapBuilder& AddPassword(std::string label) { |
| return AddFactor<StateType>(label, AuthFactorType::kPassword); |
| } |
| AfMapBuilder& AddPin(std::string label) { |
| return AddFactor<PinWeaverAuthBlockState>(label, AuthFactorType::kPin); |
| } |
| AfMapBuilder& AddRecovery(std::string label) { |
| return AddFactor<CryptohomeRecoveryAuthBlockState>( |
| label, AuthFactorType::kCryptohomeRecovery); |
| } |
| |
| // Helper to add copies of factors from an existing AuthFactorMap. |
| AfMapBuilder& AddCopiesFromMap(const AuthFactorMap& af_map) { |
| for (AuthFactorMap::ValueView entry : af_map) { |
| map_.Add(entry.auth_factor(), storage_type_); |
| } |
| return *this; |
| } |
| |
| // Consume the map. |
| AuthFactorMap Consume() { return std::move(map_); } |
| |
| private: |
| // Generic add factor implementation. The template parameter specifies the |
| // type of auth block state to use, or void for none. |
| template <typename StateType> |
| AfMapBuilder& AddFactor(std::string label, AuthFactorType auth_factor_type) { |
| AuthBlockState auth_block_state; |
| if constexpr (!std::is_void_v<StateType>) { |
| auth_block_state.state = StateType(); |
| } |
| map_.Add(AuthFactor(auth_factor_type, std::move(label), |
| AuthFactorMetadata(), auth_block_state), |
| storage_type_); |
| return *this; |
| } |
| |
| AuthFactorStorageType storage_type_ = AuthFactorStorageType::kUserSecretStash; |
| |
| AuthFactorMap map_; |
| }; |
| |
| // Minimal prepare token class. Does nothing for termination. |
| class TestToken : public PreparedAuthFactorToken { |
| public: |
| using PreparedAuthFactorToken::PreparedAuthFactorToken; |
| |
| private: |
| CryptohomeStatus TerminateAuthFactor() override { |
| return OkStatus<CryptohomeError>(); |
| } |
| }; |
| |
| } // namespace |
| |
| class AuthSessionTest : public ::testing::Test { |
| public: |
| AuthSessionTest() { |
| auto mock_processor = |
| std::make_unique<NiceMock<MockBiometricsCommandProcessor>>(); |
| bio_processor_ = mock_processor.get(); |
| bio_service_ = std::make_unique<BiometricsAuthBlockService>( |
| std::move(mock_processor), |
| /*enroll_signal_sender=*/base::DoNothing(), |
| /*auth_signal_sender=*/base::DoNothing()); |
| } |
| |
| void SetUp() override { |
| EXPECT_CALL(hwsec_, IsEnabled()).WillRepeatedly(ReturnValue(true)); |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); |
| EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).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(hwsec_pw_manager_, IsEnabled()) |
| .WillRepeatedly(ReturnValue(true)); |
| crypto_.Init(); |
| } |
| |
| protected: |
| // Fake username to be used in this test suite. |
| const Username kFakeUsername{"test_username"}; |
| |
| // 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_, &user_activity_timestamp_manager_, |
| &pkcs11_token_factory_, new NiceMock<MockMount>())); |
| return user_session_map_.Find(username); |
| } |
| |
| // Set the auth factor map for the given user. Useful for initializing the |
| // user's map to a specific value. |
| void SetAuthFactorMap(const Username& username, |
| AuthFactorMap auth_factor_map) { |
| auth_factor_manager_.GetAuthFactorMap(SanitizeUserName(username)) = |
| std::move(auth_factor_map); |
| } |
| |
| base::test::SingleThreadTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| base::SimpleTestClock clock_; |
| scoped_refptr<base::SequencedTaskRunner> task_runner_ = |
| base::SequencedTaskRunner::GetCurrentDefault(); |
| |
| // Mocks and fakes for the test AuthSessions to use. |
| NiceMock<libstorage::MockPlatform> platform_; |
| NiceMock<hwsec::MockCryptohomeFrontend> hwsec_; |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager_; |
| NiceMock<hwsec::MockRecoveryCryptoFrontend> hwsec_recovery_crypto_; |
| NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_; |
| Crypto crypto_{&hwsec_, &hwsec_pw_manager_, &cryptohome_keys_manager_, |
| &hwsec_recovery_crypto_}; |
| UssStorage uss_storage_{&platform_}; |
| UssManager uss_manager_{uss_storage_}; |
| UserUssStorage user_uss_storage_{uss_storage_, |
| SanitizeUserName(kFakeUsername)}; |
| UserSessionMap user_session_map_; |
| NiceMock<MockKeysetManagement> keyset_management_; |
| NiceMock<MockAuthBlockUtility> auth_block_utility_; |
| NiceMock<MockCryptohomeRecoveryAuthBlockService> cr_service_{ |
| &platform_, &hwsec_recovery_crypto_}; |
| std::unique_ptr<FingerprintAuthBlockService> fp_service_{ |
| FingerprintAuthBlockService::MakeNullService()}; |
| NiceMock<MockChallengeCredentialsHelper> challenge_credentials_helper_; |
| NiceMock<MockKeyChallengeServiceFactory> key_challenge_service_factory_; |
| NiceMock<MockBiometricsCommandProcessor>* bio_processor_; |
| std::unique_ptr<BiometricsAuthBlockService> bio_service_; |
| NiceMock<MockRecoverableKeyStoreBackendCertProvider> cert_provider_; |
| AuthFactorDriverManager auth_factor_driver_manager_{ |
| &platform_, |
| &crypto_, |
| &uss_manager_, |
| AsyncInitPtr<ChallengeCredentialsHelper>(&challenge_credentials_helper_), |
| &key_challenge_service_factory_, |
| &cr_service_, |
| fp_service_.get(), |
| AsyncInitPtr<BiometricsAuthBlockService>(base::BindRepeating( |
| [](AuthSessionTest* test) { return test->bio_service_.get(); }, |
| base::Unretained(this)))}; |
| AuthFactorManager auth_factor_manager_{&platform_, &keyset_management_, |
| &uss_manager_}; |
| FakeFeaturesForTesting fake_features_; |
| FpMigrationUtility fp_migration_utility_{ |
| &crypto_, |
| AsyncInitPtr<BiometricsAuthBlockService>(base::BindRepeating( |
| [](AuthSessionTest* test) { return test->bio_service_.get(); }, |
| base::Unretained(this))), |
| &fake_features_.async}; |
| NiceMock<MockSignalling> signalling_; |
| AuthSession::BackingApis backing_apis_{ |
| &crypto_, |
| &platform_, |
| &user_session_map_, |
| &keyset_management_, |
| &auth_block_utility_, |
| &auth_factor_driver_manager_, |
| &auth_factor_manager_, |
| &fp_migration_utility_, |
| &uss_storage_, |
| &uss_manager_, |
| &fake_features_.async, |
| AsyncInitPtr<SignallingInterface>(&signalling_), |
| AsyncInitPtr<RecoverableKeyStoreBackendCertProvider>(base::BindRepeating( |
| [](AuthSessionTest* test) -> RecoverableKeyStoreBackendCertProvider* { |
| return &test->cert_provider_; |
| }, |
| base::Unretained(this)))}; |
| |
| // 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, TokensAreValid) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_FALSE(auth_session.token().is_empty()); |
| EXPECT_FALSE(auth_session.public_token().is_empty()); |
| EXPECT_NE(auth_session.token(), auth_session.public_token()); |
| |
| EXPECT_FALSE(auth_session.serialized_token().empty()); |
| EXPECT_FALSE(auth_session.serialized_public_token().empty()); |
| EXPECT_NE(auth_session.serialized_token(), |
| auth_session.serialized_public_token()); |
| } |
| |
| TEST_F(AuthSessionTest, InitiallyNotAuthenticated) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| } |
| |
| TEST_F(AuthSessionTest, InitiallyNotAuthenticatedForExistingUser) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| } |
| |
| TEST_F(AuthSessionTest, Username) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.username(), kFakeUsername); |
| EXPECT_EQ(auth_session.obfuscated_username(), |
| SanitizeUserName(kFakeUsername)); |
| } |
| |
| TEST_F(AuthSessionTest, DecryptionIntent) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.auth_intent(), AuthIntent::kDecrypt); |
| } |
| |
| TEST_F(AuthSessionTest, VerfyIntent) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.auth_intent(), AuthIntent::kVerifyOnly); |
| } |
| |
| TEST_F(AuthSessionTest, WebAuthnIntent) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kWebAuthn, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.auth_intent(), AuthIntent::kWebAuthn); |
| } |
| |
| TEST_F(AuthSessionTest, RestoreKeyIntent) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kRestoreKey, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.auth_intent(), AuthIntent::kRestoreKey); |
| } |
| |
| TEST_F(AuthSessionTest, ForensicsIntent) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kForensics, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| EXPECT_EQ(auth_session.auth_intent(), AuthIntent::kForensics); |
| } |
| |
| TEST_F(AuthSessionTest, SerializedStringFromNullToken) { |
| base::UnguessableToken token = base::UnguessableToken::Null(); |
| std::string serialized_token = |
| AuthSession::GetSerializedStringFromToken(token); |
| EXPECT_TRUE(serialized_token.empty()); |
| } |
| |
| 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 if AuthSession reports the correct attributes on an already-existing |
| // ephemeral user. |
| TEST_F(AuthSessionTest, ExistingEphemeralUser) { |
| // Setting the expectation that there is no persistent user but there is an |
| // active ephemeral one. |
| EXPECT_CALL(platform_, DirectoryExists(_)).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. |
| std::unique_ptr<AuthSession> auth_session = AuthSession::Create( |
| kFakeUsername, |
| {.is_ephemeral_user = true, .intent = AuthIntent::kDecrypt}, |
| backing_apis_); |
| |
| // Verify. |
| EXPECT_TRUE(auth_session->user_exists()); |
| } |
| |
| // Test that AuthenticateAuthFactor returns an error when supplied label and |
| // type mismatch. |
| TEST_F(AuthSessionTest, AuthenticateAuthFactorMismatchLabelAndType) { |
| // Setup AuthSession. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder().AddPin(kFakePinLabel).Consume()); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test |
| // Calling AuthenticateAuthFactor. |
| AuthenticateTestFuture authenticate_future; |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePin); |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_EQ(status->local_legacy_error(), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| } |
| |
| // Test that AddAuthFactor succeeds for an ephemeral user and creates a |
| // credential verifier. |
| TEST_F(AuthSessionTest, AddPasswordFactorToEphemeral) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| // 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.GetAuthForDecrypt()->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. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| // 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.GetAuthForDecrypt()->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. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| // 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.GetAuthForDecrypt()->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.GetAuthForDecrypt()->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 fails if label doesn't exist. |
| TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotMatchInAFMap) { |
| // Setup. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .AddPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel) |
| .Consume()); |
| |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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.GetAuthForDecrypt()->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 factors. |
| TEST_F(AuthSessionTest, UpdateAuthFactorFailsLabelNotFoundInAFMap) { |
| // Setup. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .AddPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel) |
| .Consume()); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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.GetAuthForDecrypt()->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()); |
| } |
| |
| // A variant of the auth session test that tests AuthFactor APIs with the |
| // UserSecretStash. |
| class AuthSessionWithUssTest : public AuthSessionTest { |
| protected: |
| struct ReplyToVerifyKey { |
| void operator()(const Username& account_id, |
| const SerializedChallengePublicKeyInfo& public_key_info, |
| std::unique_ptr<KeyChallengeService> key_challenge_service, |
| ChallengeCredentialsHelper::VerifyKeyCallback callback) { |
| if (is_key_valid) { |
| std::move(callback).Run(OkStatus<error::CryptohomeCryptoError>()); |
| } else { |
| const error::CryptohomeError::ErrorLocationPair |
| kErrorLocationPlaceholder = |
| error::CryptohomeError::ErrorLocationPair( |
| static_cast< |
| ::cryptohome::error::CryptohomeError::ErrorLocation>(1), |
| "Testing1"); |
| |
| std::move(callback).Run(MakeStatus<error::CryptohomeCryptoError>( |
| kErrorLocationPlaceholder, |
| error::ErrorActionSet(error::PrimaryAction::kIncorrectAuth), |
| CryptoError::CE_OTHER_CRYPTO)); |
| } |
| } |
| 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kCryptohomeRecovery)); |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kCryptohomeRecovery, |
| _, _, _)) |
| .WillOnce([&secret](auto auth_block_type, auto auth_input, |
| auto auth_factor_metadata, 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.GetAuthForDecrypt()->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, |
| AuthSession& auth_session) { |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL( |
| auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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 cert provider expectation. |
| std::optional<RecoverableKeyStoreBackendCert> backend_cert = |
| GetValidBackendCert(); |
| if (backend_cert.has_value()) { |
| ON_CALL(cert_provider_, GetBackendCert) |
| .WillByDefault(Return(*backend_cert)); |
| } |
| 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); |
| user_data_auth::KnowledgeFactorHashInfo hash_info; |
| hash_info.set_algorithm( |
| KnowledgeFactorHashAlgorithm::HASH_TYPE_SHA256_TOP_HALF); |
| hash_info.set_salt("fake_salt"); |
| hash_info.set_should_generate_key_store(true); |
| *request.mutable_auth_factor() |
| ->mutable_password_metadata() |
| ->mutable_hash_info() = hash_info; |
| 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.GetAuthForDecrypt()->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_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| // Prepare recovery authentication request. |
| std::vector<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(); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| // Authenticate using recovery. |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| // Verify. |
| auto& [unused_action, status] = authenticate_future.Get(); |
| if (status.ok() || !status->local_legacy_error().has_value()) { |
| return user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| } |
| return status->local_legacy_error().value(); |
| } |
| |
| user_data_auth::CryptohomeErrorCode AuthenticatePasswordAuthFactor( |
| const std::string& label, |
| const std::string& password, |
| AuthSession& auth_session) { |
| EXPECT_CALL(auth_block_utility_, |
| GetAuthBlockTypeFromState( |
| AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>())) |
| .WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL( |
| auth_block_utility_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| |
| AuthenticateTestFuture authenticate_future; |
| std::vector<std::string> auth_factor_labels{label}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(password); |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [unused_action, status] = authenticate_future.Get(); |
| if (status.ok() || !status->local_legacy_error().has_value()) { |
| return user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| } |
| return status->local_legacy_error().value(); |
| } |
| |
| user_data_auth::CryptohomeErrorCode UpdatePasswordAuthFactor( |
| const std::string& new_password, AuthSession& auth_session) { |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL( |
| auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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.GetAuthForDecrypt()->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 UpdateAuthFactorMetadata( |
| user_data_auth::AuthFactor& auth_factor_proto, |
| AuthSession& auth_session) { |
| user_data_auth::UpdateAuthFactorMetadataRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(auth_factor_proto.label()); |
| *request.mutable_auth_factor() = std::move(auth_factor_proto); |
| |
| TestFuture<CryptohomeStatus> update_future; |
| auth_session.GetAuthForDecrypt()->UpdateAuthFactorMetadata( |
| request, update_future.GetCallback()); |
| |
| if (update_future.Get().ok() || |
| !update_future.Get().status()->local_legacy_error().has_value()) { |
| return user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| } |
| |
| return update_future.Get().status()->local_legacy_error().value(); |
| } |
| |
| user_data_auth::CryptohomeErrorCode RelabelAuthFactor( |
| const std::string& old_label, |
| const std::string& new_label, |
| AuthSession& auth_session) { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(old_label); |
| request.set_new_auth_factor_label(new_label); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| |
| if (relabel_future.Get().ok() || |
| !relabel_future.Get()->local_legacy_error().has_value()) { |
| return user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| } |
| |
| return relabel_future.Get()->local_legacy_error().value(); |
| } |
| |
| user_data_auth::CryptohomeErrorCode AddPinAuthFactor( |
| const std::string& pin, AuthSession& auth_session) { |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kPinWeaver)); |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kPinWeaver, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| AuthBlock::CreateCallback create_callback) { |
| // PIN is a knowledge factor, so security domain keys |
| // should be populated in auth input. |
| if (!auth_input.security_domain_keys.has_value()) { |
| std::move(create_callback) |
| .Run(MakeStatus<error::CryptohomeError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT), |
| nullptr, nullptr); |
| } |
| // 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()); |
| key_blobs->reset_secret = auth_input.reset_secret; |
| 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 cert provider expectation. |
| std::optional<RecoverableKeyStoreBackendCert> backend_cert = |
| GetValidBackendCert(); |
| if (backend_cert.has_value()) { |
| ON_CALL(cert_provider_, GetBackendCert) |
| .WillByDefault(Return(*backend_cert)); |
| } |
| // 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); |
| user_data_auth::KnowledgeFactorHashInfo hash_info; |
| hash_info.set_algorithm( |
| KnowledgeFactorHashAlgorithm::HASH_TYPE_PBKDF2_AES256_1234); |
| hash_info.set_salt("fake_salt"); |
| hash_info.set_should_generate_key_store(true); |
| *add_pin_request.mutable_auth_factor() |
| ->mutable_pin_metadata() |
| ->mutable_hash_info() = hash_info; |
| add_pin_request.mutable_auth_input()->mutable_pin_input()->set_secret(pin); |
| TestFuture<CryptohomeStatus> add_future; |
| auth_session.GetAuthForDecrypt()->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(); |
| } |
| |
| user_data_auth::CryptohomeErrorCode AddFingerprintAuthFactor( |
| AuthSession& auth_session, |
| const std::string& label, |
| const brillo::SecureBlob& vkk_key, |
| const std::string& record_id, |
| uint64_t leaf_label) { |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillOnce(ReturnValue(AuthBlockType::kFingerprint)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kFingerprint, _, _, _)) |
| .WillOnce([&](AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| AuthBlock::CreateCallback create_callback) { |
| EXPECT_TRUE(auth_input.reset_secret.has_value()); |
| // Make an arbitrary auth block state type that can be used in the |
| // tests. |
| auto key_blobs = std::make_unique<KeyBlobs>(); |
| key_blobs->vkk_key = vkk_key; |
| key_blobs->reset_secret = auth_input.reset_secret; |
| auto auth_block_state = std::make_unique<AuthBlockState>(); |
| FingerprintAuthBlockState fingerprint_state = |
| FingerprintAuthBlockState(); |
| fingerprint_state.template_id = record_id; |
| fingerprint_state.gsc_secret_label = leaf_label; |
| auth_block_state->state = fingerprint_state; |
| 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_FINGERPRINT); |
| request.mutable_auth_factor()->set_label(label); |
| request.mutable_auth_factor()->mutable_fingerprint_metadata(); |
| request.mutable_auth_input()->mutable_fingerprint_input(); |
| |
| TestFuture<CryptohomeStatus> add_future; |
| auth_session.GetAuthForDecrypt()->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 AddFirstFingerprintAuthFactor( |
| AuthSession& auth_session) { |
| return AddFingerprintAuthFactor(auth_session, kFakeFingerprintLabel, |
| brillo::SecureBlob(kFakeVkkKey), |
| kFakeRecordId, kFakeFpLabel); |
| } |
| |
| user_data_auth::CryptohomeErrorCode AddSecondFingerprintAuthFactor( |
| AuthSession& auth_session) { |
| return AddFingerprintAuthFactor(auth_session, kFakeSecondFingerprintLabel, |
| brillo::SecureBlob(kFakeSecondVkkKey), |
| kFakeSecondRecordId, kFakeSecondFpLabel); |
| } |
| |
| void SetUpPrepareFingerprintForAdd() { |
| const brillo::Blob kNonce(32, 1); |
| EXPECT_CALL(*bio_processor_, GetNonce(_)) |
| .WillOnce( |
| [kNonce](auto&& callback) { std::move(callback).Run(kNonce); }); |
| EXPECT_CALL(hwsec_pw_manager_, StartBiometricsAuth) |
| .WillOnce(Return( |
| hwsec::PinWeaverManagerFrontend::StartBiometricsAuthReply{})); |
| EXPECT_CALL(*bio_processor_, StartEnrollSession(_, _)) |
| .WillOnce( |
| [](auto&&, auto&& callback) { std::move(callback).Run(true); }); |
| } |
| |
| void SetUpPrepareFingerprintForAuth() { |
| const brillo::Blob kNonce(32, 1); |
| EXPECT_CALL(*bio_processor_, GetNonce(_)) |
| .WillOnce( |
| [kNonce](auto&& callback) { std::move(callback).Run(kNonce); }); |
| EXPECT_CALL(hwsec_pw_manager_, StartBiometricsAuth) |
| .WillOnce(Return( |
| hwsec::PinWeaverManagerFrontend::StartBiometricsAuthReply{})); |
| EXPECT_CALL(*bio_processor_, StartAuthenticateSession(_, _, _)) |
| .WillOnce([](auto&&, auto&&, auto&& callback) { |
| std::move(callback).Run(true); |
| }); |
| } |
| }; |
| |
| // Test that the USS is created on the user creation. |
| TEST_F(AuthSessionWithUssTest, UssCreation) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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 USS is created for an ephemeral user. |
| TEST_F(AuthSessionWithUssTest, NoUssForEphemeral) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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. |
| TEST_F(AuthSessionWithUssTest, AddPasswordAuthFactorViaUss) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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)); |
| }); |
| // 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.GetAuthForDecrypt()->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_factor_manager_.GetAuthFactorMap(auth_session.obfuscated_username()) |
| .Find(kFakeLabel), |
| Optional(_)); |
| } |
| |
| // TODO(betuls) : migrate to uss test |
| // Test that AuthenticateAuthFactor succeeds in the `AuthIntent::kWebAuthn` |
| // scenario. |
| TEST_F(AuthSessionWithUssTest, AuthenticateAuthFactorWebAuthnIntent) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kWebAuthn, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, |
| AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session)); |
| // 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))); |
| // Calling AuthenticateAuthFactor. |
| EXPECT_EQ( |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET, |
| AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session)); |
| |
| // Verify. |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly, |
| AuthIntent::kWebAuthn)); |
| } |
| |
| // Test that a new auth factor can be added to the newly created user using |
| // asynchronous key creation. |
| TEST_F(AuthSessionWithUssTest, AddPasswordAuthFactorViaAsyncUss) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([this](AuthBlockType, const AuthInput&, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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))); |
| }); |
| // 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.GetAuthForDecrypt()->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_factor_manager_.GetAuthFactorMap(auth_session.obfuscated_username()) |
| .Find(kFakeLabel), |
| Optional(_)); |
| } |
| |
| // Test the new auth factor failure path when asynchronous key creation fails. |
| TEST_F(AuthSessionWithUssTest, AddPasswordAuthFactorViaAsyncUssFails) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([this](AuthBlockType, const AuthInput&, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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::PossibleAction::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.GetAuthForDecrypt()->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 the new auth factor failure path when asynchronous key creation succeeds |
| // but when writing to USS fails. |
| TEST_F(AuthSessionWithUssTest, |
| AddPasswordAuthFactorViaAsyncUssFailsOnWriteFailure) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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 |
| // but then writing to USS will fail. |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([this](AuthBlockType, const AuthInput&, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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))); |
| }); |
| EXPECT_CALL(platform_, WriteFileAtomicDurable(_, _, _)) |
| .WillRepeatedly(DoDefault()); |
| EXPECT_CALL(platform_, |
| WriteFileAtomicDurable( |
| UserSecretStashPath(SanitizeUserName(kFakeUsername), |
| kUserSecretStashDefaultSlot), |
| _, _)) |
| .Times(AtLeast(1)) |
| .WillRepeatedly(Return(false)); |
| // 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.GetAuthForDecrypt()->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); |
| } |
| |
| // Test that a new auth factor and a pin can be added to the newly created user, |
| // in case the USS experiment is on. |
| TEST_F(AuthSessionWithUssTest, AddPasswordAndPinAuthFactorViaUss) { |
| const std::string kHashSalt(16, 0xAA); |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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)); |
| }); |
| // 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.GetAuthForDecrypt()->AddAuthFactor(request, |
| add_future.GetCallback()); |
| |
| // Verify. |
| EXPECT_THAT(add_future.Get(), IsOk()); |
| |
| // Setting the expectation that the auth block utility will create key blobs. |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kPinWeaver)); |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kPinWeaver, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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)); |
| }); |
| // Calling AddAuthFactor. |
| user_data_auth::KnowledgeFactorHashInfo hash_info; |
| hash_info.set_algorithm( |
| KnowledgeFactorHashAlgorithm::HASH_TYPE_PBKDF2_AES256_1234); |
| hash_info.set_salt(kHashSalt); |
| hash_info.set_should_generate_key_store(true); |
| 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() |
| ->mutable_hash_info() = hash_info; |
| add_pin_request.mutable_auth_input()->mutable_pin_input()->set_secret( |
| kFakePin); |
| // Test and Verify. |
| TestFuture<CryptohomeStatus> add_pin_future; |
| auth_session.GetAuthForDecrypt()->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))); |
| CryptohomeStatusOr<AuthFactor> loaded_pin_factor = |
| auth_factor_manager_.LoadAuthFactor(SanitizeUserName(kFakeUsername), |
| AuthFactorType::kPin, kFakePinLabel); |
| ASSERT_THAT(loaded_pin_factor, IsOk()); |
| const auto* loaded_hash_info = loaded_pin_factor->metadata().hash_info(); |
| ASSERT_NE(loaded_hash_info, nullptr); |
| ASSERT_TRUE(loaded_hash_info->algorithm.has_value()); |
| EXPECT_EQ(*loaded_hash_info->algorithm, |
| SerializedKnowledgeFactorHashAlgorithm::PBKDF2_AES256_1234); |
| EXPECT_EQ(loaded_hash_info->salt, brillo::BlobFromString(kHashSalt)); |
| } |
| |
| // Test that an existing user with an existing password auth factor can be |
| // authenticated. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePasswordAuthFactorViaUss) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, DeriveKeyBlobsWithAuthBlock( |
| 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), |
| std::nullopt); |
| }); |
| // Calling AuthenticateAuthFactor. |
| AuthenticateTestFuture authenticate_future; |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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 cannot be |
| // authenticated with forensic intent. |
| TEST_F(AuthSessionWithUssTest, |
| AuthenticatePasswordAuthFactorViaUssViaForensics) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kForensics, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Calling AuthenticateAuthFactor. |
| AuthenticateTestFuture authenticate_future; |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, NotOk()); |
| } |
| |
| // Test that an existing user with an existing password auth factor can be |
| // authenticated, using asynchronous key derivation. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePasswordAuthFactorViaAsyncUss) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, DeriveKeyBlobsWithAuthBlock( |
| 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), std::nullopt)); |
| }); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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(AuthSessionWithUssTest, AuthenticatePasswordAuthFactorViaAsyncUssFails) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, DeriveKeyBlobsWithAuthBlock( |
| 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::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO), |
| nullptr, std::nullopt)); |
| }); |
| |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_EQ(status->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. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePinAuthFactorViaUss) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor(AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{.metadata = PinMetadata()}, |
| AuthBlockState{.state = PinWeaverAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| } |
| |
| // Test that an existing user with an existing pin auth factor can be |
| // authenticated and then re-created if the derive suggests it. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePinAuthFactorViaUssWithRecreate) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor(AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{.metadata = PinMetadata()}, |
| AuthBlockState{.state = PinWeaverAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will derive key |
| // blobs, and then that there will be additional calls to re-create |
| // them. |
| EXPECT_CALL(auth_block_utility_, |
| GetAuthBlockTypeFromState( |
| AuthBlockStateTypeIs<PinWeaverAuthBlockState>())) |
| .WillRepeatedly(Return(AuthBlockType::kPinWeaver)); |
| EXPECT_CALL(auth_block_utility_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| AuthBlock::SuggestedAction::kRecreate); |
| }); |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kPinWeaver)); |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kPinWeaver, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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)); |
| }); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| } |
| |
| // Test that an existing user with an existing pin auth factor can be |
| // authenticated and then re-created if the derive suggests it. This test |
| // verifies that the authenticate still works even if the re-create fails. |
| TEST_F(AuthSessionWithUssTest, |
| AuthenticatePinAuthFactorViaUssWithRecreateThatFails) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor(AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{.metadata = PinMetadata()}, |
| AuthBlockState{.state = PinWeaverAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will derive key |
| // blobs, and then that there will be additional calls to re-create |
| // them. |
| EXPECT_CALL(auth_block_utility_, |
| GetAuthBlockTypeFromState( |
| AuthBlockStateTypeIs<PinWeaverAuthBlockState>())) |
| .WillRepeatedly(Return(AuthBlockType::kPinWeaver)); |
| EXPECT_CALL(auth_block_utility_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| AuthBlock::SuggestedAction::kRecreate); |
| }); |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly([](auto...) -> CryptoStatusOr<AuthBlockType> { |
| return MakeStatus<CryptohomeCryptoError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO); |
| }); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| } |
| |
| // Test that if a user gets locked out, the AuthFactorStatusUpdate timer |
| // is set and called periodically. |
| TEST_F(AuthSessionTest, AuthFactorStatusUpdateTimerTest) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{.metadata = PinMetadata()}, |
| AuthBlockState{.state = PinWeaverAuthBlockState{.le_label = 0xbaadf00d}}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, |
| DeriveKeyBlobsWithAuthBlock(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(MakeStatus<error::CryptohomeCryptoError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PrimaryAction::kIncorrectAuth}), |
| CryptoError::CE_CREDENTIAL_LOCKED), |
| nullptr, std::nullopt); |
| }); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| // The wrong pin needs to be sent multiple times. |wrong_pin| is set to |
| // be different than |kFakePin|. |
| std::string wrong_pin = "232323"; |
| auth_input_proto.mutable_pin_input()->set_secret(wrong_pin); |
| EXPECT_CALL(hwsec_pw_manager_, GetDelayInSeconds(_)) |
| .WillRepeatedly(ReturnValue(UINT32_MAX)); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| // As currently the user is locked out until they log in via password, |
| // the delay policy does not matter, but once the passwordless policy is |
| // set, this timing should change to reflect that. |
| task_environment_.FastForwardBy(base::Seconds(30)); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, AddCryptohomeRecoveryAuthFactor) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kCryptohomeRecovery)); |
| EXPECT_CALL( |
| auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kCryptohomeRecovery, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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.GetAuthForDecrypt()->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(AuthSessionWithUssTest, |
| PrepareAndTerminateCryptohomeRecoveryAuthFactor) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. |
| AuthFactor auth_factor( |
| AuthFactorType::kCryptohomeRecovery, kFakeLabel, |
| AuthFactorMetadata{.metadata = CryptohomeRecoveryMetadata()}, |
| AuthBlockState{.state = CryptohomeRecoveryAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // Adding the auth factor into the USS. |
| const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret}; |
| std::optional<brillo::SecureBlob> wrapping_key = |
| key_blobs.DeriveUssCredentialSecret(); |
| ASSERT_TRUE(wrapping_key.has_value()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| |
| // Set up expectations that the request will succeed. |
| EXPECT_CALL(cr_service_, GenerateRecoveryRequest(_, _, _, _, _)) |
| .WillOnce([](const ObfuscatedUsername&, |
| const cryptorecovery::RequestMetadata&, const brillo::Blob&, |
| const CryptohomeRecoveryAuthBlockState&, |
| PreparedAuthFactorToken::Consumer on_done) { |
| PrepareOutput prepare_output = { |
| .cryptohome_recovery_prepare_output = |
| CryptohomeRecoveryPrepareOutput{}, |
| }; |
| std::move(on_done).Run(std::make_unique<TestToken>( |
| AuthFactorType::kCryptohomeRecovery, std::move(prepare_output))); |
| }); |
| |
| // Prepare the recovery factor. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_CRYPTOHOME_RECOVERY); |
| prepare_request.set_purpose(user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR); |
| auto* recovery_input = prepare_request.mutable_prepare_input() |
| ->mutable_cryptohome_recovery_input(); |
| recovery_input->set_auth_factor_label(kFakeLabel); |
| auth_session.PrepareAuthFactor(prepare_request, prepare_future.GetCallback()); |
| ASSERT_THAT(prepare_future.Get(), IsOk()); |
| |
| // Terminate the recovery factor. |
| 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_CRYPTOHOME_RECOVERY); |
| TestFuture<CryptohomeStatus> terminate_future; |
| auth_session.TerminateAuthFactor(request, terminate_future.GetCallback()); |
| ASSERT_THAT(terminate_future.Get(), IsOk()); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, AuthenticateCryptohomeRecoveryAuthFactor) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. |
| AuthFactor auth_factor( |
| AuthFactorType::kCryptohomeRecovery, kFakeLabel, |
| AuthFactorMetadata{.metadata = CryptohomeRecoveryMetadata()}, |
| AuthBlockState{.state = CryptohomeRecoveryAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Set up expectations that the request will succeed. |
| EXPECT_CALL(cr_service_, GenerateRecoveryRequest(_, _, _, _, _)) |
| .WillOnce([](const ObfuscatedUsername&, |
| const cryptorecovery::RequestMetadata&, const brillo::Blob&, |
| const CryptohomeRecoveryAuthBlockState&, |
| PreparedAuthFactorToken::Consumer on_done) { |
| PrepareOutput prepare_output = { |
| .cryptohome_recovery_prepare_output = |
| CryptohomeRecoveryPrepareOutput{ |
| .ephemeral_pub_key = brillo::BlobFromString("test")}, |
| }; |
| std::move(on_done).Run(std::make_unique<TestToken>( |
| AuthFactorType::kCryptohomeRecovery, std::move(prepare_output))); |
| }); |
| 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_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| // 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_, |
| DeriveKeyBlobsWithAuthBlock(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::BlobFromString("test"))); |
| auto key_blobs = std::make_unique<KeyBlobs>(); |
| key_blobs->vkk_key = kFakePerCredentialSecret; |
| std::move(derive_callback) |
| .Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs), |
| std::nullopt); |
| }); |
| |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_cryptohome_recovery_input() |
| ->mutable_recovery_response(); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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_F(AuthSessionWithUssTest, |
| AuthenticateCryptohomeRecoveryAuthFactorWithForensics) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. |
| AuthFactor auth_factor( |
| AuthFactorType::kCryptohomeRecovery, kFakeLabel, |
| AuthFactorMetadata{.metadata = CryptohomeRecoveryMetadata()}, |
| AuthBlockState{.state = CryptohomeRecoveryAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kForensics, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Set up expectations that the request will succeed. |
| EXPECT_CALL(cr_service_, GenerateRecoveryRequest(_, _, _, _, _)) |
| .WillOnce([](const ObfuscatedUsername&, |
| const cryptorecovery::RequestMetadata&, const brillo::Blob&, |
| const CryptohomeRecoveryAuthBlockState&, |
| PreparedAuthFactorToken::Consumer on_done) { |
| PrepareOutput prepare_output = { |
| .cryptohome_recovery_prepare_output = |
| CryptohomeRecoveryPrepareOutput{ |
| .ephemeral_pub_key = brillo::BlobFromString("test")}, |
| }; |
| std::move(on_done).Run(std::make_unique<TestToken>( |
| AuthFactorType::kCryptohomeRecovery, std::move(prepare_output))); |
| }); |
| 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_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| // 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_, |
| DeriveKeyBlobsWithAuthBlock(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::BlobFromString("test"))); |
| auto key_blobs = std::make_unique<KeyBlobs>(); |
| key_blobs->vkk_key = kFakePerCredentialSecret; |
| std::move(derive_callback) |
| .Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs), |
| std::nullopt); |
| }); |
| |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_cryptohome_recovery_input() |
| ->mutable_recovery_response(); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| // Note that forensic intent is part of the elements here. |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly, |
| AuthIntent::kForensics)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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(AuthSessionWithUssTest, 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(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. |
| AuthFactor auth_factor( |
| AuthFactorType::kSmartCard, kFakeLabel, |
| AuthFactorMetadata{ |
| .metadata = |
| SmartCardMetadata{.public_key_spki_der = public_key_spki_der}}, |
| AuthBlockState{.state = ChallengeCredentialAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(auth_factor, AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| EXPECT_FALSE(auth_session.has_user_secret_stash()); |
| |
| // Verify. |
| EXPECT_THAT(auth_session.authorized_intents(), IsEmpty()); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), IsNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| |
| // 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_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| |
| // Calling AuthenticateAuthFactor. |
| std::vector<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); |
| auth_input_proto.mutable_smart_card_input() |
| ->set_key_delegate_dbus_service_name("test_cc_dbus"); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| 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))); |
| |
| AuthFactorMap verify_auth_factor_map; |
| verify_auth_factor_map.Add(auth_factor, |
| AuthFactorStorageType::kUserSecretStash); |
| SetAuthFactorMap(kFakeUsername, std::move(verify_auth_factor_map)); |
| AuthSession verify_auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // Simulate a successful key verification. |
| EXPECT_CALL(challenge_credentials_helper_, VerifyKey(_, _, _, _)) |
| .WillOnce(ReplyToVerifyKey{/*is_key_valid=*/true}); |
| |
| // Call AuthenticateAuthFactor again. |
| AuthenticateTestFuture verify_authenticate_future; |
| auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| verify_auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, 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(AuthSessionWithUssTest, 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 = PasswordMetadata()}); |
| 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. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder().AddPassword<void>(kFakeLabel).Consume()); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // Test. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kVerifyOnly)); |
| } |
| |
| // Test that if there is a credential to reset, after a lightweight auth, |
| // a post action requesting repeating full auth should be returned. |
| TEST_F(AuthSessionWithUssTest, LightweightPasswordPostAction) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakeLabel, *wrapping_key), |
| IsOk()); |
| // Add a rate-limiter so that later on a reset is needed after full auth. |
| ASSERT_THAT( |
| transaction.InitializeFingerprintRateLimiterId(kFakeRateLimiterLabel), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Setup the credential verifier. |
| auto user_session = std::make_unique<MockUserSession>(); |
| EXPECT_CALL(*user_session, VerifyUser(SanitizeUserName(kFakeUsername))) |
| .WillRepeatedly(Return(true)); |
| auto verifier = std::make_unique<MockCredentialVerifier>( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordMetadata()}); |
| EXPECT_CALL(*verifier, VerifySync(_)).WillOnce(ReturnOk<CryptohomeError>()); |
| user_session->AddCredentialVerifier(std::move(verifier)); |
| EXPECT_TRUE(user_session_map_.Add(kFakeUsername, std::move(user_session))); |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // Expectations for the full auth. |
| EXPECT_CALL(auth_block_utility_, |
| GetAuthBlockTypeFromState( |
| AuthBlockStateTypeIs<TpmBoundToPcrAuthBlockState>())) |
| .WillRepeatedly(Return(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, DeriveKeyBlobsWithAuthBlock( |
| 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), std::nullopt)); |
| }); |
| |
| // Test. |
| std::vector<std::string> auth_factor_labels{kFakeLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakePass); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kRepeat); |
| ASSERT_TRUE(action.repeat_request.has_value()); |
| EXPECT_EQ(action.repeat_request->flags.force_full_auth, |
| AuthSession::ForceFullAuthFlag::kForce); |
| EXPECT_THAT(auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kVerifyOnly)); |
| |
| // Test and verify with repeat request. |
| AuthenticateTestFuture second_authenticate_future; |
| auth_session.AuthenticateAuthFactor(action.repeat_request.value(), |
| auth_factor_type_policy, |
| second_authenticate_future.GetCallback()); |
| auto& [second_action, second_status] = second_authenticate_future.Get(); |
| EXPECT_THAT(second_status, IsOk()); |
| EXPECT_EQ(second_action.action_type, AuthSession::PostAuthActionType::kNone); |
| } |
| |
| // Test that AuthenticateAuthFactor succeeds for the |
| // `AuthIntent::kVerifyOnly` scenario, using the legacy fingerprint. |
| TEST_F(AuthSessionWithUssTest, LightweightFingerprintAuthentication) { |
| // Setup. |
| // 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. |
| AuthSession auth_session( |
| AuthSession::Params{.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // Test. |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_legacy_fingerprint_input(); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = |
| GetEmptyAuthFactorTypePolicy(AuthFactorType::kLegacyFingerprint); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest({}, auth_input_proto), auth_factor_type_policy, |
| authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kVerifyOnly)); |
| } |
| |
| // Test that PrepareAuthFactor succeeded for password. |
| TEST_F(AuthSessionWithUssTest, PreparePasswordFailure) { |
| // Setup. |
| // Add the user session. Configure the credential verifier mock to |
| // succeed. |
| auto user_session = std::make_unique<MockUserSession>(); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // 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(AuthSessionWithUssTest, TerminateAuthFactorBadTypeFailure) { |
| // Setup. |
| // Add the user session. Configure the credential verifier mock to |
| // succeed. |
| auto user_session = std::make_unique<MockUserSession>(); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // 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(AuthSessionWithUssTest, TerminateAuthFactorInactiveFactorFailure) { |
| // Setup. |
| // Add the user session. Configure the credential verifier mock to |
| // succeed. |
| auto user_session = std::make_unique<MockUserSession>(); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // 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_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(AuthSessionWithUssTest, PrepareAndTerminateFingerprintForAuthSuccess) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| { |
| auto transaction = uss->StartTransaction(); |
| // Add a rate-limiter so that later on a reset is needed after full auth. |
| ASSERT_THAT( |
| transaction.InitializeFingerprintRateLimiterId(kFakeRateLimiterLabel), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| SetUpPrepareFingerprintForAuth(); |
| 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_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_FINGERPRINT); |
| TestFuture<CryptohomeStatus> terminate_future; |
| auth_session.TerminateAuthFactor(request, terminate_future.GetCallback()); |
| |
| // Verify. |
| ASSERT_THAT(terminate_future.Get(), IsOk()); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RemoveAuthFactor) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Both password and pin are available. |
| std::map<std::string, AuthFactorType> stored_factors = |
| auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername)); |
| EXPECT_THAT(stored_factors, |
| ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword), |
| Pair(kFakePinLabel, AuthFactorType::kPin))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakePinLabel), Optional(_)); |
| } |
| |
| // 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.GetAuthForDecrypt()->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))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakePinLabel), Eq(std::nullopt)); |
| } |
| |
| // Calling AuthenticateAuthFactor for password succeeds. |
| error = AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Calling AuthenticateAuthFactor for pin fails. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_EQ(status->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(AuthSessionWithUssTest, RemoveAuthFactorPartialRemoveIsStillOk) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Both password and pin are available. |
| std::map<std::string, AuthFactorType> stored_factors = |
| auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername)); |
| EXPECT_THAT(stored_factors, |
| ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword), |
| Pair(kFakePinLabel, AuthFactorType::kPin))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakePinLabel), Optional(_)); |
| } |
| |
| // Disable the writing of the USS file. This shouldn't cause the remove |
| // operation to fail. |
| EXPECT_CALL(platform_, |
| WriteFileAtomicDurable( |
| UserSecretStashPath(SanitizeUserName(kFakeUsername), |
| kUserSecretStashDefaultSlot), |
| _, _)) |
| .Times(AtLeast(1)) |
| .WillRepeatedly(Return(false)); |
| |
| // 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.GetAuthForDecrypt()->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))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakePinLabel), Eq(std::nullopt)); |
| } |
| |
| // Calling AuthenticateAuthFactor for password succeeds. |
| error = AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Calling AuthenticateAuthFactor for pin fails. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_EQ(status->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(AuthSessionWithUssTest, RemoveAuthFactorRemovesCredentialVerifier) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPasswordAuthFactor(kFakeOtherLabel, kFakeOtherPass, 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))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakeOtherLabel), Optional(_)); |
| } |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT( |
| user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre( |
| IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass), |
| IsVerifierPtrWithLabelAndPassword(kFakeOtherLabel, kFakeOtherPass))); |
| |
| // 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.GetAuthForDecrypt()->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))); |
| { |
| const auto& auth_factor_map = auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username()); |
| EXPECT_THAT(auth_factor_map.Find(kFakeLabel), Optional(_)); |
| EXPECT_THAT(auth_factor_map.Find(kFakeOtherLabel), Eq(std::nullopt)); |
| } |
| |
| // Calling AuthenticateAuthFactor for the first password succeeds. |
| error = AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Calling AuthenticateAuthFactor for the second password fails. |
| std::vector<std::string> auth_factor_labels{kFakeOtherLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_password_input()->set_secret(kFakeOtherPass); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_EQ(status->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(AuthSessionWithUssTest, RemoveAndReAddAuthFactor) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| // Calling RemoveAuthFactor for pin. |
| user_data_auth::RemoveAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakePinLabel); |
| |
| TestFuture<CryptohomeStatus> remove_future; |
| auth_session.GetAuthForDecrypt()->RemoveAuthFactor( |
| request, remove_future.GetCallback()); |
| |
| EXPECT_THAT(remove_future.Get(), IsOk()); |
| |
| // Add the same pin auth factor again. |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| // The verifier still uses the original password. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre( |
| IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RemoveAuthFactorFailsForLastFactor) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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, 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.GetAuthForDecrypt()->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(AuthSessionWithUssTest, UpdateAuthFactor) { |
| // Setup. |
| std::string new_pass = "update fake pass"; |
| |
| { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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, 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); |
| } |
| |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .WithUss() |
| .AddPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel) |
| .Consume()); |
| AuthSession new_auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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(kFakeLabel, new_pass, new_auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| 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(AuthSessionWithUssTest, AddPinAfterRecoveryAuth) { |
| // Setup. |
| { |
| // Obtain AuthSession for user setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // Create the user with password and recovery factors. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| EXPECT_EQ(AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session), |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| EXPECT_EQ(AddRecoveryAuthFactor(kRecoveryLabel, kFakeRecoverySecret, |
| auth_session), |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| } |
| |
| // Obtain AuthSession for authentication. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .WithUss() |
| .AddPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel) |
| .AddRecovery(kRecoveryLabel) |
| .Consume()); |
| AuthSession new_auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // 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(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(AuthSessionWithUssTest, UpdatePasswordAfterRecoveryAuth) { |
| // Setup. |
| constexpr char kNewFakePass[] = "new fake pass"; |
| { |
| // Obtain AuthSession for user setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // Create the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| // Add password AuthFactor. |
| EXPECT_EQ(AddPasswordAuthFactor(kFakeLabel, kFakePass, 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); |
| } |
| |
| // Set up mocks for the now-existing user. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Obtain AuthSession for authentication. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .WithUss() |
| .AddPassword<TpmBoundToPcrAuthBlockState>(kFakeLabel) |
| .AddRecovery(kRecoveryLabel) |
| .Consume()); |
| AuthSession new_auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // 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(AuthSessionWithUssTest, UpdateAuthFactorFailsForWrongLabel) { |
| // Setup. |
| AuthSession auth_session( |
| AuthSession::Params{.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, 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.GetAuthForDecrypt()->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(AuthSessionWithUssTest, UpdateAuthFactorFailsForWrongType) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, 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.GetAuthForDecrypt()->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(AuthSessionWithUssTest, UpdateAuthFactorFailsWhenLabelDoesntExist) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // 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, 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.GetAuthForDecrypt()->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(AuthSessionWithUssTest, UpdateAuthFactorFailsInAuthBlock) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user and add the auth factor. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Setting the expectations for the new auth block creation. The mock is set |
| // to fail. |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock(_, _, _, _)) |
| .WillOnce( |
| [](auto, auto, auto, AuthBlock::CreateCallback create_callback) { |
| std::move(create_callback) |
| .Run(MakeStatus<CryptohomeCryptoError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PossibleAction::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.GetAuthForDecrypt()->UpdateAuthFactor( |
| update_request, update_future.GetCallback()); |
| |
| // Verify. |
| EXPECT_THAT(update_future.Get(), NotOk()); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, UpdateAuthFactorMetadataSuccess) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| user_data_auth::AuthFactor new_auth_factor; |
| std::string kFakeChromeVersion = "fake chrome version"; |
| std::string kUserSpecifiedName = "password"; |
| |
| new_auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| new_auth_factor.set_label(kFakeLabel); |
| new_auth_factor.mutable_password_metadata(); |
| new_auth_factor.mutable_common_metadata()->set_chrome_version_last_updated( |
| kFakeChromeVersion); |
| new_auth_factor.mutable_common_metadata()->set_user_specified_name( |
| kUserSpecifiedName); |
| |
| error = UpdateAuthFactorMetadata(new_auth_factor, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| auto loaded_auth_factor = auth_factor_manager_.LoadAuthFactor( |
| SanitizeUserName(kFakeUsername), AuthFactorType::kPassword, kFakeLabel); |
| EXPECT_THAT(loaded_auth_factor, IsOk()); |
| EXPECT_EQ(loaded_auth_factor->type(), AuthFactorType::kPassword); |
| EXPECT_EQ(loaded_auth_factor->label(), kFakeLabel); |
| EXPECT_EQ(loaded_auth_factor->metadata().common.chrome_version_last_updated, |
| kFakeChromeVersion); |
| EXPECT_EQ(loaded_auth_factor->metadata().common.user_specified_name, |
| kUserSpecifiedName); |
| |
| // Calling AuthenticateAuthFactor with the password succeeds. |
| error = AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, UpdateAuthFactorMetadataEmptyLabelFailure) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| user_data_auth::AuthFactor new_auth_factor; |
| |
| new_auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| new_auth_factor.set_label(""); |
| new_auth_factor.mutable_password_metadata(); |
| |
| error = UpdateAuthFactorMetadata(new_auth_factor, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, UpdateAuthFactorMetadataWrongLabelFailure) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| user_data_auth::AuthFactor new_auth_factor; |
| |
| new_auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| new_auth_factor.set_label(kFakeOtherLabel); |
| new_auth_factor.mutable_password_metadata(); |
| |
| error = UpdateAuthFactorMetadata(new_auth_factor, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, UpdateAuthFactorMetadataLongNameFailure) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| user_data_auth::AuthFactor new_auth_factor; |
| std::string extra_long_name(kUserSpecifiedNameSizeLimit + 1, 'x'); |
| |
| new_auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| new_auth_factor.set_label(kFakeLabel); |
| new_auth_factor.mutable_password_metadata(); |
| new_auth_factor.mutable_common_metadata()->set_user_specified_name( |
| extra_long_name); |
| |
| error = UpdateAuthFactorMetadata(new_auth_factor, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, UpdateAuthFactorMetadataWrongTypeFailure) { |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_THAT(auth_session.OnUserCreated(), IsOk()); |
| 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| user_data_auth::AuthFactor new_auth_factor; |
| |
| new_auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN); |
| new_auth_factor.set_label(kFakeLabel); |
| new_auth_factor.mutable_pin_metadata(); |
| |
| error = UpdateAuthFactorMetadata(new_auth_factor, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| // Test that AuthenticateAuthFactor succeeds for the `AuthIntent::kWebAuthn` |
| // scenario, using the legacy fingerprint. |
| TEST_F(AuthSessionWithUssTest, FingerprintAuthenticationForWebAuthn) { |
| // Setup. |
| // 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. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kWebAuthn, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| |
| // Test. |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_legacy_fingerprint_input(); |
| AuthenticateTestFuture authenticate_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest({}, auth_input_proto), auth_factor_type_policy, |
| authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kVerifyOnly, AuthIntent::kWebAuthn)); |
| } |
| |
| // Test that PrepareAuthFactor succeeds for fingerprint with the purpose of add. |
| TEST_F(AuthSessionWithUssTest, PrepareFingerprintAdd) { |
| // Create an AuthSession and add a mock for a successful auth block prepare. |
| auto auth_session = std::make_unique<AuthSession>( |
| AuthSession::Params{.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session->OnUserCreated().ok()); |
| EXPECT_CALL(hwsec_pw_manager_, InsertRateLimiter) |
| .WillOnce(ReturnValue(/* ret_label */ 0)); |
| |
| SetUpPrepareFingerprintForAdd(); |
| |
| // Test. |
| 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_FINGERPRINT); |
| prepare_request.set_purpose(user_data_auth::PURPOSE_ADD_AUTH_FACTOR); |
| auth_session->PrepareAuthFactor(prepare_request, |
| prepare_future.GetCallback()); |
| // Verify. |
| ASSERT_THAT(prepare_future.Get(), IsOk()); |
| |
| // Test. |
| TestFuture<CryptohomeStatus> terminate_future; |
| user_data_auth::TerminateAuthFactorRequest terminate_request; |
| terminate_request.set_auth_session_id(auth_session->serialized_token()); |
| terminate_request.set_auth_factor_type( |
| user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT); |
| auth_session->TerminateAuthFactor(terminate_request, |
| terminate_future.GetCallback()); |
| // Verify. |
| ASSERT_THAT(terminate_future.Get(), IsOk()); |
| |
| // This time, the rate-limiter doesn't need to be created anymore. |
| SetUpPrepareFingerprintForAdd(); |
| |
| // Test. |
| TestFuture<CryptohomeStatus> prepare_future2; |
| user_data_auth::PrepareAuthFactorRequest prepare_request2; |
| prepare_request2.set_auth_session_id(auth_session->serialized_token()); |
| prepare_request2.set_auth_factor_type( |
| user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT); |
| prepare_request2.set_purpose(user_data_auth::PURPOSE_ADD_AUTH_FACTOR); |
| auth_session->PrepareAuthFactor(prepare_request2, |
| prepare_future2.GetCallback()); |
| // Verify. |
| ASSERT_THAT(prepare_future2.Get(), IsOk()); |
| } |
| |
| // Test adding two fingerprint auth factors and authenticating them. |
| TEST_F(AuthSessionWithUssTest, AddFingerprintAndAuth) { |
| const brillo::SecureBlob kFakeAuthPin(32, 1), kFakeAuthSecret(32, 2); |
| // Setup. |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Prepare is necessary to create the rate-limiter. |
| EXPECT_CALL(hwsec_pw_manager_, InsertRateLimiter) |
| .WillOnce(ReturnValue(kFakeRateLimiterLabel)); |
| SetUpPrepareFingerprintForAdd(); |
| 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_FINGERPRINT); |
| prepare_request.set_purpose(user_data_auth::PURPOSE_ADD_AUTH_FACTOR); |
| auth_session.PrepareAuthFactor(prepare_request, prepare_future.GetCallback()); |
| ASSERT_THAT(prepare_future.Get(), IsOk()); |
| |
| EXPECT_EQ(AddFirstFingerprintAuthFactor(auth_session), |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| EXPECT_EQ(AddSecondFingerprintAuthFactor(auth_session), |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| EXPECT_CALL(auth_block_utility_, GetAuthBlockTypeFromState(_)) |
| .WillRepeatedly(Return(AuthBlockType::kFingerprint)); |
| EXPECT_CALL(auth_block_utility_, SelectAuthFactorWithAuthBlock( |
| AuthBlockType::kFingerprint, _, _, _)) |
| .WillRepeatedly([&](AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| std::vector<AuthFactor> auth_factors, |
| AuthBlock::SelectFactorCallback select_callback) { |
| ASSERT_TRUE(auth_input.rate_limiter_label.has_value()); |
| EXPECT_EQ(auth_input.rate_limiter_label.value(), kFakeRateLimiterLabel); |
| EXPECT_EQ(auth_factors.size(), 2); |
| |
| AuthInput ret_auth_input{ |
| .user_input = kFakeAuthPin, |
| .fingerprint_auth_input = |
| FingerprintAuthInput{ |
| .auth_secret = kFakeAuthSecret, |
| }, |
| }; |
| |
| // Assume the second auth factor is matched. |
| std::move(select_callback) |
| .Run(OkStatus<CryptohomeCryptoError>(), ret_auth_input, |
| auth_factors[1]); |
| }); |
| EXPECT_CALL(auth_block_utility_, |
| DeriveKeyBlobsWithAuthBlock(AuthBlockType::kFingerprint, _, _, _)) |
| .WillRepeatedly([&](AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| const AuthBlockState& auth_state, |
| AuthBlock::DeriveCallback derive_callback) { |
| ASSERT_TRUE(auth_input.user_input.has_value()); |
| ASSERT_TRUE(auth_input.fingerprint_auth_input.has_value()); |
| ASSERT_TRUE(auth_input.fingerprint_auth_input->auth_secret.has_value()); |
| EXPECT_EQ(auth_input.user_input.value(), kFakeAuthPin); |
| EXPECT_EQ(auth_input.fingerprint_auth_input->auth_secret.value(), |
| kFakeAuthSecret); |
| ASSERT_TRUE(std::holds_alternative<FingerprintAuthBlockState>( |
| auth_state.state)); |
| auto& state = std::get<FingerprintAuthBlockState>(auth_state.state); |
| EXPECT_EQ(state.template_id, kFakeSecondRecordId); |
| |
| auto key_blobs = std::make_unique<KeyBlobs>(); |
| key_blobs->vkk_key = brillo::SecureBlob(kFakeSecondVkkKey); |
| |
| std::move(derive_callback) |
| .Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs), |
| std::nullopt); |
| }); |
| // Set expectations that fingerprint credential leaves with non-zero wrong |
| // auth attempts will be reset after a successful authentication. |
| EXPECT_CALL(hwsec_pw_manager_, GetWrongAuthAttempts(kFakeFpLabel)) |
| .Times(2) |
| .WillRepeatedly(ReturnValue(1)); |
| EXPECT_CALL(hwsec_pw_manager_, GetWrongAuthAttempts(kFakeSecondFpLabel)) |
| .Times(2) |
| .WillRepeatedly(ReturnValue(0)); |
| EXPECT_CALL(hwsec_pw_manager_, |
| ResetCredential(kFakeRateLimiterLabel, _, |
| hwsec::PinWeaverManagerFrontend::ResetType:: |
| kWrongAttemptsAndExpirationTime)) |
| .Times(2); |
| EXPECT_CALL(hwsec_pw_manager_, |
| ResetCredential( |
| kFakeFpLabel, _, |
| hwsec::PinWeaverManagerFrontend::ResetType::kWrongAttempts)) |
| .Times(2); |
| EXPECT_CALL(hwsec_pw_manager_, ResetCredential(kFakeSecondFpLabel, _, _)) |
| .Times(0); |
| |
| // Test. |
| SetAuthFactorMap(kFakeUsername, |
| AfMapBuilder() |
| .WithUss() |
| .AddCopiesFromMap(auth_factor_manager_.GetAuthFactorMap( |
| auth_session.obfuscated_username())) |
| .Consume()); |
| std::vector<std::string> auth_factor_labels{kFakeFingerprintLabel, |
| kFakeSecondFingerprintLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_fingerprint_input(); |
| AuthSession verify_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kVerifyOnly, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| AuthenticateTestFuture verify_future; |
| auto auth_factor_type_policy = GetEmptyAuthFactorTypePolicy( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)); |
| verify_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, verify_future.GetCallback()); |
| // Trigger the LE reset expectations. |
| verify_session.ResetLECredentials(); |
| AuthenticateTestFuture decrypt_future_without_policy, |
| decrypt_future_with_policy; |
| AuthSession decrypt_session1({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| decrypt_session1.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, decrypt_future_without_policy.GetCallback()); |
| AuthSession decrypt_session2({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| SerializedUserAuthFactorTypePolicy user_policy( |
| {.type = SerializedAuthFactorType::kFingerprint, |
| .enabled_intents = {SerializedAuthIntent::kDecrypt}, |
| .disabled_intents = {}}); |
| decrypt_session2.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), user_policy, |
| decrypt_future_with_policy.GetCallback()); |
| // Trigger the LE reset expectations. |
| decrypt_session2.ResetLECredentials(); |
| |
| // Verify. |
| auto [action, status] = verify_future.Take(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_THAT(verify_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kVerifyOnly)); |
| std::tie(action, status) = decrypt_future_without_policy.Take(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kReprepare); |
| EXPECT_THAT(status, NotOk()); |
| EXPECT_THAT(decrypt_session1.authorized_intents(), IsEmpty()); |
| std::tie(action, status) = decrypt_future_with_policy.Take(); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_THAT( |
| decrypt_session2.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RelabelAuthFactor) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| |
| // Calling RelabelAuthFactor. |
| error = RelabelAuthFactor(kFakeLabel, kFakeOtherLabel, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Calling AuthenticateAuthFactor works with the new label. |
| error = |
| AuthenticatePasswordAuthFactor(kFakeOtherLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The relabel should also be reflected in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre(IsVerifierPtrWithLabelAndPassword( |
| kFakeOtherLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RelabelAuthFactorWithBadInputs) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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; |
| |
| // Add a couple of auth factors. |
| error = AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| |
| // Trying to relabel an empty label. |
| { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_new_auth_factor_label(kFakeOtherLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| EXPECT_THAT(relabel_future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to relabel to an empty label. |
| { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakeLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| EXPECT_THAT(relabel_future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to relabel a factor that doesn't exist. |
| { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(std::string(kFakeLabel) + "DoesNotExist"); |
| request.set_new_auth_factor_label(kFakeOtherLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| EXPECT_THAT(relabel_future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| } |
| |
| // Trying to relabel a factor to a label that already exists. |
| { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakeLabel); |
| request.set_new_auth_factor_label(kFakePinLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| EXPECT_THAT(relabel_future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to relabel a factor to itself. |
| { |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakeLabel); |
| request.set_new_auth_factor_label(kFakeLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| EXPECT_THAT(relabel_future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RelabelAuthFactorWithFileFailure) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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; |
| |
| // Add a couple of auth factors. |
| error = AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Disable the writing of the USS file. The rename should fail and we should |
| // still be able to use the old name. |
| EXPECT_CALL(platform_, WriteFileAtomicDurable(_, _, _)) |
| .WillRepeatedly(DoDefault()); |
| EXPECT_CALL(platform_, |
| WriteFileAtomicDurable( |
| UserSecretStashPath(SanitizeUserName(kFakeUsername), |
| kUserSecretStashDefaultSlot), |
| _, _)) |
| .Times(AtLeast(1)) |
| .WillRepeatedly(Return(false)); |
| |
| // Test. |
| |
| // Trying to relabel an empty label. |
| user_data_auth::RelabelAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakeLabel); |
| request.set_new_auth_factor_label(kFakeOtherLabel); |
| |
| TestFuture<CryptohomeStatus> relabel_future; |
| auth_session.GetAuthForDecrypt()->RelabelAuthFactor( |
| request, relabel_future.GetCallback()); |
| ASSERT_THAT(relabel_future.Get(), NotOk()); |
| |
| // Calling AuthenticateAuthFactor works with the old label. |
| error = AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The session verifiers should still be under the old label. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre( |
| IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, RelabelAuthFactorEphemeral) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_FALSE(auth_session.has_user_secret_stash()); |
| |
| user_data_auth::CryptohomeErrorCode error = |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| |
| // Add the initial auth factor. |
| 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.GetAuthForDecrypt()->AddAuthFactor(request, |
| add_future.GetCallback()); |
| EXPECT_THAT(add_future.Get(), IsOk()); |
| |
| // Test. |
| |
| // Calling RelabelAuthFactor. |
| error = RelabelAuthFactor(kFakeLabel, kFakeOtherLabel, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The relabel should be reflected in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre(IsVerifierPtrWithLabelAndPassword( |
| kFakeOtherLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, ReplaceAuthFactor) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Add the initial auth factor. |
| user_data_auth::CryptohomeErrorCode error = |
| AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| |
| // Calling ReplaceAuthFactor. |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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::ReplaceAuthFactorRequest replace_request; |
| replace_request.set_auth_session_id(auth_session.serialized_token()); |
| replace_request.set_auth_factor_label(kFakeLabel); |
| replace_request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| replace_request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| replace_request.mutable_auth_factor()->mutable_password_metadata(); |
| replace_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakeOtherPass); |
| TestFuture<CryptohomeStatus> replace_future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor( |
| replace_request, replace_future.GetCallback()); |
| EXPECT_THAT(replace_future.Get(), IsOk()); |
| |
| // Calling AuthenticateAuthFactor works with the new label. |
| error = AuthenticatePasswordAuthFactor(kFakeOtherLabel, kFakeOtherPass, |
| auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The replace should be reflected in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre(IsVerifierPtrWithLabelAndPassword( |
| kFakeOtherLabel, kFakeOtherPass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, ReplaceAuthFactorWithBadInputs) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // 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; |
| |
| // Add a couple of auth factors. |
| error = AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| error = AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Test. |
| |
| // Standard request parts. All the various tests mess around with the labels. |
| user_data_auth::ReplaceAuthFactorRequest 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()->mutable_password_metadata(); |
| request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakeOtherPass); |
| |
| // Trying to replace an empty label. |
| { |
| request.set_auth_factor_label(""); |
| request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| |
| TestFuture<CryptohomeStatus> future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor(request, |
| future.GetCallback()); |
| ASSERT_THAT(future.Get(), NotOk()); |
| EXPECT_THAT(future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to replace to an empty label. |
| { |
| request.set_auth_factor_label(kFakeLabel); |
| request.mutable_auth_factor()->set_label(""); |
| |
| TestFuture<CryptohomeStatus> future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor(request, |
| future.GetCallback()); |
| ASSERT_THAT(future.Get(), NotOk()); |
| EXPECT_THAT(future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to replace a factor that doesn't exist. |
| { |
| request.set_auth_factor_label(std::string(kFakeLabel) + "DoesNotExist"); |
| request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| |
| TestFuture<CryptohomeStatus> future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor(request, |
| future.GetCallback()); |
| ASSERT_THAT(future.Get(), NotOk()); |
| EXPECT_THAT(future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| } |
| |
| // Trying to replace a factor to a label that already exists. |
| { |
| request.set_auth_factor_label(kFakeLabel); |
| request.mutable_auth_factor()->set_label(kFakePinLabel); |
| |
| TestFuture<CryptohomeStatus> future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor(request, |
| future.GetCallback()); |
| ASSERT_THAT(future.Get(), NotOk()); |
| EXPECT_THAT(future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| |
| // Trying to replace a factor to itself. |
| { |
| request.set_auth_factor_label(kFakeLabel); |
| request.mutable_auth_factor()->set_label(kFakeLabel); |
| |
| TestFuture<CryptohomeStatus> future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor(request, |
| future.GetCallback()); |
| ASSERT_THAT(future.Get(), NotOk()); |
| EXPECT_THAT(future.Get()->local_legacy_error(), |
| Optional(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| } |
| } |
| |
| TEST_F(AuthSessionWithUssTest, ReplaceAuthFactorWithFailedAdd) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Add the initial auth factor. |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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::AddAuthFactorRequest add_request; |
| add_request.set_auth_session_id(auth_session.serialized_token()); |
| user_data_auth::AuthFactor& request_factor = |
| *add_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(); |
| add_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakePass); |
| TestFuture<CryptohomeStatus> add_future; |
| auth_session.GetAuthForDecrypt()->AddAuthFactor(add_request, |
| add_future.GetCallback()); |
| EXPECT_THAT(add_future.Get(), IsOk()); |
| |
| // Test. |
| |
| // Calling ReplaceAuthFactor. Have the key blob creation fail. |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock(_, _, _, _)) |
| .WillOnce( |
| [](auto, auto, auto, AuthBlock::CreateCallback create_callback) { |
| std::move(create_callback) |
| .Run(MakeStatus<CryptohomeCryptoError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO), |
| nullptr, nullptr); |
| }); |
| user_data_auth::ReplaceAuthFactorRequest replace_request; |
| replace_request.set_auth_session_id(auth_session.serialized_token()); |
| replace_request.set_auth_factor_label(kFakeLabel); |
| replace_request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| replace_request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| replace_request.mutable_auth_factor()->mutable_password_metadata(); |
| replace_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakeOtherPass); |
| TestFuture<CryptohomeStatus> replace_future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor( |
| replace_request, replace_future.GetCallback()); |
| EXPECT_THAT(replace_future.Get(), NotOk()); |
| |
| // Calling AuthenticateAuthFactor still works with the old label. |
| user_data_auth::CryptohomeErrorCode error = |
| AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The replace should not show up in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre( |
| IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, ReplaceAuthFactorWithFileFailure) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Add the initial auth factor. |
| EXPECT_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillRepeatedly(ReturnValue(AuthBlockType::kTpmBoundToPcr)); |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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::AddAuthFactorRequest add_request; |
| add_request.set_auth_session_id(auth_session.serialized_token()); |
| user_data_auth::AuthFactor& request_factor = |
| *add_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(); |
| add_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakePass); |
| TestFuture<CryptohomeStatus> add_future; |
| auth_session.GetAuthForDecrypt()->AddAuthFactor(add_request, |
| add_future.GetCallback()); |
| EXPECT_THAT(add_future.Get(), IsOk()); |
| |
| // Test. |
| |
| // Disable the writing of the USS file. The replace should fail and we should |
| // still be able to use the old name. |
| EXPECT_CALL(platform_, WriteFileAtomicDurable(_, _, _)) |
| .WillRepeatedly(DoDefault()); |
| EXPECT_CALL(platform_, |
| WriteFileAtomicDurable( |
| UserSecretStashPath(SanitizeUserName(kFakeUsername), |
| kUserSecretStashDefaultSlot), |
| _, _)) |
| .Times(AtLeast(1)) |
| .WillRepeatedly(Return(false)); |
| |
| // Calling ReplaceAuthFactor. The key blob creation will succeed but adding |
| // the new factor into USS will not. |
| EXPECT_CALL(auth_block_utility_, CreateKeyBlobsWithAuthBlock( |
| AuthBlockType::kTpmBoundToPcr, _, _, _)) |
| .WillOnce([](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| 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::ReplaceAuthFactorRequest replace_request; |
| replace_request.set_auth_session_id(auth_session.serialized_token()); |
| replace_request.set_auth_factor_label(kFakeLabel); |
| replace_request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| replace_request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| replace_request.mutable_auth_factor()->mutable_password_metadata(); |
| replace_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakeOtherPass); |
| TestFuture<CryptohomeStatus> replace_future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor( |
| replace_request, replace_future.GetCallback()); |
| EXPECT_THAT(replace_future.Get(), NotOk()); |
| |
| // Calling AuthenticateAuthFactor still works with the old label. |
| user_data_auth::CryptohomeErrorCode error = |
| AuthenticatePasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // The replace should not show up in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre( |
| IsVerifierPtrWithLabelAndPassword(kFakeLabel, kFakePass))); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, ReplaceAuthFactorEphemeral) { |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = true, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_FALSE(auth_session.has_user_secret_stash()); |
| |
| // Add the initial auth factor. |
| user_data_auth::AddAuthFactorRequest add_request; |
| add_request.set_auth_session_id(auth_session.serialized_token()); |
| user_data_auth::AuthFactor& request_factor = |
| *add_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(); |
| add_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakePass); |
| TestFuture<CryptohomeStatus> add_future; |
| auth_session.GetAuthForDecrypt()->AddAuthFactor(add_request, |
| add_future.GetCallback()); |
| EXPECT_THAT(add_future.Get(), IsOk()); |
| |
| // Test. |
| |
| // Calling ReplaceAuthFactor. |
| user_data_auth::ReplaceAuthFactorRequest replace_request; |
| replace_request.set_auth_session_id(auth_session.serialized_token()); |
| replace_request.set_auth_factor_label(kFakeLabel); |
| replace_request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| replace_request.mutable_auth_factor()->set_label(kFakeOtherLabel); |
| replace_request.mutable_auth_factor()->mutable_password_metadata(); |
| replace_request.mutable_auth_input()->mutable_password_input()->set_secret( |
| kFakeOtherPass); |
| TestFuture<CryptohomeStatus> replace_future; |
| auth_session.GetAuthForDecrypt()->ReplaceAuthFactor( |
| replace_request, replace_future.GetCallback()); |
| EXPECT_THAT(replace_future.Get(), IsOk()); |
| |
| // The relabel should be reflected in the session verifiers. |
| UserSession* user_session = FindOrCreateUserSession(kFakeUsername); |
| EXPECT_THAT(user_session->GetCredentialVerifiers(), |
| UnorderedElementsAre(IsVerifierPtrWithLabelAndPassword( |
| kFakeOtherLabel, kFakeOtherPass))); |
| } |
| |
| // Test that an existing user with an existing pin auth factor can be |
| // authenticated. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePinGenerateKeyStoreState) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| fake_features_.SetDefaultForFeature(Features::kGenerateRecoverableKeyStore, |
| true); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{ |
| .metadata = |
| PinMetadata{ |
| .hash_info = |
| SerializedKnowledgeFactorHashInfo{ |
| .algorithm = SerializedKnowledgeFactorHashAlgorithm:: |
| PBKDF2_AES256_1234, |
| .salt = brillo::Blob(30, 0xAA), |
| .should_generate_key_store = true, |
| }, |
| }, |
| }, |
| AuthBlockState{.state = PinWeaverAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(auth_factor, AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| // Setting cert provider expectation. |
| std::optional<RecoverableKeyStoreBackendCert> backend_cert = |
| GetValidBackendCert(); |
| ASSERT_TRUE(backend_cert.has_value()); |
| EXPECT_CALL(cert_provider_, GetBackendCert).WillOnce(Return(*backend_cert)); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| SerializedUserAuthFactorTypePolicy auth_factor_type_policy( |
| {.type = *SerializeAuthFactorType( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)), |
| .enabled_intents = {}, |
| .disabled_intents = {}}); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| CryptohomeStatusOr<AuthFactor> updated_auth_factor = |
| auth_factor_manager_.LoadAuthFactor( |
| obfuscated_username, auth_factor.type(), auth_factor.label()); |
| ASSERT_THAT(updated_auth_factor, IsOk()); |
| EXPECT_TRUE(updated_auth_factor->auth_block_state() |
| .recoverable_key_store_state.has_value()); |
| } |
| |
| // Test that an existing user with an existing pin auth factor can be |
| // authenticated. |
| TEST_F(AuthSessionWithUssTest, AuthenticatePinUpdateKeyStoreState) { |
| // Setup. |
| const ObfuscatedUsername obfuscated_username = |
| SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| fake_features_.SetDefaultForFeature(Features::kGenerateRecoverableKeyStore, |
| true); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| CryptohomeStatusOr<DecryptedUss> uss = DecryptedUss::CreateWithRandomMainKey( |
| user_uss_storage_, FileSystemKeyset::CreateRandom()); |
| ASSERT_THAT(uss, IsOk()); |
| std::optional<RecoverableKeyStoreState> key_store_state = |
| CreateRecoverableKeyStoreStateWithVersion(kCertListVersion - 1); |
| ASSERT_TRUE(key_store_state.has_value()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{ |
| .metadata = |
| PinMetadata{ |
| .hash_info = |
| SerializedKnowledgeFactorHashInfo{ |
| .algorithm = SerializedKnowledgeFactorHashAlgorithm:: |
| PBKDF2_AES256_1234, |
| .salt = brillo::Blob(30, 0xAA), |
| .should_generate_key_store = true, |
| }, |
| }, |
| }, |
| AuthBlockState{.state = PinWeaverAuthBlockState(), |
| .recoverable_key_store_state = *key_store_state}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactorFile(obfuscated_username, auth_factor) |
| .ok()); |
| AuthFactorMap auth_factor_map; |
| auth_factor_map.Add(auth_factor, AuthFactorStorageType::kUserSecretStash); |
| // 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()); |
| { |
| auto transaction = uss->StartTransaction(); |
| ASSERT_THAT(transaction.InsertWrappedMainKey(kFakePinLabel, *wrapping_key), |
| IsOk()); |
| ASSERT_THAT(std::move(transaction).Commit(), IsOk()); |
| } |
| // Creating the auth session. |
| SetAuthFactorMap(kFakeUsername, std::move(auth_factor_map)); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = true}, |
| backing_apis_); |
| 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_, |
| DeriveKeyBlobsWithAuthBlock(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), |
| std::nullopt); |
| }); |
| // Setting cert provider expectation. |
| std::optional<RecoverableKeyStoreBackendCert> backend_cert = |
| GetValidBackendCert(); |
| ASSERT_TRUE(backend_cert.has_value()); |
| EXPECT_CALL(cert_provider_, GetBackendCert).WillOnce(Return(*backend_cert)); |
| // Calling AuthenticateAuthFactor. |
| std::vector<std::string> auth_factor_labels{kFakePinLabel}; |
| user_data_auth::AuthInput auth_input_proto; |
| auth_input_proto.mutable_pin_input()->set_secret(kFakePin); |
| AuthenticateTestFuture authenticate_future; |
| SerializedUserAuthFactorTypePolicy auth_factor_type_policy( |
| {.type = *SerializeAuthFactorType( |
| *DetermineFactorTypeFromAuthInput(auth_input_proto)), |
| .enabled_intents = {}, |
| .disabled_intents = {}}); |
| auth_session.AuthenticateAuthFactor( |
| ToAuthenticateRequest(auth_factor_labels, auth_input_proto), |
| auth_factor_type_policy, authenticate_future.GetCallback()); |
| |
| // Verify. |
| auto& [action, status] = authenticate_future.Get(); |
| EXPECT_THAT(status, IsOk()); |
| EXPECT_EQ(action.action_type, AuthSession::PostAuthActionType::kNone); |
| EXPECT_THAT( |
| auth_session.authorized_intents(), |
| UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly)); |
| EXPECT_THAT(auth_session.GetAuthForDecrypt(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForVerifyOnly(), NotNull()); |
| EXPECT_THAT(auth_session.GetAuthForWebAuthn(), IsNull()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| CryptohomeStatusOr<AuthFactor> updated_auth_factor = |
| auth_factor_manager_.LoadAuthFactor( |
| obfuscated_username, auth_factor.type(), auth_factor.label()); |
| ASSERT_THAT(updated_auth_factor, IsOk()); |
| EXPECT_NE(updated_auth_factor->auth_block_state() |
| .recoverable_key_store_state->key_store_proto, |
| key_store_state->key_store_proto); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, AddPinCreatesRecoverableKeyStoreState) { |
| // Setup. |
| fake_features_.SetDefaultForFeature(Features::kGenerateRecoverableKeyStore, |
| true); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Test. Adding the PIN factor. |
| user_data_auth::CryptohomeErrorCode error = |
| AddPinAuthFactor(kFakePin, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Verify. The PIN factor is added and the key store state is generated. |
| CryptohomeStatusOr<AuthFactor> pin_auth_factor = |
| auth_factor_manager_.LoadAuthFactor(SanitizeUserName(kFakeUsername), |
| AuthFactorType::kPin, kFakePinLabel); |
| ASSERT_THAT(pin_auth_factor, IsOk()); |
| EXPECT_TRUE(pin_auth_factor->auth_block_state() |
| .recoverable_key_store_state.has_value()); |
| } |
| |
| TEST_F(AuthSessionWithUssTest, AddPasswordCreatesRecoverableKeyStoreState) { |
| // Setup. |
| fake_features_.SetDefaultForFeature(Features::kGenerateRecoverableKeyStore, |
| true); |
| AuthSession auth_session({.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_TRUE(auth_session.has_user_secret_stash()); |
| |
| // Test. Adding the password factor. |
| user_data_auth::CryptohomeErrorCode error = |
| AddPasswordAuthFactor(kFakeLabel, kFakePass, auth_session); |
| EXPECT_EQ(error, user_data_auth::CRYPTOHOME_ERROR_NOT_SET); |
| |
| // Verify. The password factor is added and the key store state is generated. |
| CryptohomeStatusOr<AuthFactor> password_auth_factor = |
| auth_factor_manager_.LoadAuthFactor(SanitizeUserName(kFakeUsername), |
| AuthFactorType::kPassword, |
| kFakeLabel); |
| ASSERT_THAT(password_auth_factor, IsOk()); |
| EXPECT_TRUE(password_auth_factor->auth_block_state() |
| .recoverable_key_store_state.has_value()); |
| } |
| |
| // Test that MigrateLegacyFingerprints succeeds with multiple legacy records. |
| TEST_F(AuthSessionWithUssTest, MigrateLegacyFingerprints) { |
| // set feature flags to allow fp migration. |
| fake_features_.SetDefaultForFeature(Features::kMigrateLegacyFingerprint, |
| true); |
| |
| // Create an AuthSession and add a mock for successful auth block creations. |
| auto auth_session = std::make_unique<AuthSession>( |
| AuthSession::Params{.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session->OnUserCreated().ok()); |
| EXPECT_CALL(hwsec_pw_manager_, InsertRateLimiter) |
| .WillOnce(ReturnValue(/* ret_label */ 0)); |
| |
| LegacyRecord record1{kFakeRecordId, "finger 1"}; |
| LegacyRecord record2{kFakeSecondRecordId, "finger 2"}; |
| std::vector<LegacyRecord> legacy_records = {record1, record2}; |
| const brillo::Blob kNonce(32, 1); |
| ON_CALL(*bio_processor_, ListLegacyRecords(_)) |
| .WillByDefault([legacy_records](auto&& callback) { |
| std::move(callback).Run(legacy_records); |
| }); |
| ON_CALL(*bio_processor_, GetNonce(_)) |
| .WillByDefault( |
| [kNonce](auto&& callback) { std::move(callback).Run(kNonce); }); |
| ON_CALL(*bio_processor_, EnrollLegacyTemplate(_, _, _)) |
| .WillByDefault([](auto&&, auto&&, auto&& callback) { |
| std::move(callback).Run(true); |
| }); |
| ON_CALL(hwsec_pw_manager_, StartBiometricsAuth) |
| .WillByDefault(ReturnValue( |
| hwsec::PinWeaverManagerFrontend::StartBiometricsAuthReply{})); |
| ON_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillByDefault(ReturnValue(AuthBlockType::kFingerprint)); |
| |
| // Expect CreateKeyBlobsWithAuthBlock to be called multiple times: |
| // return auth block states corresponding to each legacy record. |
| { |
| testing::InSequence s; |
| for (auto record : legacy_records) { |
| std::string legacy_record_id = record.legacy_record_id; |
| EXPECT_CALL( |
| auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kFingerprint, _, _, _)) |
| .WillOnce([&, legacy_record_id]( |
| AuthBlockType auth_block_type, |
| const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| AuthBlock::CreateCallback create_callback) { |
| EXPECT_TRUE(auth_input.reset_secret.has_value()); |
| auto key_blobs = std::make_unique<KeyBlobs>(); |
| key_blobs->vkk_key = brillo::SecureBlob("fake vkk key"); |
| key_blobs->reset_secret = auth_input.reset_secret; |
| auto auth_block_state = std::make_unique<AuthBlockState>(); |
| FingerprintAuthBlockState fingerprint_state = |
| FingerprintAuthBlockState(); |
| fingerprint_state.template_id = legacy_record_id; |
| fingerprint_state.gsc_secret_label = kFakeFpLabel; |
| auth_block_state->state = fingerprint_state; |
| std::move(create_callback) |
| .Run(OkStatus<CryptohomeCryptoError>(), std::move(key_blobs), |
| std::move(auth_block_state)); |
| }); |
| } |
| } |
| |
| // Test. |
| TestFuture<CryptohomeStatus> migration_future; |
| auth_session->GetAuthForDecrypt()->MigrateLegacyFingerprints( |
| migration_future.GetCallback()); |
| |
| // Verify. |
| ASSERT_THAT(migration_future.Get(), IsOk()); |
| size_t index = 1; |
| for (auto legacy_record : legacy_records) { |
| auto auth_factor_label = FpMigrationUtility::MigratedLegacyFpLabel(index++); |
| CryptohomeStatusOr<AuthFactor> fp_auth_factor = |
| auth_factor_manager_.LoadAuthFactor(SanitizeUserName(kFakeUsername), |
| AuthFactorType::kFingerprint, |
| auth_factor_label); |
| ASSERT_THAT(fp_auth_factor, IsOk()); |
| // The label in LegacyRecord is the user specified fingerprint |
| // name. Check that it is properly migrated into the common metadata of the |
| // auth factor. |
| ASSERT_EQ(fp_auth_factor->metadata().common.user_specified_name, |
| legacy_record.user_specified_name); |
| auto& fp_metadata = |
| std::get<FingerprintMetadata>(fp_auth_factor->metadata().metadata); |
| ASSERT_NE(fp_metadata.was_migrated, std::nullopt); |
| ASSERT_TRUE(fp_metadata.was_migrated.value()); |
| } |
| auto encrypted_uss = |
| uss_manager_.LoadEncrypted(auth_session->obfuscated_username()); |
| ASSERT_OK(encrypted_uss); |
| ASSERT_EQ((*encrypted_uss)->legacy_fingerprint_migration_rollout(), 1); |
| } |
| |
| // Test that MigrateLegacyFingerprints properly returns error when it fails. |
| TEST_F(AuthSessionWithUssTest, MigrateLegacyFingerprintsAddCredFailure) { |
| // set feature flags to allow fp migration. |
| fake_features_.SetDefaultForFeature(Features::kMigrateLegacyFingerprint, |
| true); |
| |
| // Create an AuthSession and add a mock for a successful auth block prepare. |
| auto auth_session = std::make_unique<AuthSession>( |
| AuthSession::Params{.username = kFakeUsername, |
| .is_ephemeral_user = false, |
| .intent = AuthIntent::kDecrypt, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = false}, |
| backing_apis_); |
| EXPECT_TRUE(auth_session->OnUserCreated().ok()); |
| EXPECT_CALL(hwsec_pw_manager_, InsertRateLimiter) |
| .WillOnce(ReturnValue(/* ret_label */ 0)); |
| |
| LegacyRecord record{kFakeRecordId, "finger 1"}; |
| std::vector<LegacyRecord> legacy_records = {record}; |
| const brillo::Blob kNonce(32, 1); |
| ON_CALL(*bio_processor_, ListLegacyRecords(_)) |
| .WillByDefault([legacy_records](auto&& callback) { |
| std::move(callback).Run(legacy_records); |
| }); |
| ON_CALL(*bio_processor_, GetNonce(_)) |
| .WillByDefault( |
| [kNonce](auto&& callback) { std::move(callback).Run(kNonce); }); |
| ON_CALL(*bio_processor_, EnrollLegacyTemplate(_, _, _)) |
| .WillByDefault([](auto&&, auto&&, auto&& callback) { |
| std::move(callback).Run(true); |
| }); |
| ON_CALL(hwsec_pw_manager_, StartBiometricsAuth) |
| .WillByDefault(ReturnValue( |
| hwsec::PinWeaverManagerFrontend::StartBiometricsAuthReply{})); |
| ON_CALL(auth_block_utility_, SelectAuthBlockTypeForCreation(_)) |
| .WillByDefault(ReturnValue(AuthBlockType::kFingerprint)); |
| |
| // Expect CreateKeyBlobsWithAuthBlock to fail for once and then succeeds. |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthBlock(AuthBlockType::kFingerprint, _, _, _)) |
| .WillOnce([&](AuthBlockType auth_block_type, const AuthInput& auth_input, |
| const AuthFactorMetadata& auth_factor_metadata, |
| AuthBlock::CreateCallback create_callback) { |
| std::move(create_callback) |
| .Run(MakeStatus<error::CryptohomeError>( |
| kErrorLocationForTestingAuthSession, |
| error::ErrorActionSet( |
| {error::PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT), |
| nullptr, nullptr); |
| }); |
| |
| // Test. |
| TestFuture<CryptohomeStatus> migration_future; |
| auth_session->GetAuthForDecrypt()->MigrateLegacyFingerprints( |
| migration_future.GetCallback()); |
| |
| // Verify the expected failure of the migration. |
| ASSERT_THAT(migration_future.Get(), NotOk()); |
| ASSERT_EQ(migration_future.Get()->local_legacy_error(), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| |
| } // namespace cryptohome |