| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Unit tests for AuthSession. |
| |
| #include "cryptohome/auth_session.h" |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include <base/callback_helpers.h> |
| #include <base/test/bind.h> |
| #include <base/test/task_environment.h> |
| #include <base/timer/mock_timer.h> |
| #include <brillo/cryptohome.h> |
| #include <cryptohome/proto_bindings/auth_factor.pb.h> |
| #include <cryptohome/proto_bindings/UserDataAuth.pb.h> |
| #include <gtest/gtest.h> |
| #include <libhwsec-foundation/crypto/aes.h> |
| |
| #include "cryptohome/auth_blocks/auth_block_state.h" |
| #include "cryptohome/auth_blocks/auth_block_utility_impl.h" |
| #include "cryptohome/auth_blocks/mock_auth_block_utility.h" |
| #include "cryptohome/auth_factor/auth_factor.h" |
| #include "cryptohome/auth_factor/auth_factor_manager.h" |
| #include "cryptohome/auth_factor/auth_factor_metadata.h" |
| #include "cryptohome/auth_factor/auth_factor_type.h" |
| #include "cryptohome/crypto_error.h" |
| #include "cryptohome/cryptohome_common.h" |
| #include "cryptohome/key_objects.h" |
| #include "cryptohome/mock_crypto.h" |
| #include "cryptohome/mock_keyset_management.h" |
| #include "cryptohome/mock_platform.h" |
| #include "cryptohome/user_secret_stash.h" |
| #include "cryptohome/user_secret_stash_storage.h" |
| |
| using brillo::cryptohome::home::SanitizeUserName; |
| using cryptohome::error::CryptohomeCryptoError; |
| using cryptohome::error::CryptohomeError; |
| using cryptohome::error::CryptohomeMountError; |
| using hwsec_foundation::status::OkStatus; |
| using hwsec_foundation::status::StatusChain; |
| using ::testing::_; |
| using ::testing::ByMove; |
| using ::testing::ElementsAre; |
| using ::testing::NiceMock; |
| using ::testing::Pair; |
| using ::testing::Return; |
| |
| namespace { |
| // Fake labels to be in used in this test suite. |
| constexpr char kFakeLabel[] = "test_label"; |
| constexpr char kFakeOtherLabel[] = "test_other_label"; |
| constexpr char kFakePinLabel[] = "test_pin_label"; |
| // Fake passwords to be in used in this test suite. |
| constexpr char kFakePass[] = "test_pass"; |
| constexpr char kFakePin[] = "123456"; |
| constexpr char kFakeOtherPass[] = "test_other_pass"; |
| // Fake username to be used in this test suite. |
| constexpr char kFakeUsername[] = "test_username"; |
| |
| } // namespace |
| |
| namespace cryptohome { |
| |
| class AuthSessionTest : public ::testing::Test { |
| public: |
| AuthSessionTest() = default; |
| AuthSessionTest(const AuthSessionTest&) = delete; |
| AuthSessionTest& operator=(const AuthSessionTest&) = delete; |
| ~AuthSessionTest() override = default; |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| // Mock and fake objects, will be passed to AuthSession for its internal use. |
| NiceMock<MockCrypto> crypto_; |
| NiceMock<MockPlatform> platform_; |
| NiceMock<MockKeysetManagement> keyset_management_; |
| NiceMock<MockAuthBlockUtility> auth_block_utility_; |
| AuthFactorManager auth_factor_manager_{&platform_}; |
| UserSecretStashStorage user_secret_stash_storage_{&platform_}; |
| }; |
| |
| TEST_F(AuthSessionTest, Username) { |
| AuthSession auth_session( |
| kFakeUsername, user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE, |
| /*on_timeout=*/base::DoNothing(), &crypto_, &keyset_management_, |
| &auth_block_utility_, &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| EXPECT_EQ(auth_session.username(), kFakeUsername); |
| EXPECT_EQ(auth_session.obfuscated_username(), |
| SanitizeUserName(kFakeUsername)); |
| } |
| |
| TEST_F(AuthSessionTest, TimeoutTest) { |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool* called, const base::UnguessableToken&) { *called = true; }, |
| base::Unretained(&called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| EXPECT_EQ(auth_session.GetStatus(), |
| AuthStatus::kAuthStatusFurtherFactorRequired); |
| auth_session.SetAuthSessionAsAuthenticated(); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| auth_session.timer_.FireNow(); |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusTimedOut); |
| EXPECT_TRUE(called); |
| } |
| |
| TEST_F(AuthSessionTest, SerializedStringFromNullToken) { |
| base::UnguessableToken token = base::UnguessableToken::Null(); |
| std::optional<std::string> serialized_token = |
| AuthSession::GetSerializedStringFromToken(token); |
| EXPECT_FALSE(serialized_token.has_value()); |
| } |
| |
| TEST_F(AuthSessionTest, TokenFromEmptyString) { |
| std::string serialized_string = ""; |
| std::optional<base::UnguessableToken> unguessable_token = |
| AuthSession::GetTokenFromSerializedString(serialized_string); |
| EXPECT_FALSE(unguessable_token.has_value()); |
| } |
| |
| TEST_F(AuthSessionTest, TokenFromUnexpectedSize) { |
| std::string serialized_string = "unexpected_sized_string"; |
| std::optional<base::UnguessableToken> unguessable_token = |
| AuthSession::GetTokenFromSerializedString(serialized_string); |
| EXPECT_FALSE(unguessable_token.has_value()); |
| } |
| |
| TEST_F(AuthSessionTest, TokenFromString) { |
| base::UnguessableToken original_token = base::UnguessableToken::Create(); |
| std::optional<std::string> serialized_token = |
| AuthSession::GetSerializedStringFromToken(original_token); |
| EXPECT_TRUE(serialized_token.has_value()); |
| std::optional<base::UnguessableToken> deserialized_token = |
| AuthSession::GetTokenFromSerializedString(serialized_token.value()); |
| EXPECT_TRUE(deserialized_token.has_value()); |
| EXPECT_EQ(deserialized_token.value(), original_token); |
| } |
| |
| // This test check AuthSession::GetCredential function for a regular user and |
| // ensures that the fields are set as they should be. |
| TEST_F(AuthSessionTest, GetCredentialRegularUser) { |
| // SETUP |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool* called, const base::UnguessableToken&) { *called = true; }, |
| base::Unretained(&called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| EXPECT_EQ(auth_session.GetStatus(), |
| AuthStatus::kAuthStatusFurtherFactorRequired); |
| |
| // TEST |
| EXPECT_FALSE(called); |
| cryptohome::AuthorizationRequest authorization_request; |
| authorization_request.mutable_key()->set_secret(kFakePass); |
| authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel); |
| MountStatusOr<std::unique_ptr<Credentials>> test_creds = |
| auth_session.GetCredentials(authorization_request); |
| ASSERT_TRUE(test_creds.ok()); |
| |
| // VERIFY |
| // SerializeToString is used in the absence of a comparator for KeyData |
| // protobuf. |
| std::string key_data_serialized1 = "1"; |
| std::string key_data_serialized2 = "2"; |
| test_creds.value()->key_data().SerializeToString(&key_data_serialized1); |
| authorization_request.mutable_key()->data().SerializeToString( |
| &key_data_serialized2); |
| EXPECT_EQ(key_data_serialized1, key_data_serialized2); |
| } |
| |
| // This test check AuthSession::GetCredential function for a kiosk user and |
| // ensures that the fields are set as they should be. |
| TEST_F(AuthSessionTest, GetCredentialKioskUser) { |
| // SETUP |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool* called, const base::UnguessableToken&) { *called = true; }, |
| base::Unretained(&called)); |
| // SecureBlob for kFakePass above |
| const brillo::SecureBlob fake_pass_blob( |
| brillo::BlobFromString(kFakeUsername)); |
| |
| AuthSession auth_session(kFakeUsername, 0, std::move(on_timeout), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| EXPECT_CALL(keyset_management_, GetPublicMountPassKey(_)) |
| .WillOnce(Return(ByMove(fake_pass_blob))); |
| EXPECT_EQ(auth_session.GetStatus(), |
| AuthStatus::kAuthStatusFurtherFactorRequired); |
| |
| // TEST |
| EXPECT_FALSE(called); |
| cryptohome::AuthorizationRequest authorization_request; |
| authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel); |
| authorization_request.mutable_key()->mutable_data()->set_type( |
| KeyData::KEY_TYPE_KIOSK); |
| MountStatusOr<std::unique_ptr<Credentials>> test_creds = |
| auth_session.GetCredentials(authorization_request); |
| ASSERT_TRUE(test_creds.ok()); |
| |
| // VERIFY |
| // SerializeToString is used in the absence of a comparator for KeyData |
| // protobuf. |
| std::string key_data_serialized1 = "1"; |
| std::string key_data_serialized2 = "2"; |
| test_creds.value()->key_data().SerializeToString(&key_data_serialized1); |
| authorization_request.mutable_key()->data().SerializeToString( |
| &key_data_serialized2); |
| EXPECT_EQ(key_data_serialized1, key_data_serialized2); |
| EXPECT_EQ(test_creds.value()->passkey(), fake_pass_blob); |
| } |
| |
| // Test if AuthSession correctly adds new credentials for a new user. |
| TEST_F(AuthSessionTest, AddCredentialNewUser) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| // For AuthSession::AddInitialKeyset/AddKeyset callback to properly |
| // execute auth_block_utility_ cannot be a mock |
| std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ = |
| std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_, |
| &platform_); |
| // Setting the expectation that the user does not exist. |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, |
| auth_block_utility_impl_.get(), |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired, |
| auth_session.GetStatus()); |
| EXPECT_FALSE(auth_session.user_exists()); |
| |
| user_data_auth::AddCredentialsRequest add_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| add_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| |
| EXPECT_CALL(keyset_management_, |
| AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _)) |
| .WillOnce(Return(ByMove(std::make_unique<VaultKeyset>()))); |
| |
| base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)> on_done = |
| base::BindLambdaForTesting( |
| [](const user_data_auth::AddCredentialsReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error()); |
| }); |
| |
| // Verify. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| auth_session.AddCredentials(add_cred_request, std::move(on_done)); |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| } |
| |
| // Test if AuthSession correctly adds new credentials for a new user, even when |
| // called twice. The first credential gets added as an initial keyset, and the |
| // second as a regular one. |
| TEST_F(AuthSessionTest, AddCredentialNewUserTwice) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // For AuthSession::AddInitialKeyset/AddKeyset callback to properly |
| // execute auth_block_utility_ cannot be a mock |
| std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ = |
| std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_, |
| &platform_); |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, auth_block_utility_impl_.get(), |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)> on_done = |
| base::BindLambdaForTesting( |
| [](const user_data_auth::AddCredentialsReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error()); |
| }); |
| |
| // Test adding the first credential. |
| EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired, |
| auth_session.GetStatus()); |
| EXPECT_FALSE(auth_session.user_exists()); |
| |
| user_data_auth::AddCredentialsRequest add_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| add_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| |
| EXPECT_CALL(keyset_management_, |
| AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _)) |
| .WillOnce(Return(ByMove(std::make_unique<VaultKeyset>()))); |
| |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| auth_session.AddCredentials(add_cred_request, std::move(on_done)); |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| // Test adding the second credential. |
| // Set up expectation in callback for success. |
| base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)> |
| other_on_done = base::BindLambdaForTesting( |
| [](const user_data_auth::AddCredentialsReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error()); |
| }); |
| user_data_auth::AddCredentialsRequest add_other_cred_request; |
| cryptohome::AuthorizationRequest* other_authorization_request = |
| add_other_cred_request.mutable_authorization(); |
| other_authorization_request->mutable_key()->set_secret(kFakeOtherPass); |
| other_authorization_request->mutable_key()->mutable_data()->set_label( |
| kFakeOtherLabel); |
| |
| EXPECT_CALL(keyset_management_, AddKeysetWithKeyBlobs(_, _, _, _, _, _)) |
| .WillOnce(Return(CRYPTOHOME_ERROR_NOT_SET)); |
| auth_session.AddCredentials(add_other_cred_request, std::move(other_on_done)); |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| } |
| |
| // Test if AuthSession correctly authenticates existing credentials for a |
| // user. |
| TEST_F(AuthSessionTest, AuthenticateExistingUser) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(keyset_management_, GetVaultKeysetLabelsAndData(_, _)); |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_THAT(AuthStatus::kAuthStatusFurtherFactorRequired, |
| auth_session.GetStatus()); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| cryptohome::AuthorizationRequest authorization_request; |
| authorization_request.mutable_key()->set_secret(kFakePass); |
| authorization_request.mutable_key()->mutable_data()->set_label(kFakeLabel); |
| |
| auto vk = std::make_unique<VaultKeyset>(); |
| EXPECT_CALL(keyset_management_, GetValidKeyset(_, _)) |
| .WillOnce(Return(ByMove(std::move(vk)))); |
| EXPECT_CALL(keyset_management_, ReSaveKeysetIfNeeded(_, _)) |
| .WillOnce(Return(true)); |
| |
| // Verify. |
| EXPECT_TRUE(auth_session.Authenticate(authorization_request).ok()); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| |
| EXPECT_EQ(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus()); |
| EXPECT_TRUE(auth_session.TakeCredentialVerifier()->Verify( |
| brillo::SecureBlob(kFakePass))); |
| |
| // Cleanup. |
| auth_session.timer_.FireNow(); |
| EXPECT_THAT(AuthStatus::kAuthStatusTimedOut, auth_session.GetStatus()); |
| EXPECT_TRUE(called); |
| } |
| |
| // Test if AuthSession::Addcredentials skips adding/saving credential to disk |
| // for an ephemeral user. |
| TEST_F(AuthSessionTest, AddCredentialNewEphemeralUser) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = |
| user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_THAT(AuthStatus::kAuthStatusAuthenticated, auth_session.GetStatus()); |
| EXPECT_FALSE(auth_session.user_exists()); |
| ASSERT_TRUE(auth_session.timer_.IsRunning()); |
| |
| user_data_auth::AddCredentialsRequest add_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| add_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| |
| EXPECT_CALL(keyset_management_, |
| AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _)) |
| .Times(0); |
| |
| base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)> on_done = |
| base::BindLambdaForTesting( |
| [](const user_data_auth::AddCredentialsReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error()); |
| }); |
| |
| // Verify. |
| auth_session.AddCredentials(add_cred_request, std::move(on_done)); |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| } |
| |
| // Test if AuthSession correctly updates existing credentials for a new user. |
| TEST_F(AuthSessionTest, UpdateCredentialUnauthenticatedAuthSession) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| user_data_auth::UpdateCredentialRequest update_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| update_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| update_cred_request.set_old_credential_label(kFakeLabel); |
| |
| // Test. |
| base::OnceCallback<void(const user_data_auth::UpdateCredentialReply&)> |
| on_done = base::BindLambdaForTesting( |
| [](const user_data_auth::UpdateCredentialReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ( |
| user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION, |
| reply.error()); |
| }); |
| auth_session.UpdateCredential(update_cred_request, std::move(on_done)); |
| } |
| |
| // Test if AuthSession correctly updates existing credentials for a new user. |
| TEST_F(AuthSessionTest, UpdateCredentialSuccess) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // For AuthSession::UpdateKeyset callback to properly |
| // execute auth_block_utility_ cannot be a mock |
| std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_ = |
| std::make_unique<AuthBlockUtilityImpl>(&keyset_management_, &crypto_, |
| &platform_); |
| |
| // Setting the expectation that the user does exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, |
| auth_block_utility_impl_.get(), |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| auth_session.SetStatus(AuthStatus::kAuthStatusAuthenticated); |
| user_data_auth::UpdateCredentialRequest update_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| update_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| update_cred_request.set_old_credential_label(kFakeLabel); |
| |
| // Test. |
| EXPECT_CALL(keyset_management_, UpdateKeysetWithKeyBlobs(_, _, _, _, _)) |
| .WillOnce(Return(CRYPTOHOME_ERROR_NOT_SET)); |
| base::OnceCallback<void(const user_data_auth::UpdateCredentialReply&)> |
| on_done = base::BindLambdaForTesting( |
| [](const user_data_auth::UpdateCredentialReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET, reply.error()); |
| }); |
| auth_session.UpdateCredential(update_cred_request, std::move(on_done)); |
| } |
| // Test if AuthSession correctly updates existing credentials for a new user. |
| TEST_F(AuthSessionTest, UpdateCredentialInvalidLabel) { |
| // Setup. |
| bool called = false; |
| auto on_timeout = base::BindOnce( |
| [](bool& called, const base::UnguessableToken&) { called = true; }, |
| std::ref(called)); |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| AuthSession auth_session(kFakeUsername, flags, std::move(on_timeout), |
| &crypto_, &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| user_data_auth::UpdateCredentialRequest update_cred_request; |
| cryptohome::AuthorizationRequest* authorization_request = |
| update_cred_request.mutable_authorization(); |
| authorization_request->mutable_key()->set_secret(kFakePass); |
| authorization_request->mutable_key()->mutable_data()->set_label(kFakeLabel); |
| update_cred_request.set_old_credential_label("wrong-label"); |
| |
| // Test. |
| base::OnceCallback<void(const user_data_auth::UpdateCredentialReply&)> |
| on_done = base::BindLambdaForTesting( |
| [](const user_data_auth::UpdateCredentialReply& reply) { |
| // Evaluate error returned by callback. |
| EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT, |
| reply.error()); |
| }); |
| auth_session.UpdateCredential(update_cred_request, std::move(on_done)); |
| } |
| |
| // Test that the UserSecretStash isn't created by default when a new user is |
| // created. |
| TEST_F(AuthSessionTest, NoUssByDefault) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| |
| // Verify. |
| EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| } |
| |
| // A variant of the auth session test that has the UserSecretStash experiment |
| // enabled. |
| class AuthSessionWithUssExperimentTest : public AuthSessionTest { |
| protected: |
| AuthSessionWithUssExperimentTest() { |
| SetUserSecretStashExperimentForTesting(/*enabled=*/true); |
| } |
| |
| ~AuthSessionWithUssExperimentTest() { |
| // Reset this global variable to avoid affecting unrelated test cases. |
| SetUserSecretStashExperimentForTesting(/*enabled=*/std::nullopt); |
| } |
| }; |
| |
| // Test that the UserSecretStash is created on the user creation, in case the |
| // UserSecretStash experiment is on. |
| TEST_F(AuthSessionWithUssExperimentTest, UssCreation) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| |
| // Verify. |
| EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| } |
| |
| // Test that no UserSecretStash is created for an ephemeral user. |
| TEST_F(AuthSessionWithUssExperimentTest, NoUssForEphemeral) { |
| // Setup. |
| int flags = |
| user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| // Test. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| |
| // Verify. |
| EXPECT_EQ(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_EQ(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| } |
| |
| // Test that a new auth factor can be added to the newly created user, in case |
| // the UserSecretStash experiment is on. |
| TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAuthFactorViaUss) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will create key blobs. |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthFactorType(AuthFactorType::kPassword, |
| /*auth_input=*/_, |
| /*out_auth_block_state=*/_, |
| /*out_key_blobs=*/_)) |
| .WillOnce([](AuthFactorType auth_factor_type, const AuthInput& auth_input, |
| AuthBlockState& out_auth_block_state, |
| KeyBlobs& out_key_blobs) { |
| // An arbitrary auth block state type can be used in this test. |
| out_auth_block_state.state = TpmBoundToPcrAuthBlockState(); |
| out_key_blobs.vkk_key = brillo::SecureBlob("fake vkk key"); |
| return OkStatus<CryptohomeCryptoError>(); |
| }); |
| // 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); |
| EXPECT_TRUE(auth_session.AddAuthFactor(request).ok()); |
| |
| // Verify. |
| std::map<std::string, AuthFactorType> stored_factors = |
| auth_factor_manager_.ListAuthFactors(SanitizeUserName(kFakeUsername)); |
| EXPECT_THAT(stored_factors, |
| ElementsAre(Pair(kFakeLabel, AuthFactorType::kPassword))); |
| EXPECT_NE(auth_session.label_to_auth_factor_.find(kFakeLabel), |
| auth_session.label_to_auth_factor_.end()); |
| } |
| |
| // Test that a new auth factor cannot be added for an unauthenticated |
| // authsession. |
| TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAuthFactorUnAuthenticated) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| |
| user_data_auth::AddAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| request.mutable_auth_factor()->set_label(kFakeLabel); |
| request.mutable_auth_factor()->mutable_password_metadata(); |
| request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass); |
| |
| // Test and Verify. |
| CryptohomeStatus status = auth_session.AddAuthFactor(request); |
| ASSERT_FALSE(status.ok()); |
| EXPECT_EQ(status->local_legacy_error().value(), |
| user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION); |
| } |
| |
| // Test that a new auth factor and a pin can be added to the newly created user, |
| // in case the UserSecretStash experiment is on. |
| TEST_F(AuthSessionWithUssExperimentTest, AddPasswordAndPinAuthFactorViaUss) { |
| // Setup. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| // Setting the expectation that the user does not exist. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(false)); |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| // Creating the user. |
| EXPECT_TRUE(auth_session.OnUserCreated().ok()); |
| EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| // Add a password first. |
| // Setting the expectation that the auth block utility will create key blobs. |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthFactorType(AuthFactorType::kPassword, |
| /*auth_input=*/_, |
| /*out_auth_block_state=*/_, |
| /*out_key_blobs=*/_)) |
| .WillOnce([](AuthFactorType auth_factor_type, const AuthInput& auth_input, |
| AuthBlockState& out_auth_block_state, |
| KeyBlobs& out_key_blobs) { |
| // An arbitrary auth block state type can be used in this test. |
| out_auth_block_state.state = TpmBoundToPcrAuthBlockState(); |
| out_key_blobs.vkk_key = brillo::SecureBlob("fake vkk key"); |
| return OkStatus<CryptohomeCryptoError>(); |
| }); |
| // 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); |
| EXPECT_TRUE(auth_session.AddAuthFactor(request).ok()); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will create key blobs. |
| EXPECT_CALL(auth_block_utility_, |
| CreateKeyBlobsWithAuthFactorType(AuthFactorType::kPin, |
| /*auth_input=*/_, |
| /*out_auth_block_state=*/_, |
| /*out_key_blobs=*/_)) |
| .WillOnce([](AuthFactorType auth_factor_type, const AuthInput& auth_input, |
| AuthBlockState& out_auth_block_state, |
| KeyBlobs& out_key_blobs) { |
| // An arbitrary auth block state type can be used in this test. |
| out_auth_block_state.state = PinWeaverAuthBlockState(); |
| out_key_blobs.vkk_key = brillo::SecureBlob("fake vkk key"); |
| return OkStatus<CryptohomeCryptoError>(); |
| }); |
| // Calling AddAuthFactor. |
| user_data_auth::AddAuthFactorRequest add_pin_request; |
| add_pin_request.set_auth_session_id(auth_session.serialized_token()); |
| add_pin_request.mutable_auth_factor()->set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_PIN); |
| add_pin_request.mutable_auth_factor()->set_label(kFakePinLabel); |
| add_pin_request.mutable_auth_factor()->mutable_pin_metadata(); |
| add_pin_request.mutable_auth_input()->mutable_pin_input()->set_secret( |
| kFakePin); |
| EXPECT_TRUE(auth_session.AddAuthFactor(add_pin_request).ok()); |
| |
| // Verify. |
| 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))); |
| } |
| |
| TEST_F(AuthSessionWithUssExperimentTest, AuthenticatePasswordAuthFactorViaUss) { |
| // Setup. |
| const std::string obfuscated_username = SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| std::unique_ptr<UserSecretStash> uss = |
| UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom()); |
| ASSERT_NE(uss, nullptr); |
| std::optional<brillo::SecureBlob> uss_main_key = |
| UserSecretStash::CreateRandomMainKey(); |
| ASSERT_TRUE(uss_main_key.has_value()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPassword, kFakeLabel, |
| AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()}, |
| AuthBlockState{.state = TpmBoundToPcrAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor) |
| .ok()); |
| // Adding the auth factor into the USS and persisting the latter. |
| const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret}; |
| std::optional<brillo::SecureBlob> wrapping_key = |
| key_blobs.DeriveUssCredentialSecret(); |
| ASSERT_TRUE(wrapping_key.has_value()); |
| EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakeLabel, |
| wrapping_key.value())); |
| std::optional<brillo::SecureBlob> encrypted_uss = |
| uss->GetEncryptedContainer(uss_main_key.value()); |
| ASSERT_TRUE(encrypted_uss.has_value()); |
| EXPECT_TRUE(user_secret_stash_storage_.Persist(encrypted_uss.value(), |
| obfuscated_username)); |
| // Creating the auth session. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will derive key blobs. |
| EXPECT_CALL(auth_block_utility_, DeriveKeyBlobs(_, _, _)) |
| .WillOnce([&](const AuthInput& auth_input, |
| const AuthBlockState& auth_block_state, |
| KeyBlobs& out_key_blobs) { |
| out_key_blobs.vkk_key = kFakePerCredentialSecret; |
| return OkStatus<CryptohomeCryptoError>(); |
| }); |
| // Calling AuthenticateAuthFactor. |
| user_data_auth::AuthenticateAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakeLabel); |
| request.mutable_auth_input()->mutable_password_input()->set_secret(kFakePass); |
| bool called = false; |
| user_data_auth::CryptohomeErrorCode error = |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| EXPECT_TRUE(auth_session.AuthenticateAuthFactor( |
| request, |
| base::BindOnce( |
| [](bool& called, user_data_auth::CryptohomeErrorCode& error, |
| const user_data_auth::AuthenticateAuthFactorReply& reply) { |
| called = true; |
| error = reply.error(); |
| }, |
| std::ref(called), std::ref(error)))); |
| |
| // Verify. |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| } |
| |
| TEST_F(AuthSessionWithUssExperimentTest, AuthenticatePinAuthFactorViaUss) { |
| // Setup. |
| const std::string obfuscated_username = SanitizeUserName(kFakeUsername); |
| const brillo::SecureBlob kFakePerCredentialSecret("fake-vkk"); |
| // Setting the expectation that the user exists. |
| EXPECT_CALL(keyset_management_, UserExists(_)).WillRepeatedly(Return(true)); |
| // Generating the USS. |
| std::unique_ptr<UserSecretStash> uss = |
| UserSecretStash::CreateRandom(FileSystemKeyset::CreateRandom()); |
| ASSERT_NE(uss, nullptr); |
| std::optional<brillo::SecureBlob> uss_main_key = |
| UserSecretStash::CreateRandomMainKey(); |
| ASSERT_TRUE(uss_main_key.has_value()); |
| // Creating the auth factor. An arbitrary auth block state is used in this |
| // test. |
| AuthFactor auth_factor( |
| AuthFactorType::kPin, kFakePinLabel, |
| AuthFactorMetadata{.metadata = PinAuthFactorMetadata()}, |
| AuthBlockState{.state = PinWeaverAuthBlockState()}); |
| EXPECT_TRUE( |
| auth_factor_manager_.SaveAuthFactor(obfuscated_username, auth_factor) |
| .ok()); |
| // Adding the auth factor into the USS and persisting the latter. |
| const KeyBlobs key_blobs = {.vkk_key = kFakePerCredentialSecret}; |
| std::optional<brillo::SecureBlob> wrapping_key = |
| key_blobs.DeriveUssCredentialSecret(); |
| ASSERT_TRUE(wrapping_key.has_value()); |
| EXPECT_TRUE(uss->AddWrappedMainKey(uss_main_key.value(), kFakePinLabel, |
| wrapping_key.value())); |
| std::optional<brillo::SecureBlob> encrypted_uss = |
| uss->GetEncryptedContainer(uss_main_key.value()); |
| ASSERT_TRUE(encrypted_uss.has_value()); |
| EXPECT_TRUE(user_secret_stash_storage_.Persist(encrypted_uss.value(), |
| obfuscated_username)); |
| // Creating the auth session. |
| int flags = user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE; |
| AuthSession auth_session(kFakeUsername, flags, |
| /*on_timeout=*/base::DoNothing(), &crypto_, |
| &keyset_management_, &auth_block_utility_, |
| &auth_factor_manager_, &user_secret_stash_storage_); |
| EXPECT_TRUE(auth_session.user_exists()); |
| |
| // Test. |
| // Setting the expectation that the auth block utility will derive key blobs. |
| EXPECT_CALL(auth_block_utility_, DeriveKeyBlobs(_, _, _)) |
| .WillOnce([&](const AuthInput& auth_input, |
| const AuthBlockState& auth_block_state, |
| KeyBlobs& out_key_blobs) { |
| out_key_blobs.vkk_key = kFakePerCredentialSecret; |
| return OkStatus<CryptohomeCryptoError>(); |
| }); |
| // Calling AuthenticateAuthFactor. |
| user_data_auth::AuthenticateAuthFactorRequest request; |
| request.set_auth_session_id(auth_session.serialized_token()); |
| request.set_auth_factor_label(kFakePinLabel); |
| request.mutable_auth_input()->mutable_pin_input()->set_secret(kFakePin); |
| bool called = false; |
| user_data_auth::CryptohomeErrorCode error = |
| user_data_auth::CRYPTOHOME_ERROR_NOT_SET; |
| EXPECT_TRUE(auth_session.AuthenticateAuthFactor( |
| request, |
| base::BindOnce( |
| [](bool& called, user_data_auth::CryptohomeErrorCode& error, |
| const user_data_auth::AuthenticateAuthFactorReply& reply) { |
| called = true; |
| error = reply.error(); |
| }, |
| std::ref(called), std::ref(error)))); |
| |
| // Verify. |
| EXPECT_EQ(auth_session.GetStatus(), AuthStatus::kAuthStatusAuthenticated); |
| EXPECT_NE(auth_session.user_secret_stash_for_testing(), nullptr); |
| EXPECT_NE(auth_session.user_secret_stash_main_key_for_testing(), |
| std::nullopt); |
| } |
| |
| } // namespace cryptohome |