blob: 18021e5e205dbc83b873af1c811f2c35a08ab564 [file] [log] [blame]
// Copyright 2012 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Unit tests for VaultKeyset.
#include "cryptohome/vault_keyset.h"
#include <memory>
#include <openssl/evp.h>
#include <optional>
#include <string.h> // For memcmp().
#include <utility>
#include <variant>
#include <vector>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/test/task_environment.h>
#include <brillo/secure_blob.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/frontend/cryptohome/mock_frontend.h>
#include <libhwsec/frontend/pinweaver/mock_frontend.h>
#include <libhwsec-foundation/crypto/aes.h>
#include <libhwsec-foundation/crypto/hmac.h>
#include <libhwsec-foundation/crypto/libscrypt_compat.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "base/test/test_future.h"
#include "cryptohome/auth_blocks/auth_block.h"
#include "cryptohome/auth_blocks/auth_block_utils.h"
#include "cryptohome/auth_blocks/pin_weaver_auth_block.h"
#include "cryptohome/auth_blocks/scrypt_auth_block.h"
#include "cryptohome/crypto.h"
#include "cryptohome/crypto_error.h"
#include "cryptohome/cryptohome_common.h"
#include "cryptohome/fake_features.h"
#include "cryptohome/flatbuffer_schemas/auth_block_state.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_le_credential_manager.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/storage/file_system_keyset.h"
namespace cryptohome {
using base::FilePath;
using base::test::TestFuture;
using brillo::SecureBlob;
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::CryptohomeError;
using cryptohome::error::CryptohomeLECredError;
using cryptohome::error::ErrorActionSet;
using cryptohome::error::PossibleAction;
using cryptohome::error::PrimaryAction;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::GetSecureRandom;
using hwsec_foundation::HmacSha256;
using hwsec_foundation::kAesBlockSize;
using hwsec_foundation::SecureBlobToHex;
using hwsec_foundation::error::testing::IsOk;
using hwsec_foundation::error::testing::NotOk;
using hwsec_foundation::error::testing::ReturnError;
using hwsec_foundation::error::testing::ReturnOk;
using hwsec_foundation::error::testing::ReturnValue;
using hwsec_foundation::status::OkStatus;
using ::testing::_;
using ::testing::DoAll;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::WithArg;
namespace {
constexpr char kHexHighEntropySecret[] =
"F3D9D5B126C36676689E18BB8517D95DF4F30947E71D4A840824425760B1D3FA";
constexpr char kHexResetSecret[] =
"B133D2450392335BA8D33AA95AD52488254070C66F5D79AEA1A46AC4A30760D4";
constexpr char kHexWrappedKeyset[] =
"B737B5D73E39BD390A4F361CE2FC166CF1E89EC6AEAA35D4B34456502C48B4F5EFA310077"
"324B393E13AF633DF3072FF2EC78BD2B80D919035DB97C30F1AD418737DA3F26A4D35DF6B"
"6A9743BD0DF3D37D8A68DE0932A9905452D05ECF92701B9805937F76EE01D10924268F057"
"EDD66087774BB86C2CB92B01BD3A3C41C10C52838BD3A3296474598418E5191DEE9E8D831"
"3C859C9EDB0D5F2BC1D7FC3C108A0D4ABB2D90E413086BCFFD0902AB68E2BF787817EB10C"
"25E2E43011CAB3FB8AA";
constexpr char kHexSalt[] = "D470B9B108902241";
constexpr char kHexVaultKey[] =
"665A58534E684F2B61516B6D42624B514E6749732B4348427450305453754158377232347"
"37A79466C6B383D";
constexpr char kHexFekIv[] = "EA80F14BF29C6D580D536E7F0CC47F3E";
constexpr char kHexChapsIv[] = "ED85D928940E5B02ED218F29225AA34F";
constexpr char kHexWrappedChapsKey[] =
"7D7D01EECC8DAE7906CAD56310954BBEB3CC81765210D29902AB92DDE074217771AD284F2"
"12C13897C6CBB30CEC4CD75";
constexpr int kLegacyIndex = 1;
constexpr char kLegacyLabel[] = "legacy-1";
constexpr char kTempLabel[] = "tempLabel";
constexpr char kFilePath[] = "foo";
constexpr char kFakePasswordKey[] = "blabla";
constexpr int kPasswordRounds = 5;
std::string HexDecode(const std::string& hex) {
std::vector<uint8_t> output;
CHECK(base::HexStringToBytes(hex, &output));
return std::string(output.begin(), output.end());
}
} // namespace
class VaultKeysetTest : public ::testing::Test {
public:
VaultKeysetTest()
: crypto_(&hwsec_, &pinweaver_, &cryptohome_keys_manager_, nullptr) {}
VaultKeysetTest(const VaultKeysetTest&) = delete;
VaultKeysetTest& operator=(const VaultKeysetTest&) = delete;
~VaultKeysetTest() override = default;
static bool FindBlobInBlob(const brillo::SecureBlob& haystack,
const brillo::SecureBlob& needle) {
if (needle.size() > haystack.size()) {
return false;
}
for (unsigned int start = 0; start <= (haystack.size() - needle.size());
start++) {
if (memcmp(&haystack[start], needle.data(), needle.size()) == 0) {
return true;
}
}
return false;
}
protected:
MockPlatform platform_;
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
Crypto crypto_;
};
TEST_F(VaultKeysetTest, AllocateRandom) {
// Check that allocating a random VaultKeyset works
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SIZE, vault_keyset.GetFek().size());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SIGNATURE_SIZE,
vault_keyset.GetFekSig().size());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SALT_SIZE, vault_keyset.GetFekSalt().size());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SIZE, vault_keyset.GetFnek().size());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SIGNATURE_SIZE,
vault_keyset.GetFnekSig().size());
EXPECT_EQ(CRYPTOHOME_DEFAULT_KEY_SALT_SIZE,
vault_keyset.GetFnekSalt().size());
EXPECT_EQ(CRYPTOHOME_CHAPS_KEY_LENGTH, vault_keyset.GetChapsKey().size());
}
TEST_F(VaultKeysetTest, SerializeTest) {
// Check that serialize works
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
SecureBlob blob;
EXPECT_TRUE(vault_keyset.ToKeysBlob(&blob));
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFek()));
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFekSig()));
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFekSalt()));
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFnek()));
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFnekSig()));
EXPECT_TRUE(
VaultKeysetTest::FindBlobInBlob(blob, vault_keyset.GetFnekSalt()));
}
TEST_F(VaultKeysetTest, DeserializeTest) {
// Check that deserialize works
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
SecureBlob blob;
EXPECT_TRUE(vault_keyset.ToKeysBlob(&blob));
VaultKeyset new_vault_keyset;
EXPECT_TRUE(new_vault_keyset.FromKeysBlob(blob));
EXPECT_EQ(vault_keyset.GetFek().size(), new_vault_keyset.GetFek().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFek(),
new_vault_keyset.GetFek()));
EXPECT_EQ(vault_keyset.GetFekSig().size(),
new_vault_keyset.GetFekSig().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFekSig(),
new_vault_keyset.GetFekSig()));
EXPECT_EQ(vault_keyset.GetFekSalt().size(),
new_vault_keyset.GetFekSalt().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFekSalt(),
new_vault_keyset.GetFekSalt()));
EXPECT_EQ(vault_keyset.GetFnek().size(), new_vault_keyset.GetFnek().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFnek(),
new_vault_keyset.GetFnek()));
EXPECT_EQ(vault_keyset.GetFnekSig().size(),
new_vault_keyset.GetFnekSig().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFnekSig(),
new_vault_keyset.GetFnekSig()));
EXPECT_EQ(vault_keyset.GetFnekSalt().size(),
new_vault_keyset.GetFnekSalt().size());
EXPECT_TRUE(VaultKeysetTest::FindBlobInBlob(vault_keyset.GetFnekSalt(),
new_vault_keyset.GetFnekSalt()));
}
ACTION_P(CopyToSecureBlob, b) {
b->assign(arg0.begin(), arg0.end());
return true;
}
ACTION_P(CopyFromSecureBlob, b) {
arg0->assign(b->begin(), b->end());
return true;
}
TEST_F(VaultKeysetTest, WriteError) {
// Setup.
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
const auto reset_iv = CreateSecureRandomBlob(kAesBlockSize);
static const int kFscryptPolicyVersion = 2;
vault_keyset.SetResetIV(reset_iv);
vault_keyset.SetFSCryptPolicyVersion(kFscryptPolicyVersion);
vault_keyset.SetLegacyIndex(kLegacyIndex);
KeyBlobs key_blobs = {.vkk_key = brillo::SecureBlob(32, 'A'),
.vkk_iv = brillo::SecureBlob(16, 'B'),
.chaps_iv = brillo::SecureBlob(16, 'C')};
TpmBoundToPcrAuthBlockState pcr_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
ASSERT_THAT(vault_keyset.EncryptEx(key_blobs, auth_state),
hwsec_foundation::error::testing::IsOk());
EXPECT_CALL(platform_, WriteFileAtomicDurable(FilePath(kFilePath), _, _))
.WillOnce(Return(false));
// Test.
EXPECT_FALSE(vault_keyset.Save(FilePath(kFilePath)));
}
TEST_F(VaultKeysetTest, ErrorSavingUnecryptedKeyset) {
// Setup.
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
// vault_keyset.encryptex is not called, therefore save should fail as soon as
// it is tried.
EXPECT_CALL(platform_, WriteFileAtomicDurable(FilePath(kFilePath), _, _))
.Times(0);
// Test.
EXPECT_FALSE(vault_keyset.Save(FilePath(kFilePath)));
}
TEST_F(VaultKeysetTest, GetPcrBoundAuthBlockStateTest) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::PCR_BOUND);
keyset.SetTpmPublicKeyHash(brillo::SecureBlob("yadayada"));
keyset.SetTPMKey(brillo::SecureBlob("blabla"));
keyset.SetExtendedTPMKey(brillo::SecureBlob("foobaz"));
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const TpmBoundToPcrAuthBlockState* tpm_state =
std::get_if<TpmBoundToPcrAuthBlockState>(&auth_state.state);
ASSERT_NE(tpm_state, nullptr);
ASSERT_TRUE(tpm_state->scrypt_derived.has_value());
EXPECT_TRUE(tpm_state->scrypt_derived.value());
EXPECT_TRUE(tpm_state->extended_tpm_key.has_value());
EXPECT_TRUE(tpm_state->tpm_key.has_value());
}
TEST_F(VaultKeysetTest, GetEccAuthBlockStateTest) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::ECC |
SerializedVaultKeyset::PCR_BOUND);
keyset.SetTpmPublicKeyHash(brillo::SecureBlob("yadayada"));
keyset.SetTPMKey(brillo::SecureBlob("blabla"));
keyset.SetExtendedTPMKey(brillo::SecureBlob("foobaz"));
keyset.password_rounds_ = 5;
keyset.vkk_iv_ = brillo::SecureBlob("wowowow");
keyset.auth_salt_ = brillo::SecureBlob("salt");
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const TpmEccAuthBlockState* tpm_state =
std::get_if<TpmEccAuthBlockState>(&auth_state.state);
ASSERT_NE(tpm_state, nullptr);
EXPECT_TRUE(tpm_state->salt.has_value());
EXPECT_TRUE(tpm_state->sealed_hvkkm.has_value());
EXPECT_TRUE(tpm_state->extended_sealed_hvkkm.has_value());
EXPECT_TRUE(tpm_state->tpm_public_key_hash.has_value());
EXPECT_TRUE(tpm_state->vkk_iv.has_value());
EXPECT_EQ(tpm_state->auth_value_rounds.value(), 5);
}
TEST_F(VaultKeysetTest, GetNotPcrBoundAuthBlockState) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::TPM_WRAPPED);
keyset.SetTpmPublicKeyHash(brillo::SecureBlob("yadayada"));
keyset.SetTPMKey(brillo::SecureBlob("blabla"));
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const TpmNotBoundToPcrAuthBlockState* tpm_state =
std::get_if<TpmNotBoundToPcrAuthBlockState>(&auth_state.state);
ASSERT_NE(tpm_state, nullptr);
ASSERT_TRUE(tpm_state->scrypt_derived.has_value());
EXPECT_FALSE(tpm_state->scrypt_derived.value());
EXPECT_TRUE(tpm_state->tpm_key.has_value());
}
TEST_F(VaultKeysetTest, GetPinWeaverAuthBlockState) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
const uint64_t le_label = 012345;
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::LE_CREDENTIAL);
keyset.SetLELabel(le_label);
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const PinWeaverAuthBlockState* pin_auth_state =
std::get_if<PinWeaverAuthBlockState>(&auth_state.state);
ASSERT_NE(pin_auth_state, nullptr);
EXPECT_TRUE(pin_auth_state->le_label.has_value());
EXPECT_EQ(le_label, pin_auth_state->le_label.value());
}
TEST_F(VaultKeysetTest, GetChallengeCredentialAuthBlockState) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::SCRYPT_WRAPPED |
SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED);
const brillo::Blob kScryptPlaintext = brillo::BlobFromString("plaintext");
const auto blob_to_encrypt = brillo::SecureBlob(brillo::CombineBlobs(
{kScryptPlaintext, hwsec_foundation::Sha1(kScryptPlaintext)}));
brillo::SecureBlob wrapped_keyset;
brillo::SecureBlob wrapped_chaps_key;
brillo::SecureBlob wrapped_reset_seed;
brillo::SecureBlob derived_key = {
0x67, 0xeb, 0xcd, 0x84, 0x49, 0x5e, 0xa2, 0xf3, 0xb1, 0xe6, 0xe7,
0x5b, 0x13, 0xb9, 0x16, 0x2f, 0x5a, 0x39, 0xc8, 0xfe, 0x6a, 0x60,
0xd4, 0x7a, 0xd8, 0x2b, 0x44, 0xc4, 0x45, 0x53, 0x1a, 0x85, 0x4a,
0x97, 0x9f, 0x2d, 0x06, 0xf5, 0xd0, 0xd3, 0xa6, 0xe7, 0xac, 0x9b,
0x02, 0xaf, 0x3c, 0x08, 0xce, 0x43, 0x46, 0x32, 0x6d, 0xd7, 0x2b,
0xe9, 0xdf, 0x8b, 0x38, 0x0e, 0x60, 0x3d, 0x64, 0x12};
brillo::SecureBlob scrypt_salt = brillo::SecureBlob("salt");
brillo::SecureBlob chaps_salt = brillo::SecureBlob("chaps_salt");
brillo::SecureBlob reset_seed_salt = brillo::SecureBlob("reset_seed_salt");
scrypt_salt.resize(hwsec_foundation::kLibScryptSaltSize);
chaps_salt.resize(hwsec_foundation::kLibScryptSaltSize);
reset_seed_salt.resize(hwsec_foundation::kLibScryptSaltSize);
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, scrypt_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_keyset));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, chaps_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_chaps_key));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, reset_seed_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_reset_seed));
keyset.SetWrappedKeyset(wrapped_keyset);
keyset.SetWrappedChapsKey(wrapped_chaps_key);
keyset.SetWrappedResetSeed(wrapped_reset_seed);
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const ChallengeCredentialAuthBlockState* cc_state =
std::get_if<ChallengeCredentialAuthBlockState>(&auth_state.state);
EXPECT_NE(cc_state, nullptr);
}
TEST_F(VaultKeysetTest, GetScryptAuthBlockState) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::SCRYPT_WRAPPED);
const brillo::Blob kScryptPlaintext = brillo::BlobFromString("plaintext");
const auto blob_to_encrypt = brillo::SecureBlob(brillo::CombineBlobs(
{kScryptPlaintext, hwsec_foundation::Sha1(kScryptPlaintext)}));
brillo::SecureBlob wrapped_keyset;
brillo::SecureBlob wrapped_chaps_key;
brillo::SecureBlob wrapped_reset_seed;
brillo::SecureBlob derived_key = {
0x67, 0xeb, 0xcd, 0x84, 0x49, 0x5e, 0xa2, 0xf3, 0xb1, 0xe6, 0xe7,
0x5b, 0x13, 0xb9, 0x16, 0x2f, 0x5a, 0x39, 0xc8, 0xfe, 0x6a, 0x60,
0xd4, 0x7a, 0xd8, 0x2b, 0x44, 0xc4, 0x45, 0x53, 0x1a, 0x85, 0x4a,
0x97, 0x9f, 0x2d, 0x06, 0xf5, 0xd0, 0xd3, 0xa6, 0xe7, 0xac, 0x9b,
0x02, 0xaf, 0x3c, 0x08, 0xce, 0x43, 0x46, 0x32, 0x6d, 0xd7, 0x2b,
0xe9, 0xdf, 0x8b, 0x38, 0x0e, 0x60, 0x3d, 0x64, 0x12};
brillo::SecureBlob scrypt_salt = brillo::SecureBlob("salt");
brillo::SecureBlob chaps_salt = brillo::SecureBlob("chaps_salt");
brillo::SecureBlob reset_seed_salt = brillo::SecureBlob("reset_seed_salt");
scrypt_salt.resize(hwsec_foundation::kLibScryptSaltSize);
chaps_salt.resize(hwsec_foundation::kLibScryptSaltSize);
reset_seed_salt.resize(hwsec_foundation::kLibScryptSaltSize);
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, scrypt_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_keyset));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, chaps_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_chaps_key));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, reset_seed_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_reset_seed));
keyset.SetWrappedKeyset(wrapped_keyset);
keyset.SetWrappedChapsKey(wrapped_chaps_key);
keyset.SetWrappedResetSeed(wrapped_reset_seed);
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const ScryptAuthBlockState* scrypt_state =
std::get_if<ScryptAuthBlockState>(&auth_state.state);
ASSERT_NE(scrypt_state, nullptr);
EXPECT_TRUE(scrypt_state->salt.has_value());
EXPECT_TRUE(scrypt_state->chaps_salt.has_value());
EXPECT_TRUE(scrypt_state->reset_seed_salt.has_value());
EXPECT_TRUE(scrypt_state->work_factor.has_value());
EXPECT_TRUE(scrypt_state->block_size.has_value());
EXPECT_TRUE(scrypt_state->parallel_factor.has_value());
}
TEST_F(VaultKeysetTest, GetDoubleWrappedCompatAuthBlockStateFailure) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::SCRYPT_WRAPPED |
SerializedVaultKeyset::TPM_WRAPPED);
const brillo::Blob kScryptPlaintext = brillo::BlobFromString("plaintext");
const auto blob_to_encrypt = brillo::SecureBlob(brillo::CombineBlobs(
{kScryptPlaintext, hwsec_foundation::Sha1(kScryptPlaintext)}));
brillo::SecureBlob wrapped_keyset;
brillo::SecureBlob wrapped_chaps_key;
brillo::SecureBlob wrapped_reset_seed;
brillo::SecureBlob derived_key = {
0x67, 0xeb, 0xcd, 0x84, 0x49, 0x5e, 0xa2, 0xf3, 0xb1, 0xe6, 0xe7,
0x5b, 0x13, 0xb9, 0x16, 0x2f, 0x5a, 0x39, 0xc8, 0xfe, 0x6a, 0x60,
0xd4, 0x7a, 0xd8, 0x2b, 0x44, 0xc4, 0x45, 0x53, 0x1a, 0x85, 0x4a,
0x97, 0x9f, 0x2d, 0x06, 0xf5, 0xd0, 0xd3, 0xa6, 0xe7, 0xac, 0x9b,
0x02, 0xaf, 0x3c, 0x08, 0xce, 0x43, 0x46, 0x32, 0x6d, 0xd7, 0x2b,
0xe9, 0xdf, 0x8b, 0x38, 0x0e, 0x60, 0x3d, 0x64, 0x12};
brillo::SecureBlob scrypt_salt = brillo::SecureBlob("salt");
brillo::SecureBlob chaps_salt = brillo::SecureBlob("chaps_salt");
brillo::SecureBlob reset_seed_salt = brillo::SecureBlob("reset_seed_salt");
scrypt_salt.resize(hwsec_foundation::kLibScryptSaltSize);
chaps_salt.resize(hwsec_foundation::kLibScryptSaltSize);
reset_seed_salt.resize(hwsec_foundation::kLibScryptSaltSize);
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, scrypt_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_keyset));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, chaps_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_chaps_key));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, reset_seed_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_reset_seed));
keyset.SetWrappedKeyset(wrapped_keyset);
keyset.SetWrappedChapsKey(wrapped_chaps_key);
keyset.SetWrappedResetSeed(wrapped_reset_seed);
AuthBlockState auth_state;
// A required tpm_key is not set in keyset, failure in creating
// sub-state TpmNotBoundToPcrAuthBlockState.
EXPECT_FALSE(GetAuthBlockState(keyset, auth_state));
const DoubleWrappedCompatAuthBlockState* double_wrapped_state =
std::get_if<DoubleWrappedCompatAuthBlockState>(&auth_state.state);
EXPECT_EQ(double_wrapped_state, nullptr);
}
TEST_F(VaultKeysetTest, GetDoubleWrappedCompatAuthBlockState) {
VaultKeyset keyset;
keyset.Initialize(&platform_, &crypto_);
keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
keyset.SetFlags(SerializedVaultKeyset::SCRYPT_WRAPPED |
SerializedVaultKeyset::TPM_WRAPPED);
keyset.SetTPMKey(brillo::SecureBlob("blabla"));
const brillo::Blob kScryptPlaintext = brillo::BlobFromString("plaintext");
const auto blob_to_encrypt = brillo::SecureBlob(brillo::CombineBlobs(
{kScryptPlaintext, hwsec_foundation::Sha1(kScryptPlaintext)}));
brillo::SecureBlob wrapped_keyset;
brillo::SecureBlob wrapped_chaps_key;
brillo::SecureBlob wrapped_reset_seed;
brillo::SecureBlob derived_key = {
0x67, 0xeb, 0xcd, 0x84, 0x49, 0x5e, 0xa2, 0xf3, 0xb1, 0xe6, 0xe7,
0x5b, 0x13, 0xb9, 0x16, 0x2f, 0x5a, 0x39, 0xc8, 0xfe, 0x6a, 0x60,
0xd4, 0x7a, 0xd8, 0x2b, 0x44, 0xc4, 0x45, 0x53, 0x1a, 0x85, 0x4a,
0x97, 0x9f, 0x2d, 0x06, 0xf5, 0xd0, 0xd3, 0xa6, 0xe7, 0xac, 0x9b,
0x02, 0xaf, 0x3c, 0x08, 0xce, 0x43, 0x46, 0x32, 0x6d, 0xd7, 0x2b,
0xe9, 0xdf, 0x8b, 0x38, 0x0e, 0x60, 0x3d, 0x64, 0x12};
brillo::SecureBlob scrypt_salt = brillo::SecureBlob("salt");
brillo::SecureBlob chaps_salt = brillo::SecureBlob("chaps_salt");
brillo::SecureBlob reset_seed_salt = brillo::SecureBlob("reset_seed_salt");
scrypt_salt.resize(hwsec_foundation::kLibScryptSaltSize);
chaps_salt.resize(hwsec_foundation::kLibScryptSaltSize);
reset_seed_salt.resize(hwsec_foundation::kLibScryptSaltSize);
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, scrypt_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_keyset));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, chaps_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_chaps_key));
ASSERT_TRUE(hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, reset_seed_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_reset_seed));
keyset.SetWrappedKeyset(wrapped_keyset);
keyset.SetWrappedChapsKey(wrapped_chaps_key);
keyset.SetWrappedResetSeed(wrapped_reset_seed);
AuthBlockState auth_state;
EXPECT_TRUE(GetAuthBlockState(keyset, auth_state));
const DoubleWrappedCompatAuthBlockState* double_wrapped_state =
std::get_if<DoubleWrappedCompatAuthBlockState>(&auth_state.state);
EXPECT_NE(double_wrapped_state, nullptr);
}
TEST_F(VaultKeysetTest, GetLegacyLabelTest) {
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.SetLegacyIndex(kLegacyIndex);
ASSERT_EQ(vault_keyset.GetLabel(), kLegacyLabel);
}
TEST_F(VaultKeysetTest, GetLabelTest) {
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
KeyData key_data;
key_data.set_label(kTempLabel);
vault_keyset.SetLegacyIndex(kLegacyIndex);
vault_keyset.SetKeyData(key_data);
ASSERT_EQ(vault_keyset.GetLabel(), kTempLabel);
}
TEST_F(VaultKeysetTest, GetEmptyLabelTest) {
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
KeyData key_data;
// Setting empty label.
key_data.set_label("");
vault_keyset.SetLegacyIndex(kLegacyIndex);
vault_keyset.SetKeyData(key_data);
ASSERT_EQ(vault_keyset.GetLabel(), kLegacyLabel);
}
TEST_F(VaultKeysetTest, InitializeToAdd) {
// Check if InitializeToAdd correctly copies keys
// from parameter vault keyset to underlying data structure.
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
const auto reset_iv = CreateSecureRandomBlob(kAesBlockSize);
static const int kFscryptPolicyVersion = 2;
vault_keyset.SetResetIV(reset_iv);
vault_keyset.SetFSCryptPolicyVersion(kFscryptPolicyVersion);
vault_keyset.SetLegacyIndex(kLegacyIndex);
KeyBlobs key_blobs = {.vkk_key = brillo::SecureBlob(32, 'A'),
.vkk_iv = brillo::SecureBlob(16, 'B'),
.chaps_iv = brillo::SecureBlob(16, 'C')};
TpmBoundToPcrAuthBlockState pcr_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
ASSERT_THAT(vault_keyset.EncryptEx(key_blobs, auth_state),
hwsec_foundation::error::testing::IsOk());
VaultKeyset vault_keyset_copy;
vault_keyset_copy.InitializeToAdd(vault_keyset);
// Check that InitializeToAdd correctly copied vault_keyset fields
// i.e. fek/fnek keys, reset seed, reset IV, and FSCrypt policy version
// FEK
ASSERT_EQ(vault_keyset.GetFek(), vault_keyset_copy.GetFek());
ASSERT_EQ(vault_keyset.GetFekSig(), vault_keyset_copy.GetFekSig());
ASSERT_EQ(vault_keyset.GetFekSalt(), vault_keyset_copy.GetFekSalt());
// FNEK
ASSERT_EQ(vault_keyset.GetFnek(), vault_keyset_copy.GetFnek());
ASSERT_EQ(vault_keyset.GetFnekSig(), vault_keyset_copy.GetFnekSig());
ASSERT_EQ(vault_keyset.GetFnekSalt(), vault_keyset_copy.GetFnekSalt());
// Other metadata
ASSERT_EQ(vault_keyset.GetResetSeed(), vault_keyset_copy.GetResetSeed());
ASSERT_EQ(vault_keyset.GetResetIV(), vault_keyset_copy.GetResetIV());
ASSERT_EQ(vault_keyset.GetChapsKey(), vault_keyset_copy.GetChapsKey());
ASSERT_EQ(vault_keyset.GetFSCryptPolicyVersion(),
vault_keyset_copy.GetFSCryptPolicyVersion());
// Other fields are empty/not changed/uninitialized
// i.e. the wrapped_keyset_ shouldn't be copied
ASSERT_NE(vault_keyset.GetWrappedKeyset(),
vault_keyset_copy.GetWrappedKeyset());
// int32_t flags_
ASSERT_NE(vault_keyset_copy.GetFlags(), vault_keyset.GetFlags());
// int legacy_index_
ASSERT_NE(vault_keyset_copy.GetLegacyIndex(), vault_keyset.GetLegacyIndex());
}
TEST_F(VaultKeysetTest, GetTpmWritePasswordRounds) {
// Test to ensure that for GetTpmNotBoundtoPcrState
// correctly copies the password_rounds field from
// the VaultKeyset to the auth_state parameter.
VaultKeyset keyset;
SerializedVaultKeyset serialized_vk;
serialized_vk.set_flags(SerializedVaultKeyset::TPM_WRAPPED);
serialized_vk.set_password_rounds(kPasswordRounds);
keyset.InitializeFromSerialized(serialized_vk);
keyset.Initialize(&platform_, &crypto_);
keyset.SetTPMKey(brillo::SecureBlob(kFakePasswordKey));
AuthBlockState tpm_state;
EXPECT_TRUE(GetAuthBlockState(keyset, tpm_state));
auto test_state =
std::get_if<TpmNotBoundToPcrAuthBlockState>(&tpm_state.state);
// test_state is of type TpmNotBoundToPcrAuthBlockState
ASSERT_EQ(keyset.GetPasswordRounds(), test_state->password_rounds.value());
}
TEST_F(VaultKeysetTest, DecryptionTestWithKeyBlobs) {
// Check that Decrypt returns the original keyset.
// Setup
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
SecureBlob bytes;
EXPECT_CALL(platform_, WriteFileAtomicDurable(FilePath(kFilePath), _, _))
.WillOnce(WithArg<1>(CopyToSecureBlob(&bytes)));
EXPECT_CALL(platform_, ReadFile(FilePath(kFilePath), _))
.WillOnce(WithArg<1>(CopyFromSecureBlob(&bytes)));
KeyBlobs key_blobs = {.vkk_key = brillo::SecureBlob(32, 'A'),
.vkk_iv = brillo::SecureBlob(16, 'B'),
.chaps_iv = brillo::SecureBlob(16, 'C')};
TpmBoundToPcrAuthBlockState pcr_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
ASSERT_TRUE(vault_keyset.EncryptEx(key_blobs, auth_state).ok());
EXPECT_TRUE(vault_keyset.Save(FilePath(kFilePath)));
EXPECT_EQ(vault_keyset.GetSourceFile(), FilePath(kFilePath));
SecureBlob original_data;
ASSERT_TRUE(vault_keyset.ToKeysBlob(&original_data));
// Test
VaultKeyset new_keyset;
new_keyset.Initialize(&platform_, &crypto_);
EXPECT_TRUE(new_keyset.Load(FilePath(kFilePath)));
ASSERT_TRUE(new_keyset.DecryptEx(key_blobs).ok());
// Verify
SecureBlob new_data;
ASSERT_TRUE(new_keyset.ToKeysBlob(&new_data));
EXPECT_EQ(new_data.size(), original_data.size());
ASSERT_TRUE(VaultKeysetTest::FindBlobInBlob(new_data, original_data));
}
TEST_F(VaultKeysetTest, DecryptWithAuthBlockFailNotLoaded) {
// Check to decrypt a VaultKeyset that hasn't been loaded yet.
VaultKeyset vault_keyset;
vault_keyset.Initialize(&platform_, &crypto_);
vault_keyset.CreateFromFileSystemKeyset(FileSystemKeyset::CreateRandom());
KeyBlobs key_blobs = {.vkk_key = brillo::SecureBlob(32, 'A'),
.vkk_iv = brillo::SecureBlob(16, 'B'),
.chaps_iv = brillo::SecureBlob(16, 'C')};
TpmBoundToPcrAuthBlockState pcr_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
EXPECT_TRUE(vault_keyset.EncryptEx(key_blobs, auth_state).ok());
CryptoStatus status = vault_keyset.DecryptEx(key_blobs);
// Load() needs to be called before decrypting the keyset.
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_crypto_error(), CryptoError::CE_OTHER_CRYPTO);
}
TEST_F(VaultKeysetTest, KeyData) {
VaultKeyset vk;
vk.Initialize(&platform_, &crypto_);
vk.SetLegacyIndex(0);
EXPECT_FALSE(vk.HasKeyData());
// When there's no key data stored, |GetKeyDataOrDefault()| should return an
// empty protobuf.
KeyData key_data = vk.GetKeyDataOrDefault();
EXPECT_FALSE(key_data.has_type());
EXPECT_FALSE(key_data.has_label());
KeyData key_data2;
key_data2.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data2.set_label("pin");
vk.SetKeyData(key_data2);
vk.SetLowEntropyCredential(true);
ASSERT_TRUE(vk.HasKeyData());
KeyData key_data3 = vk.GetKeyData();
KeyData key_data4 = vk.GetKeyDataOrDefault();
EXPECT_EQ(key_data3.has_type(), key_data4.has_type());
EXPECT_EQ(key_data3.type(), key_data4.type());
EXPECT_EQ(key_data3.has_label(), key_data4.has_label());
EXPECT_EQ(key_data3.label(), key_data4.label());
EXPECT_EQ(key_data3.has_policy(), key_data4.has_policy());
EXPECT_EQ(key_data3.policy().has_low_entropy_credential(),
key_data4.policy().has_low_entropy_credential());
EXPECT_EQ(key_data3.policy().low_entropy_credential(),
key_data4.policy().low_entropy_credential());
EXPECT_TRUE(key_data3.has_type());
EXPECT_EQ(key_data3.type(), KeyData::KEY_TYPE_PASSWORD);
EXPECT_TRUE(key_data3.has_label());
EXPECT_EQ(key_data3.label(), "pin");
EXPECT_TRUE(key_data3.has_policy());
EXPECT_TRUE(key_data3.policy().has_low_entropy_credential());
EXPECT_TRUE(key_data3.policy().low_entropy_credential());
}
TEST_F(VaultKeysetTest, TPMBoundToPCRAuthBlockTypeToVKFlagNoScrypt) {
VaultKeyset vault_keyset;
// TPMBoundToPCR test, no Scrypt derived.
TpmBoundToPcrAuthBlockState pcr_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by the TPM, and the
// keys were not derived using scrypt.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_EQ(SerializedVaultKeyset::TPM_WRAPPED,
(crypt_flags & SerializedVaultKeyset::TPM_WRAPPED));
EXPECT_EQ(SerializedVaultKeyset::PCR_BOUND,
(crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_FALSE(vault_keyset.HasTPMKey());
EXPECT_FALSE(vault_keyset.HasExtendedTPMKey());
EXPECT_FALSE(vault_keyset.HasTpmPublicKeyHash());
}
TEST_F(VaultKeysetTest, TPMBoundToPCRAuthBlockTypeToVKFlagScrypt) {
// TPMBoundToPCR test, Scrypt derived.
VaultKeyset vault_keyset;
brillo::SecureBlob tpm_key = brillo::SecureBlob("tpm_key");
brillo::SecureBlob extended_tpm_key = brillo::SecureBlob("extended_tpm_key");
brillo::SecureBlob tpm_public_key_hash =
brillo::SecureBlob("tpm_public_key_hash");
TpmBoundToPcrAuthBlockState pcr_state = {
.scrypt_derived = true,
.salt = brillo::SecureBlob("salt"),
.tpm_key = tpm_key,
.extended_tpm_key = extended_tpm_key,
.tpm_public_key_hash = tpm_public_key_hash};
AuthBlockState auth_state = {.state = pcr_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by the TPM, and
// the keys were derived using scrypt.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_EQ(SerializedVaultKeyset::SCRYPT_DERIVED,
(crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(SerializedVaultKeyset::TPM_WRAPPED,
(crypt_flags & SerializedVaultKeyset::TPM_WRAPPED));
EXPECT_EQ(SerializedVaultKeyset::PCR_BOUND,
(crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_TRUE(vault_keyset.HasTPMKey());
EXPECT_EQ(vault_keyset.GetTPMKey(), tpm_key);
EXPECT_TRUE(vault_keyset.HasExtendedTPMKey());
EXPECT_EQ(vault_keyset.GetExtendedTPMKey(), extended_tpm_key);
EXPECT_TRUE(vault_keyset.HasTpmPublicKeyHash());
EXPECT_EQ(vault_keyset.GetTpmPublicKeyHash(), tpm_public_key_hash);
}
TEST_F(VaultKeysetTest, TPMNotBoundToPCRAuthBlockTypeToVKFlagNoScrypt) {
VaultKeyset vault_keyset;
// TPMNotBoundToPCR test, no Scrypt derived.
TpmNotBoundToPcrAuthBlockState pcr_state = {.salt =
brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pcr_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by the TPM, and the
// keys were not derived using scrypt.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_EQ(SerializedVaultKeyset::TPM_WRAPPED,
(crypt_flags & SerializedVaultKeyset::TPM_WRAPPED));
EXPECT_FALSE(vault_keyset.HasTPMKey());
EXPECT_FALSE(vault_keyset.HasTpmPublicKeyHash());
}
TEST_F(VaultKeysetTest, TPMNotBoundToPCRAuthBlockTypeToVKFlagScrypt) {
// TPMNotBoundToPCR test, Scrypt derived.
VaultKeyset vault_keyset;
brillo::SecureBlob tpm_key = brillo::SecureBlob("tpm_key");
brillo::SecureBlob tpm_public_key_hash =
brillo::SecureBlob("tpm_public_key_hash");
TpmNotBoundToPcrAuthBlockState pcr_state = {
.scrypt_derived = true,
.salt = brillo::SecureBlob("salt"),
.tpm_key = tpm_key,
.tpm_public_key_hash = tpm_public_key_hash};
AuthBlockState auth_state = {.state = pcr_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by the TPM, and
// the keys were derived using scrypt.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(SerializedVaultKeyset::SCRYPT_DERIVED,
(crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(SerializedVaultKeyset::TPM_WRAPPED,
(crypt_flags & SerializedVaultKeyset::TPM_WRAPPED));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_TRUE(vault_keyset.HasTPMKey());
EXPECT_EQ(vault_keyset.GetTPMKey(), tpm_key);
EXPECT_TRUE(vault_keyset.HasTpmPublicKeyHash());
EXPECT_EQ(vault_keyset.GetTpmPublicKeyHash(), tpm_public_key_hash);
}
TEST_F(VaultKeysetTest, PinWeaverAuthBlockTypeToVKFlagNoValuesSet) {
VaultKeyset vault_keyset;
// PinWeaver test, no Scrypt derived.
PinWeaverAuthBlockState pin_weaver_state = {.salt =
brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = pin_weaver_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by Pin weave credentials.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(SerializedVaultKeyset::LE_CREDENTIAL,
(crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_FALSE(vault_keyset.HasLELabel());
EXPECT_FALSE(vault_keyset.HasResetSalt());
}
TEST_F(VaultKeysetTest, PinWeaverAuthBlockTypeToVKFlagValuesSet) {
// PinWeaver test.
VaultKeyset vault_keyset;
brillo::SecureBlob reset_salt = brillo::SecureBlob("reset_salt");
unsigned int le_label = 12345; // random number;
PinWeaverAuthBlockState pin_weaver_state = {
.le_label = le_label,
.salt = brillo::SecureBlob("salt"),
.reset_salt = reset_salt};
AuthBlockState auth_state = {.state = pin_weaver_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by the Pin weaver.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_EQ(SerializedVaultKeyset::LE_CREDENTIAL,
(crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_TRUE(vault_keyset.HasLELabel());
EXPECT_TRUE(vault_keyset.HasResetSalt());
EXPECT_EQ(vault_keyset.GetLELabel(), le_label);
EXPECT_EQ(vault_keyset.GetResetSalt(), reset_salt);
}
TEST_F(VaultKeysetTest, ScryptAuthBlockTypeToVKFlagValuesSet) {
// Scrypt test.
VaultKeyset vault_keyset;
ScryptAuthBlockState scrypt_state = {
.salt = brillo::SecureBlob("salt"),
};
AuthBlockState auth_state = {.state = scrypt_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by SCRYPT.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(SerializedVaultKeyset::SCRYPT_WRAPPED,
(crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
}
TEST_F(VaultKeysetTest, ChallengeCredentialAuthBlockTypeToVKFlagValuesSet) {
// ChallengeCredential test.
VaultKeyset vault_keyset;
ChallengeCredentialAuthBlockState challenge_credential_state = {
.keyset_challenge_info = structure::SignatureChallengeInfo{
.sealed_secret = hwsec::Tpm2PolicySignedData{},
}};
AuthBlockState auth_state = {.state = challenge_credential_state};
vault_keyset.SetAuthBlockState(auth_state);
// Check that the keyset was indeed wrapped by SCRYPT.
unsigned int crypt_flags = vault_keyset.GetFlags();
EXPECT_EQ(
SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED,
(crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::ECC));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_DERIVED));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::PCR_BOUND));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::SCRYPT_WRAPPED));
EXPECT_TRUE(vault_keyset.HasSignatureChallengeInfo());
}
TEST_F(VaultKeysetTest, TpmEccAuthBlockTypeToVKFlagNoValues) {
VaultKeyset vault_keyset;
// TpmEcc test. Ensure that all fields are set to what is expected.
TpmEccAuthBlockState ecc_state = {.salt = brillo::SecureBlob("salt")};
AuthBlockState auth_state = {.state = ecc_state};
vault_keyset.SetAuthBlockState(auth_state);
unsigned int crypt_flags = vault_keyset.GetFlags();
unsigned int tpm_ecc_require_flags = SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::PCR_BOUND |
SerializedVaultKeyset::ECC;
EXPECT_NE(0, (crypt_flags & tpm_ecc_require_flags));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_FALSE(vault_keyset.HasTPMKey());
EXPECT_FALSE(vault_keyset.HasExtendedTPMKey());
EXPECT_FALSE(vault_keyset.HasTpmPublicKeyHash());
EXPECT_FALSE(vault_keyset.HasPasswordRounds());
EXPECT_FALSE(vault_keyset.HasVkkIv());
}
TEST_F(VaultKeysetTest, TpmEccAuthBlockTypeToVKFlagHasValues) {
// TpmEcc test. Ensure all values are set correctly.
VaultKeyset vault_keyset;
brillo::SecureBlob tpm_key = brillo::SecureBlob("tpm_key");
brillo::SecureBlob extended_tpm_key = brillo::SecureBlob("extended_tpm_key");
brillo::SecureBlob tpm_public_key_hash =
brillo::SecureBlob("tpm_public_key_hash");
brillo::SecureBlob vkk_iv = brillo::SecureBlob("vkk_iv");
unsigned int passwords_round = 233; // random number;.
TpmEccAuthBlockState ecc_state = {.salt = brillo::SecureBlob("salt"),
.vkk_iv = vkk_iv,
.auth_value_rounds = passwords_round,
.sealed_hvkkm = tpm_key,
.extended_sealed_hvkkm = extended_tpm_key,
.tpm_public_key_hash = tpm_public_key_hash};
AuthBlockState auth_state = {.state = ecc_state};
vault_keyset.SetAuthBlockState(auth_state);
unsigned int crypt_flags = vault_keyset.GetFlags();
unsigned int tpm_ecc_require_flags = SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::PCR_BOUND |
SerializedVaultKeyset::ECC;
EXPECT_NE(0, (crypt_flags & tpm_ecc_require_flags));
EXPECT_EQ(0, (crypt_flags & SerializedVaultKeyset::LE_CREDENTIAL));
EXPECT_EQ(
0, (crypt_flags & SerializedVaultKeyset::SIGNATURE_CHALLENGE_PROTECTED));
EXPECT_TRUE(vault_keyset.HasTPMKey());
EXPECT_EQ(vault_keyset.GetTPMKey(), tpm_key);
EXPECT_TRUE(vault_keyset.HasExtendedTPMKey());
EXPECT_EQ(vault_keyset.GetExtendedTPMKey(), extended_tpm_key);
EXPECT_TRUE(vault_keyset.HasTpmPublicKeyHash());
EXPECT_EQ(vault_keyset.GetTpmPublicKeyHash(), tpm_public_key_hash);
EXPECT_TRUE(vault_keyset.HasPasswordRounds());
EXPECT_EQ(vault_keyset.GetPasswordRounds(), passwords_round);
EXPECT_TRUE(vault_keyset.HasVkkIv());
EXPECT_EQ(vault_keyset.GetVkkIv(), vkk_iv);
}
class LeCredentialsManagerTest : public ::testing::Test {
public:
LeCredentialsManagerTest()
: crypto_(&hwsec_, &pinweaver_, &cryptohome_keys_manager_, nullptr) {
EXPECT_CALL(cryptohome_keys_manager_, Init())
.WillOnce(Return()); // because HasCryptohomeKey returned false once.
EXPECT_CALL(hwsec_, IsEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsSealingSupported()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(pinweaver_, IsEnabled()).WillRepeatedly(ReturnValue(true));
// Raw pointer as crypto_ expects unique_ptr, which we will wrap this
// allocation into.
le_cred_manager_ = new MockLECredentialManager();
EXPECT_CALL(*le_cred_manager_, CheckCredential(_, _, _, _))
.WillRepeatedly(DoAll(
SetArgPointee<2>(
brillo::SecureBlob(HexDecode(kHexHighEntropySecret))),
SetArgPointee<3>(brillo::SecureBlob(HexDecode(kHexResetSecret))),
ReturnError<CryptohomeLECredError>()));
crypto_.set_le_manager_for_testing(
std::unique_ptr<LECredentialManager>(le_cred_manager_));
crypto_.Init();
pin_vault_keyset_.Initialize(&platform_, &crypto_);
}
~LeCredentialsManagerTest() override = default;
// Not copyable or movable
LeCredentialsManagerTest(const LeCredentialsManagerTest&) = delete;
LeCredentialsManagerTest& operator=(const LeCredentialsManagerTest&) = delete;
LeCredentialsManagerTest(LeCredentialsManagerTest&&) = delete;
LeCredentialsManagerTest& operator=(LeCredentialsManagerTest&&) = delete;
protected:
MockPlatform platform_;
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
Crypto crypto_;
MockLECredentialManager* le_cred_manager_;
base::test::TaskEnvironment task_environment_;
VaultKeyset pin_vault_keyset_;
const CryptohomeError::ErrorLocationPair kErrorLocationForTesting1 =
CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1),
std::string("Testing1"));
};
TEST_F(LeCredentialsManagerTest, EncryptWithKeyBlobs) {
EXPECT_CALL(*le_cred_manager_, InsertCredential(_, _, _, _, _, _, _))
.WillOnce(ReturnError<CryptohomeLECredError>());
pin_vault_keyset_.CreateFromFileSystemKeyset(
FileSystemKeyset::CreateRandom());
pin_vault_keyset_.SetLowEntropyCredential(true);
FakeFeaturesForTesting features;
auto auth_block = std::make_unique<PinWeaverAuthBlock>(features.async,
crypto_.le_manager());
AuthInput auth_input = {brillo::SecureBlob(HexDecode(kHexVaultKey)),
false,
Username("unused"),
ObfuscatedUsername("unused"),
/*reset_secret*/ std::nullopt,
pin_vault_keyset_.reset_seed_};
base::test::TestFuture<CryptohomeStatus, std::unique_ptr<KeyBlobs>,
std::unique_ptr<AuthBlockState>>
result;
auth_block->Create(auth_input, result.GetCallback());
ASSERT_TRUE(result.IsReady());
auto [status, key_blobs, auth_state] = result.Take();
ASSERT_THAT(status, IsOk());
EXPECT_TRUE(
std::holds_alternative<PinWeaverAuthBlockState>(auth_state->state));
EXPECT_TRUE(pin_vault_keyset_.EncryptEx(*key_blobs, *auth_state).ok());
EXPECT_TRUE(pin_vault_keyset_.HasResetSalt());
EXPECT_FALSE(pin_vault_keyset_.HasWrappedResetSeed());
EXPECT_FALSE(pin_vault_keyset_.GetAuthLocked());
const SerializedVaultKeyset& serialized = pin_vault_keyset_.ToSerialized();
EXPECT_FALSE(serialized.key_data().policy().auth_locked());
}
TEST_F(LeCredentialsManagerTest, EncryptWithKeyBlobsFailWithBadAuthState) {
EXPECT_CALL(*le_cred_manager_, InsertCredential(_, _, _, _, _, _, _))
.WillOnce(ReturnError<CryptohomeLECredError>(
kErrorLocationForTesting1, ErrorActionSet({PossibleAction::kFatal}),
LE_CRED_ERROR_NO_FREE_LABEL));
pin_vault_keyset_.CreateFromFileSystemKeyset(
FileSystemKeyset::CreateRandom());
pin_vault_keyset_.SetLowEntropyCredential(true);
brillo::SecureBlob reset_seed = CreateSecureRandomBlob(kAesBlockSize);
FakeFeaturesForTesting features;
auto auth_block = std::make_unique<PinWeaverAuthBlock>(features.async,
crypto_.le_manager());
AuthInput auth_input = {brillo::SecureBlob(44, 'A'),
false,
Username("unused"),
ObfuscatedUsername("unused"),
/*reset_secret*/ std::nullopt,
pin_vault_keyset_.GetResetSeed()};
base::test::TestFuture<CryptohomeStatus, std::unique_ptr<KeyBlobs>,
std::unique_ptr<AuthBlockState>>
result;
auth_block->Create(auth_input, result.GetCallback());
ASSERT_TRUE(result.IsReady());
auto [status, key_blobs, auth_state] = result.Take();
ASSERT_THAT(status, NotOk());
}
TEST_F(LeCredentialsManagerTest, EncryptWithKeyBlobsFailWithNoResetSeed) {
EXPECT_CALL(*le_cred_manager_, InsertCredential(_, _, _, _, _, _, _))
.Times(0);
pin_vault_keyset_.CreateFromFileSystemKeyset(
FileSystemKeyset::CreateRandom());
pin_vault_keyset_.SetLowEntropyCredential(true);
FakeFeaturesForTesting features;
auto auth_block = std::make_unique<PinWeaverAuthBlock>(features.async,
crypto_.le_manager());
AuthInput auth_input = {
brillo::SecureBlob(44, 'A'), false, Username("unused"),
ObfuscatedUsername("unused"),
/*reset_secret*/ std::nullopt,
/*reset_seed*/ std::nullopt};
base::test::TestFuture<CryptohomeStatus, std::unique_ptr<KeyBlobs>,
std::unique_ptr<AuthBlockState>>
result;
auth_block->Create(auth_input, result.GetCallback());
ASSERT_TRUE(result.IsReady());
auto [status, key_blobs, auth_state] = result.Take();
ASSERT_THAT(status, NotOk());
}
TEST_F(LeCredentialsManagerTest, DecryptWithKeyBlobs) {
VaultKeyset vk;
vk.Initialize(&platform_, &crypto_);
SerializedVaultKeyset serialized;
serialized.set_flags(SerializedVaultKeyset::LE_CREDENTIAL);
serialized.set_le_fek_iv(HexDecode(kHexFekIv));
serialized.set_le_chaps_iv(HexDecode(kHexChapsIv));
serialized.set_wrapped_keyset(HexDecode(kHexWrappedKeyset));
serialized.set_wrapped_chaps_key(HexDecode(kHexWrappedChapsKey));
serialized.set_salt(HexDecode(kHexSalt));
serialized.set_le_label(0644);
vk.InitializeFromSerialized(serialized);
FakeFeaturesForTesting features;
auto auth_block = std::make_unique<PinWeaverAuthBlock>(features.async,
crypto_.le_manager());
TestFuture<CryptohomeStatus, std::unique_ptr<KeyBlobs>,
std::optional<AuthBlock::SuggestedAction>>
result;
AuthInput auth_input = {brillo::SecureBlob(HexDecode(kHexVaultKey)), false};
AuthBlockState auth_state;
ASSERT_TRUE(vk.GetPinWeaverState(&auth_state));
auth_block->Derive(auth_input, auth_state, result.GetCallback());
ASSERT_TRUE(result.IsReady());
auto [status, key_blobs, suggested_action] = result.Take();
ASSERT_THAT(status, IsOk());
EXPECT_TRUE(vk.GetPinWeaverState(&auth_state));
EXPECT_TRUE(vk.DecryptVaultKeysetEx(*key_blobs).ok());
}
} // namespace cryptohome