| // Copyright 2022 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "cryptohome/auth_blocks/revocation.h" | |
| #include <string> | |
| #include <brillo/secure_blob.h> | |
| #include <gtest/gtest.h> | |
| #include <libhwsec/backend/pinweaver_manager/pinweaver_manager.h> | |
| #include <libhwsec/error/pinweaver_error.h> | |
| #include <libhwsec/error/tpm_error.h> | |
| #include <libhwsec/error/tpm_retry_action.h> | |
| #include <libhwsec/frontend/pinweaver_manager/mock_frontend.h> | |
| #include <libhwsec-foundation/error/testing_helper.h> | |
| using cryptohome::error::CryptohomeError; | |
| using cryptohome::error::ErrorActionSet; | |
| using cryptohome::error::PossibleAction; | |
| using cryptohome::error::PrimaryAction; | |
| using hwsec::TPMError; | |
| using hwsec::TPMRetryAction; | |
| 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 testing::_; | |
| using testing::DoAll; | |
| using testing::NiceMock; | |
| using testing::Return; | |
| using testing::SaveArg; | |
| using testing::SetArgPointee; | |
| namespace cryptohome { | |
| namespace revocation { | |
| namespace { | |
| const char kFakePerCredentialSecret[] = "fake per-credential secret"; | |
| const char kFakeHESecret[] = "fake high entropy secret"; | |
| } // namespace | |
| TEST(RevocationTest, Create) { | |
| brillo::SecureBlob per_credential_secret(kFakePerCredentialSecret); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state; | |
| KeyBlobs key_blobs = {.vkk_key = per_credential_secret}; | |
| EXPECT_CALL(hwsec_pw_manager, InsertCredential(_, _, _, _, _, _)) | |
| .WillOnce(ReturnValue(/* ret_label */ 0)); | |
| ASSERT_THAT(Create(&hwsec_pw_manager, &state, &key_blobs), IsOk()); | |
| } | |
| TEST(RevocationTest, Derive) { | |
| brillo::SecureBlob he_secret(kFakeHESecret); | |
| brillo::SecureBlob per_credential_secret(kFakePerCredentialSecret); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| KeyBlobs key_blobs = {.vkk_key = per_credential_secret}; | |
| EXPECT_CALL(hwsec_pw_manager, CheckCredential(_, _)) | |
| .WillOnce(ReturnValue(hwsec::PinWeaverManager::CheckCredentialReply{ | |
| .he_secret = he_secret})); | |
| ASSERT_THAT(Derive(&hwsec_pw_manager, state, &key_blobs), IsOk()); | |
| } | |
| TEST(RevocationTest, DeriveFailsWithoutLabel) { | |
| brillo::SecureBlob per_credential_secret(kFakePerCredentialSecret); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| KeyBlobs key_blobs = {.vkk_key = per_credential_secret}; | |
| RevocationState state; | |
| auto status = Derive(&hwsec_pw_manager, state, &key_blobs); | |
| ASSERT_THAT(status, NotOk()); | |
| EXPECT_EQ(status->local_crypto_error(), CryptoError::CE_OTHER_CRYPTO); | |
| } | |
| TEST(RevocationTest, Revoke) { | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| uint64_t label; | |
| EXPECT_CALL(hwsec_pw_manager, RemoveCredential(_)) | |
| .WillOnce(DoAll(SaveArg<0>(&label), ReturnOk<hwsec::PinWeaverError>())); | |
| ASSERT_THAT( | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state), | |
| IsOk()); | |
| EXPECT_EQ(label, state.le_label.value()); | |
| } | |
| TEST(RevocationTest, RevokeFailsWithoutLabel) { | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state; | |
| auto status = | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state); | |
| ASSERT_THAT(status, NotOk()); | |
| EXPECT_EQ(status->local_crypto_error(), CryptoError::CE_OTHER_CRYPTO); | |
| } | |
| TEST(RevocationTest, RevokeSucceedsWithTPMRetryActionkNoRetry) { | |
| const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 = | |
| CryptohomeError::ErrorLocationPair( | |
| static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1), | |
| std::string("Testing1")); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| uint64_t label; | |
| EXPECT_CALL(hwsec_pw_manager, RemoveCredential(_)) | |
| .WillOnce(DoAll(SaveArg<0>(&label), | |
| ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry))); | |
| // Revoke succeeds after TPMRetryAction::kNoRetry). | |
| ASSERT_THAT( | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state), | |
| IsOk()); | |
| EXPECT_EQ(label, state.le_label.value()); | |
| } | |
| TEST(RevocationTest, RevokeSucceedsWithTPMRetryActionkSpaceNotFound) { | |
| const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 = | |
| CryptohomeError::ErrorLocationPair( | |
| static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1), | |
| std::string("Testing1")); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| uint64_t label; | |
| EXPECT_CALL(hwsec_pw_manager, RemoveCredential(_)) | |
| .WillOnce( | |
| DoAll(SaveArg<0>(&label), | |
| ReturnError<TPMError>("fake", TPMRetryAction::kSpaceNotFound))); | |
| // Revoke succeeds after TPMRetryAction::kSpaceNotFound. | |
| ASSERT_THAT( | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state), | |
| IsOk()); | |
| EXPECT_EQ(label, state.le_label.value()); | |
| } | |
| TEST(RevocationTest, RevokeFailsWithTPMRetryActionkReboot) { | |
| const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 = | |
| CryptohomeError::ErrorLocationPair( | |
| static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1), | |
| std::string("Testing1")); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| uint64_t label; | |
| EXPECT_CALL(hwsec_pw_manager, RemoveCredential(_)) | |
| .WillOnce(DoAll(SaveArg<0>(&label), | |
| ReturnError<TPMError>("fake", TPMRetryAction::kReboot))); | |
| // Revoke fails after TPMRetryAction::kReboot. | |
| auto status = | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state); | |
| ASSERT_THAT(status, NotOk()); | |
| EXPECT_EQ(status->local_crypto_error(), CryptoError::CE_OTHER_CRYPTO); | |
| } | |
| TEST(RevocationTest, RevokeFailsWithTPMRetryActionkUserAuth) { | |
| const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 = | |
| CryptohomeError::ErrorLocationPair( | |
| static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1), | |
| std::string("Testing1")); | |
| NiceMock<hwsec::MockPinWeaverManagerFrontend> hwsec_pw_manager; | |
| RevocationState state = {.le_label = 0}; | |
| uint64_t label; | |
| EXPECT_CALL(hwsec_pw_manager, RemoveCredential(_)) | |
| .WillOnce( | |
| DoAll(SaveArg<0>(&label), | |
| ReturnError<TPMError>("fake", TPMRetryAction::kUserAuth))); | |
| // Revoke fails after TPMRetryAction::kUserAuth. | |
| auto status = | |
| Revoke(AuthBlockType::kCryptohomeRecovery, &hwsec_pw_manager, state); | |
| ASSERT_THAT(status, NotOk()); | |
| EXPECT_EQ(status->local_crypto_error(), CryptoError::CE_OTHER_CRYPTO); | |
| } | |
| } // namespace revocation | |
| } // namespace cryptohome |