| // 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. |
| |
| #include "cryptohome/homedirs.h" |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/data_encoding.h> |
| #include <brillo/secure_blob.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "cryptohome/credentials.h" |
| #include "cryptohome/crypto.h" |
| #include "cryptohome/cryptolib.h" |
| #include "cryptohome/mock_le_credential_manager.h" |
| #include "cryptohome/mock_platform.h" |
| #include "cryptohome/mock_tpm.h" |
| #include "cryptohome/mock_tpm_init.h" |
| #include "cryptohome/mock_vault_keyset.h" |
| #include "cryptohome/mock_vault_keyset_factory.h" |
| #include "cryptohome/signed_secret.pb.h" |
| #include "cryptohome/vault_keyset.h" |
| |
| using ::testing::_; |
| using ::testing::ContainerEq; |
| using ::testing::ElementsAre; |
| using ::testing::EndsWith; |
| using ::testing::MatchesRegex; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::StrEq; |
| using ::testing::UnorderedElementsAre; |
| |
| namespace cryptohome { |
| |
| namespace { |
| |
| struct UserPassword { |
| const char* name; |
| const char* password; |
| }; |
| |
| constexpr char kUser0[] = "First User"; |
| constexpr char kUserPassword0[] = "user0_pass"; |
| |
| constexpr char kPasswordLabel[] = "password"; |
| constexpr char kAltPasswordLabel[] = "alt_password"; |
| |
| constexpr int kInitialKeysetIndex = 0; |
| |
| void GetKeysetBlob(const SerializedVaultKeyset& serialized, |
| brillo::SecureBlob* blob) { |
| brillo::SecureBlob local_wrapped_keyset(serialized.wrapped_keyset().length()); |
| serialized.wrapped_keyset().copy(local_wrapped_keyset.char_data(), |
| serialized.wrapped_keyset().length(), 0); |
| blob->swap(local_wrapped_keyset); |
| } |
| |
| } // namespace |
| |
| // TODO(dlunev): Remove kKey* extern declarations once we have it declared |
| // in the proper place. |
| extern const char kKeyFile[]; |
| extern const int kKeyFileMax; |
| extern const char kKeyLegacyPrefix[]; |
| |
| class KeysetManagementTest : public ::testing::Test { |
| public: |
| KeysetManagementTest() : crypto_(&platform_) {} |
| ~KeysetManagementTest() override {} |
| |
| // Not copyable or movable |
| KeysetManagementTest(const KeysetManagementTest&) = delete; |
| KeysetManagementTest& operator=(const KeysetManagementTest&) = delete; |
| KeysetManagementTest(KeysetManagementTest&&) = delete; |
| KeysetManagementTest& operator=(KeysetManagementTest&&) = delete; |
| |
| void SetUp() override { |
| crypto_.set_tpm(&tpm_); |
| crypto_.set_use_tpm(false); |
| homedirs_.Init(&platform_, &crypto_, nullptr); |
| |
| ASSERT_TRUE(homedirs_.GetSystemSalt(&system_salt_)); |
| platform_.GetFake()->SetSystemSaltForLibbrillo(system_salt_); |
| |
| AddUser(kUser0, kUserPassword0); |
| |
| PrepareDirectoryStructure(); |
| } |
| |
| void TearDown() override { |
| platform_.GetFake()->RemoveSystemSaltForLibbrillo(); |
| } |
| |
| protected: |
| NiceMock<MockPlatform> platform_; |
| NiceMock<MockTpm> tpm_; |
| Crypto crypto_; |
| HomeDirs homedirs_; |
| brillo::SecureBlob system_salt_; |
| |
| struct UserInfo { |
| std::string name; |
| std::string obfuscated; |
| brillo::SecureBlob passkey; |
| Credentials credentials; |
| base::FilePath homedir_path; |
| base::FilePath user_path; |
| }; |
| |
| // SETUPers |
| |
| // Information about users' homedirs. The order of users is equal to kUsers. |
| std::vector<UserInfo> users_; |
| |
| void AddUser(const char* name, const char* password) { |
| std::string obfuscated = |
| brillo::cryptohome::home::SanitizeUserNameWithSalt(name, system_salt_); |
| brillo::SecureBlob passkey; |
| cryptohome::Crypto::PasswordToPasskey(password, system_salt_, &passkey); |
| Credentials credentials(name, passkey); |
| |
| UserInfo info = {name, |
| obfuscated, |
| passkey, |
| credentials, |
| homedirs_.shadow_root().Append(obfuscated), |
| brillo::cryptohome::home::GetHashedUserPath(obfuscated)}; |
| users_.push_back(info); |
| } |
| |
| void PrepareDirectoryStructure() { |
| ASSERT_TRUE(platform_.CreateDirectory(homedirs_.shadow_root())); |
| ASSERT_TRUE(platform_.CreateDirectory( |
| brillo::cryptohome::home::GetUserPathPrefix())); |
| // We only need the homedir path, not the vault/mount paths. |
| for (const auto& user : users_) { |
| ASSERT_TRUE(platform_.CreateDirectory(user.homedir_path)); |
| } |
| } |
| |
| KeyData DefaultKeyData() { |
| KeyData key_data; |
| key_data.set_label(kPasswordLabel); |
| return key_data; |
| } |
| |
| KeyData SignedKeyData(const std::string& cipher_key, |
| const std::string& signing_key, |
| int revision) { |
| KeyData key_data; |
| key_data.set_label(kPasswordLabel); |
| key_data.set_revision(revision); |
| key_data.mutable_privileges()->set_update(false); |
| key_data.mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data.add_authorization_data(); |
| // Allow the default override on the revision. |
| auth_data->set_type( |
| KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| |
| // Add cipher. |
| if (!cipher_key.empty()) { |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_encrypt(true); |
| auth_secret->set_symmetric_key(cipher_key); |
| } |
| // Add signing. |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| auth_secret->set_symmetric_key(signing_key); |
| |
| return key_data; |
| } |
| |
| Credentials CredsForUpdate(const brillo::SecureBlob& passkey) { |
| Credentials credentials(users_[0].name, passkey); |
| KeyData key_data; |
| key_data.set_label(kAltPasswordLabel); |
| credentials.set_key_data(key_data); |
| return credentials; |
| } |
| |
| Key KeyForUpdate(const Credentials& creds, int revision) { |
| Key key; |
| std::string secret_str; |
| secret_str.resize(creds.passkey().size()); |
| secret_str.assign(reinterpret_cast<const char*>(creds.passkey().data()), |
| creds.passkey().size()); |
| key.set_secret(secret_str); |
| key.mutable_data()->set_label(creds.key_data().label()); |
| key.mutable_data()->set_revision(revision); |
| |
| return key; |
| } |
| |
| std::string SignatureForUpdate(const Key& key, |
| const std::string& signing_key) { |
| std::string changes_str; |
| ac::chrome::managedaccounts::account::Secret secret; |
| secret.set_revision(key.data().revision()); |
| secret.set_secret(key.secret()); |
| secret.SerializeToString(&changes_str); |
| |
| brillo::SecureBlob hmac_key(signing_key); |
| brillo::SecureBlob hmac_data(changes_str.begin(), changes_str.end()); |
| brillo::SecureBlob hmac = CryptoLib::HmacSha256(hmac_key, hmac_data); |
| |
| return hmac.to_string(); |
| } |
| |
| void KeysetSetUpWithKeyData(const KeyData& key_data) { |
| for (auto& user : users_) { |
| VaultKeyset vk; |
| vk.Initialize(&platform_, homedirs_.crypto()); |
| vk.CreateRandom(); |
| *vk.mutable_serialized()->mutable_key_data() = key_data; |
| user.credentials.set_key_data(key_data); |
| ASSERT_TRUE(vk.Encrypt(user.passkey, user.obfuscated)); |
| ASSERT_TRUE( |
| vk.Save(user.homedir_path.Append(kKeyFile).AddExtension("0"))); |
| } |
| } |
| |
| void KeysetSetUpWithoutKeyData() { |
| for (auto& user : users_) { |
| VaultKeyset vk; |
| vk.Initialize(&platform_, homedirs_.crypto()); |
| vk.CreateRandom(); |
| ASSERT_TRUE(vk.Encrypt(user.passkey, user.obfuscated)); |
| ASSERT_TRUE( |
| vk.Save(user.homedir_path.Append(kKeyFile).AddExtension("0"))); |
| } |
| } |
| |
| // TESTers |
| |
| void VerifyKeysetIndicies(const std::vector<int>& expected) { |
| std::vector<int> indicies; |
| ASSERT_TRUE(homedirs_.GetVaultKeysets(users_[0].obfuscated, &indicies)); |
| EXPECT_THAT(indicies, ContainerEq(expected)); |
| } |
| |
| void VerifyKeysetNotPresentWithCreds(const Credentials& creds) { |
| VaultKeyset vk; |
| vk.Initialize(&platform_, homedirs_.crypto()); |
| ASSERT_FALSE(homedirs_.GetValidKeyset(creds, &vk, /* error */ nullptr)); |
| } |
| |
| void VerifyKeysetPresentWithCredsAtIndex(const Credentials& creds, |
| int index) { |
| VaultKeyset vk; |
| vk.Initialize(&platform_, homedirs_.crypto()); |
| ASSERT_TRUE(homedirs_.GetValidKeyset(creds, &vk, /* error */ nullptr)); |
| EXPECT_EQ(vk.legacy_index(), index); |
| EXPECT_TRUE(vk.serialized().has_wrapped_chaps_key()); |
| EXPECT_TRUE(vk.serialized().has_wrapped_reset_seed()); |
| } |
| |
| void VerifyKeysetPresentWithCredsAtIndexAndRevision(const Credentials& creds, |
| int index, |
| int revision) { |
| VaultKeyset vk; |
| vk.Initialize(&platform_, homedirs_.crypto()); |
| ASSERT_TRUE(homedirs_.GetValidKeyset(creds, &vk, /* error */ nullptr)); |
| EXPECT_EQ(vk.legacy_index(), index); |
| EXPECT_EQ(vk.serialized().key_data().revision(), revision); |
| EXPECT_TRUE(vk.serialized().has_wrapped_chaps_key()); |
| EXPECT_TRUE(vk.serialized().has_wrapped_reset_seed()); |
| } |
| }; |
| |
| TEST_F(KeysetManagementTest, AreCredentialsValid) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| Credentials wrong_credentials(users_[0].name, brillo::SecureBlob("wrong")); |
| |
| // TEST |
| ASSERT_TRUE(homedirs_.AreCredentialsValid(users_[0].credentials)); |
| ASSERT_FALSE(homedirs_.AreCredentialsValid(wrong_credentials)); |
| } |
| |
| // Successfully adds initial keyset |
| TEST_F(KeysetManagementTest, AddInitialKeyset) { |
| // SETUP |
| |
| users_[0].credentials.set_key_data(DefaultKeyData()); |
| |
| // TEST |
| |
| EXPECT_TRUE( |
| homedirs_.AddInitialKeyset(users_[0].credentials, /*dircrypto_v2=*/true)); |
| |
| // VERIFY |
| // Initial keyset is added, readable, has "new-er" fields correctly |
| // populated and the initial index is "0". |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Successfully adds new keyset |
| TEST_F(KeysetManagementTest, AddKeysetSuccess) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| // TEST |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| EXPECT_NE(index, -1); |
| |
| // VERIFY |
| // After we add an additional keyset, we can list and read both of them. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex, index}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials, index); |
| } |
| |
| // Overrides existing keyset on label collision when "clobber" flag is present. |
| TEST_F(KeysetManagementTest, AddKeysetClobberSuccess) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| // Re-use key data from existing credentials to cause label collision. |
| KeyData key_data = users_[0].credentials.key_data(); |
| new_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, &key_data, |
| true, &index)); |
| EXPECT_EQ(index, 0); |
| |
| // VERIFY |
| // When adding new keyset with an "existing" label and the clobber is on, we |
| // expect it to override the keyset with the same label. Thus we shall have |
| // a keyset readable with new_credentials under the index of the old keyset. |
| // The old keyset shall be removed. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetNotPresentWithCreds(users_[0].credentials); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials, kInitialKeysetIndex); |
| } |
| |
| // Return error on label collision when no "clobber". |
| TEST_F(KeysetManagementTest, AddKeysetNoClobber) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| // Re-use key data from existing credentials to cause label collision. |
| KeyData key_data = users_[0].credentials.key_data(); |
| new_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_KEY_LABEL_EXISTS, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, &key_data, |
| false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // Label collision without "clobber" causes an addition error. Old keyset |
| // shall still be readable with old credentials, and the new one shall not |
| // exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to invalid label. |
| TEST_F(KeysetManagementTest, AddKeysetNonExistentLabel) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| Credentials not_existing_label_credentials = users_[0].credentials; |
| KeyData key_data = users_[0].credentials.key_data(); |
| key_data.set_label("i do not exist"); |
| not_existing_label_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND, |
| homedirs_.AddKeyset(not_existing_label_credentials, new_passkey, |
| nullptr, false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // Invalid label causes an addition error. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to invalid credentials. |
| TEST_F(KeysetManagementTest, AddKeysetInvalidCreds) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| brillo::SecureBlob wrong_passkey("wrong"); |
| Credentials wrong_credentials(users_[0].name, wrong_passkey); |
| |
| // TEST |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED, |
| homedirs_.AddKeyset(wrong_credentials, new_passkey, nullptr, false, |
| &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // Invalid credentials cause an addition error. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to lacking privilieges. |
| TEST_F(KeysetManagementTest, AddKeysetInvalidPrivileges) { |
| // SETUP |
| |
| KeyData vk_key_data; |
| vk_key_data.mutable_privileges()->set_add(false); |
| |
| KeysetSetUpWithKeyData(vk_key_data); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| // TEST |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // Invalid permissions cause an addition error. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to index pool exhaustion. |
| TEST_F(KeysetManagementTest, AddKeysetNoFreeIndices) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| // Use mock not to literally create a hundread files. |
| EXPECT_CALL(platform_, OpenFile(Property(&base::FilePath::value, |
| MatchesRegex(".*/master\\..*$")), |
| StrEq("wx"))) |
| .WillRepeatedly(Return(nullptr)); |
| |
| // TEST |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // Nothing should change if we were not able to add keyset due to a lack of |
| // free slots. Since we mocked the "slot" check, we should still have only |
| // initial keyset index, adn the keyset is readable with the old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to failed encryption. |
| TEST_F(KeysetManagementTest, AddKeysetEncryptFail) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| // Mock vk to inject encryption failure |
| MockVaultKeysetFactory vault_keyset_factory; |
| auto mock_vk = new NiceMock<MockVaultKeyset>(); |
| mock_vk->mutable_serialized()->set_wrapped_reset_seed("reset_seed"); |
| EXPECT_CALL(vault_keyset_factory, New(_, _)).WillOnce(Return(mock_vk)); |
| EXPECT_CALL(*mock_vk, Load(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Decrypt(_, _, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Encrypt(new_passkey, _)).WillOnce(Return(false)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory); |
| |
| // TEST |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // If we failed to save the added keyset due to encryption failure, the old |
| // keyset should still exist and be readable with the old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to add new keyset due to failed disk write. |
| TEST_F(KeysetManagementTest, AddKeysetSaveFail) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| // Mock vk to inject save failure. |
| MockVaultKeysetFactory vault_keyset_factory; |
| auto mock_vk = new NiceMock<MockVaultKeyset>(); |
| mock_vk->mutable_serialized()->set_wrapped_reset_seed("reset_seed"); |
| EXPECT_CALL(vault_keyset_factory, New(_, _)).WillOnce(Return(mock_vk)); |
| EXPECT_CALL(*mock_vk, Load(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Decrypt(_, _, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Encrypt(new_passkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Save(_)).WillOnce(Return(false)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory); |
| |
| // TEST |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| EXPECT_EQ(index, -1); |
| |
| // VERIFY |
| // If we failed to save the added keyset due to disk failure, the old |
| // keyset should still exist and be readable with the old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Successfully updates the keyset. |
| TEST_F(KeysetManagementTest, UpdateKeysetSuccess) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // The keyset update doesn't require signature, thus successfully can be |
| // updated without providing one. The keyset now available with the new |
| // credentials only. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetNotPresentWithCreds(users_[0].credentials); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials, kInitialKeysetIndex); |
| } |
| |
| // Fail to update keyset due to failed encryption. |
| TEST_F(KeysetManagementTest, UpdateKeysetEncryptFail) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| // Mock vk to inject encryption failure |
| MockVaultKeysetFactory vault_keyset_factory; |
| auto mock_vk = new NiceMock<MockVaultKeyset>(); |
| EXPECT_CALL(vault_keyset_factory, New(_, _)).WillOnce(Return(mock_vk)); |
| EXPECT_CALL(*mock_vk, Load(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Decrypt(_, _, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Encrypt(new_passkey, _)).WillOnce(Return(false)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // Failed encrypting updated keyset. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to update keyset due to failed disk write |
| TEST_F(KeysetManagementTest, UpdateKeysetSaveFail) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| // Mock vk to inject encryption failure |
| MockVaultKeysetFactory vault_keyset_factory; |
| auto mock_vk = new NiceMock<MockVaultKeyset>(); |
| base::FilePath source_path("doesn't matter"); |
| EXPECT_CALL(vault_keyset_factory, New(_, _)).WillOnce(Return(mock_vk)); |
| EXPECT_CALL(*mock_vk, Load(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Decrypt(_, _, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, Encrypt(new_passkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*mock_vk, source_file()).WillOnce(ReturnRef(source_path)); |
| EXPECT_CALL(*mock_vk, Save(_)).WillOnce(Return(false)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // Failed saving updated keyset. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to update keyset due to lacking privilieges. |
| TEST_F(KeysetManagementTest, UpdateKeysetInvalidPrivileges) { |
| // SETUP |
| |
| KeyData vk_key_data; |
| vk_key_data.mutable_privileges()->set_update(false); |
| vk_key_data.mutable_privileges()->set_authorized_update(false); |
| |
| KeysetSetUpWithKeyData(vk_key_data); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // Invalid permissions cause an update error. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fail to update keyset due to non existent label. |
| TEST_F(KeysetManagementTest, UpdateKeysetNonExistentLabel) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(SignedKeyData(std::string(), "abc123", 0)); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| Credentials not_existing_label_credentials = users_[0].credentials; |
| key_data = users_[0].credentials.key_data(); |
| key_data.set_label("i do not exist"); |
| not_existing_label_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND, |
| homedirs_.UpdateKeyset(not_existing_label_credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // Invalid label cause an update error. Old keyset shall still be |
| // readable with old credentials, and the new one shall not exist. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fails to update keyset due to missing signature. |
| TEST_F(KeysetManagementTest, UpdateKeysetAuthorizedNoSignature) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(SignedKeyData(std::string(), "abc123", 0)); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // The keyset update requires the signature and fails when non provided. The |
| // keyset is accessible with the old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Successfully updates keyset by providing correct signature. |
| TEST_F(KeysetManagementTest, UpdateKeysetAuthorizedSuccess) { |
| // SETUP |
| |
| std::string signing_key("abc123"); |
| KeysetSetUpWithKeyData(SignedKeyData(std::string(), signing_key, 0)); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| SignatureForUpdate(new_key, signing_key))); |
| |
| // VERIFY |
| // The keyset update requires signature, and succeed with the correct one |
| // provided. The keyset now available with the new credentials only. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetNotPresentWithCreds(users_[0].credentials); |
| VerifyKeysetPresentWithCredsAtIndexAndRevision(new_credentials, |
| kInitialKeysetIndex, 1); |
| } |
| |
| // Ensure signing matches the test vectors in Chrome. |
| TEST_F(KeysetManagementTest, UpdateKeysetAuthorizedCompatVector) { |
| // SETUP |
| |
| // The salted password passed in from Chrome. |
| constexpr char kPassword[] = "OSL3HZZSfK+mDQTYUh3lXhgAzJNWhYz52ax0Bleny7Q="; |
| // A no-op encryption key. |
| constexpr char kB64CipherKey[] = |
| "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE="; |
| // The signing key pre-installed. |
| constexpr char kB64SigningKey[] = |
| "p5TR/34XX0R7IMuffH14BiL1vcdSD8EajPzdIg09z9M="; |
| // The HMAC-256 signature over kPassword using kSigningKey. |
| constexpr char kB64Signature[] = |
| "KOPQmmJcMr9iMkr36N1cX+G9gDdBBu7zutAxNayPMN4="; |
| |
| std::string cipher_key; |
| ASSERT_TRUE(brillo::data_encoding::Base64Decode(kB64CipherKey, &cipher_key)); |
| std::string signing_key; |
| ASSERT_TRUE( |
| brillo::data_encoding::Base64Decode(kB64SigningKey, &signing_key)); |
| |
| KeysetSetUpWithKeyData(SignedKeyData(cipher_key, signing_key, 0)); |
| |
| brillo::SecureBlob new_passkey(kPassword); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| std::string signature; |
| ASSERT_TRUE(brillo::data_encoding::Base64Decode(kB64Signature, &signature)); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, signature)); |
| |
| // VERIFY |
| // The keyset update requires signature, and succeed with the correct one |
| // provided. The keyset now available with the new credentials only. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetNotPresentWithCreds(users_[0].credentials); |
| VerifyKeysetPresentWithCredsAtIndexAndRevision(new_credentials, |
| kInitialKeysetIndex, 1); |
| } |
| |
| // Fails to update keyset due to stale revision. |
| TEST_F(KeysetManagementTest, UpdateKeysetAuthorizedNoLessOrEqualRevision) { |
| // SETUP |
| |
| std::string signing_key("abc123"); |
| KeysetSetUpWithKeyData(SignedKeyData(std::string(), signing_key, 1)); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| |
| for (auto i = 0; i <= 1; i++) { |
| Key new_key = KeyForUpdate(new_credentials, i); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| SignatureForUpdate(new_key, signing_key))); |
| } |
| |
| // VERIFY |
| // The keyset update requires version to be higher than the current one, and |
| // fails if that is not the case. The keyset now available with the old |
| // credentials only. |
| |
| // Update doesn't change label for restricted keysets. |
| KeyData key_data = new_credentials.key_data(); |
| key_data.set_label(kPasswordLabel); |
| new_credentials.set_key_data(key_data); |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndexAndRevision(users_[0].credentials, |
| kInitialKeysetIndex, 1); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fails to update keyset due to wrong signature. |
| TEST_F(KeysetManagementTest, UpdateKeysetAuthorizedBadSignature) { |
| // SETUP |
| |
| std::string signing_key("abc123"); |
| KeysetSetUpWithKeyData(SignedKeyData(std::string(), signing_key, 0)); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials = CredsForUpdate(new_passkey); |
| Key new_key = KeyForUpdate(new_credentials, 1); |
| |
| Key wrong_key = new_key; |
| wrong_key.set_secret("wrong"); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(users_[0].credentials, &new_key, |
| SignatureForUpdate(wrong_key, signing_key))); |
| |
| // VERIFY |
| // The keyset update requires the signature and fails when bad provided. The |
| // keyset is accessible with the old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndexAndRevision(users_[0].credentials, |
| kInitialKeysetIndex, 0); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| } |
| |
| // Fails to update keyset due to wrong credentials. |
| TEST_F(KeysetManagementTest, UpdateKeysetBadSecret) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob wrong_passkey("wrong"); |
| Credentials wrong_credentials(users_[0].name, wrong_passkey); |
| Key new_key; |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED, |
| homedirs_.UpdateKeyset(wrong_credentials, &new_key, |
| /*signature=*/std::string())); |
| |
| // VERIFY |
| // The keyset update fails when wrong credentials are provided. The keyset |
| // now available with the old credentials only. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Successfully removes keyset. |
| TEST_F(KeysetManagementTest, RemoveKeysetSuccess) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new path"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.RemoveKeyset(users_[0].credentials, |
| users_[0].credentials.key_data())); |
| |
| // VERIFY |
| // We had one initial keyset and one added one. After deleting the initial |
| // one, only the new one shoulde be available. |
| |
| VerifyKeysetIndicies({index}); |
| |
| VerifyKeysetNotPresentWithCreds(users_[0].credentials); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials, index); |
| } |
| |
| // Fails to remove due to missing the desired key. |
| TEST_F(KeysetManagementTest, RemoveKeysetNotFound) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| KeyData key_data = users_[0].credentials.key_data(); |
| key_data.set_label("i do not exist"); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_KEY_NOT_FOUND, |
| homedirs_.RemoveKeyset(users_[0].credentials, key_data)); |
| |
| // VERIFY |
| // Trying to delete keyset with non-existing label. Nothing changes, initial |
| // keyset still available with old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Fails to remove due to not existing label. |
| TEST_F(KeysetManagementTest, RemoveKeysetNonExistentLabel) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| Credentials not_existing_label_credentials = users_[0].credentials; |
| KeyData key_data = users_[0].credentials.key_data(); |
| key_data.set_label("i do not exist"); |
| not_existing_label_credentials.set_key_data(key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND, |
| homedirs_.RemoveKeyset(not_existing_label_credentials, |
| users_[0].credentials.key_data())); |
| |
| // VERIFY |
| // Wrong label on authorization credentials. Nothing changes, initial |
| // keyset still available with old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Fails to remove due to invalid credentials. |
| TEST_F(KeysetManagementTest, RemoveKeysetInvalidCreds) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob wrong_passkey("wrong"); |
| Credentials wrong_credentials(users_[0].name, wrong_passkey); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED, |
| homedirs_.RemoveKeyset(wrong_credentials, |
| users_[0].credentials.key_data())); |
| |
| // VERIFY |
| // Wrong credentials. Nothing changes, initial keyset still available |
| // with old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Fails to remove due to lacking privilieges. |
| TEST_F(KeysetManagementTest, RemoveKeysetInvalidPrivileges) { |
| // SETUP |
| |
| KeyData vk_key_data; |
| vk_key_data.mutable_privileges()->set_remove(false); |
| vk_key_data.set_label(kPasswordLabel); |
| |
| KeysetSetUpWithKeyData(vk_key_data); |
| |
| // TEST |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED, |
| homedirs_.RemoveKeyset(users_[0].credentials, |
| users_[0].credentials.key_data())); |
| |
| // VERIFY |
| // Wrong permission on the keyset. Nothing changes, initial keyset still |
| // available with old credentials. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // List labels. |
| TEST_F(KeysetManagementTest, GetVaultKeysetLabels) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new path"); |
| KeyData key_data; |
| key_data.set_label(kAltPasswordLabel); |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, &key_data, |
| false, &index)); |
| |
| // TEST |
| |
| std::vector<std::string> labels; |
| EXPECT_TRUE(homedirs_.GetVaultKeysetLabels(users_[0].obfuscated, &labels)); |
| |
| // VERIFY |
| // Labels of the initial and newly added keysets are returned. |
| |
| ASSERT_EQ(2, labels.size()); |
| EXPECT_THAT(labels, UnorderedElementsAre(kPasswordLabel, kAltPasswordLabel)); |
| } |
| |
| // List labels for legacy keyset. |
| TEST_F(KeysetManagementTest, GetVaultKeysetLabelsOneLegacyLabeled) { |
| // SETUP |
| |
| KeysetSetUpWithoutKeyData(); |
| std::vector<std::string> labels; |
| |
| // TEST |
| |
| EXPECT_TRUE(homedirs_.GetVaultKeysetLabels(users_[0].obfuscated, &labels)); |
| |
| // VERIFY |
| // Initial keyset has no key data thus shall provide "legacy" label. |
| |
| ASSERT_EQ(1, labels.size()); |
| EXPECT_EQ(base::StringPrintf("%s%d", kKeyLegacyPrefix, kInitialKeysetIndex), |
| labels[0]); |
| } |
| |
| // Successfully force removes keyset. |
| TEST_F(KeysetManagementTest, ForceRemoveKeysetSuccess) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| brillo::SecureBlob new_passkey2("new pass2"); |
| Credentials new_credentials2(users_[0].name, new_passkey2); |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| int index2 = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey2, nullptr, |
| false, &index2)); |
| |
| // TEST |
| |
| EXPECT_TRUE(homedirs_.ForceRemoveKeyset(users_[0].obfuscated, index)); |
| // Remove a non-existing keyset is a success. |
| EXPECT_TRUE(homedirs_.ForceRemoveKeyset(users_[0].obfuscated, index)); |
| |
| // VERIFY |
| // We added two new keysets and force removed on of them. Only initial and the |
| // second added shall remain. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex, index2}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetNotPresentWithCreds(new_credentials); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials2, index2); |
| } |
| |
| // Fails to remove keyset due to invalid index. |
| TEST_F(KeysetManagementTest, ForceRemoveKeysetInvalidIndex) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| // TEST |
| |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset(users_[0].obfuscated, -1)); |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset(users_[0].obfuscated, kKeyFileMax)); |
| |
| // VERIFY |
| // Trying to delete keyset with out-of-bound index id. Nothing changes, |
| // initial keyset still available with old creds. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Fails to remove keyset due to injected error. |
| TEST_F(KeysetManagementTest, ForceRemoveKeysetFailedDelete) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| EXPECT_CALL( |
| platform_, |
| DeleteFile(Property(&base::FilePath::value, EndsWith("master.0")), false)) |
| .WillOnce(Return(false)); |
| |
| // TEST |
| |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset(users_[0].obfuscated, 0)); |
| |
| // VERIFY |
| // Deletion fails, Nothing changes, initial keyset still available with old |
| // creds. |
| |
| VerifyKeysetIndicies({kInitialKeysetIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| } |
| |
| // Successfully moves keyset. |
| TEST_F(KeysetManagementTest, MoveKeysetSuccess) { |
| // SETUP |
| |
| constexpr int kFirstMoveIndex = 17; |
| constexpr int kSecondMoveIndex = 22; |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| // TEST |
| |
| // Move twice to test move from the initial position and from a non-initial |
| // position. |
| ASSERT_TRUE(homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, |
| kFirstMoveIndex)); |
| ASSERT_TRUE(homedirs_.MoveKeyset(users_[0].obfuscated, kFirstMoveIndex, |
| kSecondMoveIndex)); |
| |
| // VERIFY |
| // Move initial keyset twice, expect it to be accessible with old creds on the |
| // new index slot. |
| |
| VerifyKeysetIndicies({kSecondMoveIndex}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, kSecondMoveIndex); |
| } |
| |
| // Fails to move keyset. |
| TEST_F(KeysetManagementTest, MoveKeysetFail) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| brillo::SecureBlob new_passkey("new pass"); |
| Credentials new_credentials(users_[0].name, new_passkey); |
| |
| int index = -1; |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(users_[0].credentials, new_passkey, nullptr, |
| false, &index)); |
| |
| const std::string kInitialFile = |
| base::StringPrintf("master.%d", kInitialKeysetIndex); |
| const std::string kIndexPlus2File = |
| base::StringPrintf("master.%d", index + 2); |
| const std::string kIndexPlus3File = |
| base::StringPrintf("master.%d", index + 3); |
| |
| // Inject open failure for the slot 2. |
| ON_CALL(platform_, |
| OpenFile(Property(&base::FilePath::value, EndsWith(kIndexPlus2File)), |
| StrEq("wx"))) |
| .WillByDefault(Return(nullptr)); |
| |
| // Inject rename failure for the slot 3. |
| ON_CALL(platform_, |
| Rename(Property(&base::FilePath::value, EndsWith(kInitialFile)), |
| Property(&base::FilePath::value, EndsWith(kIndexPlus3File)))) |
| .WillByDefault(Return(false)); |
| |
| // TEST |
| |
| // Out of bound indexes |
| ASSERT_FALSE(homedirs_.MoveKeyset(users_[0].obfuscated, -1, index)); |
| ASSERT_FALSE( |
| homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, -1)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(users_[0].obfuscated, kKeyFileMax, index)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, |
| kKeyFileMax)); |
| |
| // Not existing source |
| ASSERT_FALSE( |
| homedirs_.MoveKeyset(users_[0].obfuscated, index + 4, index + 5)); |
| |
| // Destination exists |
| ASSERT_FALSE( |
| homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, index)); |
| |
| // Destination file error-injected. |
| ASSERT_FALSE(homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, |
| index + 2)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(users_[0].obfuscated, kInitialKeysetIndex, |
| index + 3)); |
| |
| // VERIFY |
| |
| // TODO(chromium:1141301, dlunev): the fact we have keyset index+3 is a bug - |
| // MoveKeyset will not cleanup created file if Rename fails. Not addressing it |
| // now durign test refactor, but will in the coming CLs. |
| VerifyKeysetIndicies({kInitialKeysetIndex, index, index + 3}); |
| |
| VerifyKeysetPresentWithCredsAtIndex(users_[0].credentials, |
| kInitialKeysetIndex); |
| VerifyKeysetPresentWithCredsAtIndex(new_credentials, index); |
| } |
| |
| TEST_F(KeysetManagementTest, ReSaveKeysetNoReSave) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| VaultKeyset vk0; |
| vk0.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0, |
| /* error */ nullptr)); |
| |
| // TEST |
| |
| MountError code; |
| std::unique_ptr<VaultKeyset> vk_load = |
| homedirs_.LoadUnwrappedKeyset(users_[0].credentials, &code); |
| EXPECT_EQ(MOUNT_ERROR_NONE, code); |
| |
| // VERIFY |
| |
| VaultKeyset vk0_new; |
| vk0_new.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0_new, |
| /* error */ nullptr)); |
| |
| brillo::SecureBlob lhs, rhs; |
| GetKeysetBlob(vk0.serialized(), &lhs); |
| GetKeysetBlob(vk0_new.serialized(), &rhs); |
| ASSERT_EQ(lhs.size(), rhs.size()); |
| ASSERT_EQ(0, brillo::SecureMemcmp(lhs.data(), rhs.data(), lhs.size())); |
| } |
| |
| TEST_F(KeysetManagementTest, ReSaveKeysetChapsRepopulation) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| VaultKeyset vk0; |
| vk0.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.LoadVaultKeysetForUser(users_[0].obfuscated, 0, &vk0)); |
| vk0.mutable_serialized()->clear_wrapped_chaps_key(); |
| EXPECT_FALSE(vk0.serialized().has_wrapped_chaps_key()); |
| ASSERT_TRUE(vk0.Save(vk0.source_file())); |
| |
| // TEST |
| |
| MountError code; |
| std::unique_ptr<VaultKeyset> vk_load = |
| homedirs_.LoadUnwrappedKeyset(users_[0].credentials, &code); |
| EXPECT_EQ(MOUNT_ERROR_NONE, code); |
| EXPECT_TRUE(vk_load->serialized().has_wrapped_chaps_key()); |
| |
| // VERIFY |
| |
| VaultKeyset vk0_new; |
| vk0_new.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0_new, |
| /* error */ nullptr)); |
| EXPECT_TRUE(vk0_new.serialized().has_wrapped_chaps_key()); |
| |
| ASSERT_EQ(vk0_new.chaps_key().size(), vk_load->chaps_key().size()); |
| ASSERT_EQ(0, brillo::SecureMemcmp(vk0_new.chaps_key().data(), |
| vk_load->chaps_key().data(), |
| vk0_new.chaps_key().size())); |
| } |
| |
| TEST_F(KeysetManagementTest, ReSaveOnLoadNoReSave) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| VaultKeyset vk0; |
| vk0.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0, |
| /* error */ nullptr)); |
| |
| // TEST |
| |
| EXPECT_FALSE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| } |
| |
| // The following tests use MOCKs for TpmState and hand-crafted vault keyset |
| // state. Ideally we shall have a fake tpm, but that is not feasible ATM. |
| |
| TEST_F(KeysetManagementTest, ReSaveOnLoadTestRegularCreds) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| VaultKeyset vk0; |
| vk0.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0, |
| /* error */ nullptr)); |
| |
| NiceMock<MockTpmInit> mock_tpm_init; |
| EXPECT_CALL(mock_tpm_init, HasCryptohomeKey()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(mock_tpm_init, SetupTpm(true)).WillRepeatedly(Return(true)); |
| |
| EXPECT_CALL(tpm_, IsEnabled()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(tpm_, IsOwned()).WillRepeatedly(Return(true)); |
| |
| homedirs_.set_use_tpm(true); |
| crypto_.set_use_tpm(true); |
| crypto_.Init(&mock_tpm_init); |
| |
| // TEST |
| |
| // Scrypt wrapped shall be resaved when tpm present. |
| EXPECT_TRUE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| |
| // Tpm wrapped not pcr bound, but no public hash - resave. |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::TPM_WRAPPED | |
| SerializedVaultKeyset::SCRYPT_DERIVED); |
| EXPECT_TRUE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| |
| // Tpm wrapped pcr bound, but no public hash - resave. |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::TPM_WRAPPED | |
| SerializedVaultKeyset::SCRYPT_DERIVED | |
| SerializedVaultKeyset::PCR_BOUND); |
| EXPECT_TRUE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| |
| // Tpm wrapped not pcr bound, public hash - resave. |
| vk0.mutable_serialized()->set_tpm_public_key_hash("public hash"); |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::TPM_WRAPPED | |
| SerializedVaultKeyset::SCRYPT_DERIVED); |
| EXPECT_TRUE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| |
| // Tpm wrapped pcr bound, public hash - no resave. |
| vk0.mutable_serialized()->set_tpm_public_key_hash("public hash"); |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::TPM_WRAPPED | |
| SerializedVaultKeyset::SCRYPT_DERIVED | |
| SerializedVaultKeyset::PCR_BOUND); |
| EXPECT_FALSE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| } |
| |
| TEST_F(KeysetManagementTest, ReSaveOnLoadTestLeCreds) { |
| // SETUP |
| |
| KeysetSetUpWithKeyData(DefaultKeyData()); |
| |
| VaultKeyset vk0; |
| vk0.Initialize(&platform_, homedirs_.crypto()); |
| EXPECT_TRUE(homedirs_.GetValidKeyset(users_[0].credentials, &vk0, |
| /* error */ nullptr)); |
| |
| NiceMock<MockTpmInit> mock_tpm_init; |
| EXPECT_CALL(mock_tpm_init, HasCryptohomeKey()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(mock_tpm_init, SetupTpm(true)).WillRepeatedly(Return(true)); |
| |
| EXPECT_CALL(tpm_, IsEnabled()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(tpm_, IsOwned()).WillRepeatedly(Return(true)); |
| |
| auto le_cred_manager = new cryptohome::MockLECredentialManager(); |
| crypto_.set_le_manager_for_testing( |
| std::unique_ptr<cryptohome::LECredentialManager>(le_cred_manager)); |
| |
| homedirs_.set_use_tpm(true); |
| crypto_.set_use_tpm(true); |
| crypto_.Init(&mock_tpm_init); |
| |
| // TEST |
| |
| // le credentials which doesn't need pcr binding - no re-save |
| EXPECT_CALL(*le_cred_manager, NeedsPcrBinding(_)) |
| .WillRepeatedly(Return(false)); |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::LE_CREDENTIAL); |
| EXPECT_FALSE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| |
| // le credentials which needs pcr binding - no resave. |
| EXPECT_CALL(*le_cred_manager, NeedsPcrBinding(_)) |
| .WillRepeatedly(Return(true)); |
| vk0.mutable_serialized()->set_flags(SerializedVaultKeyset::LE_CREDENTIAL); |
| EXPECT_TRUE(homedirs_.ShouldReSaveKeyset(&vk0)); |
| } |
| |
| } // namespace cryptohome |