blob: 7a21d19ee0e5ea9cea787bfe8ab55883a9e7c471 [file] [log] [blame]
// 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/pin_weaver_auth_block.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/tpm.h"
#include "cryptohome/vault_keyset.h"
#include <map>
#include <string>
#include <base/optional.h>
#include <brillo/secure_blob.h>
#include "cryptohome/vault_keyset.pb.h"
namespace cryptohome {
namespace {
constexpr int kDefaultSecretSize = 32;
CryptoError ConvertLeError(int le_error) {
switch (le_error) {
case LE_CRED_ERROR_INVALID_LE_SECRET:
return CryptoError::CE_LE_INVALID_SECRET;
case LE_CRED_ERROR_TOO_MANY_ATTEMPTS:
return CryptoError::CE_TPM_DEFEND_LOCK;
case LE_CRED_ERROR_INVALID_LABEL:
return CryptoError::CE_OTHER_FATAL;
case LE_CRED_ERROR_HASH_TREE:
return CryptoError::CE_OTHER_FATAL;
case LE_CRED_ERROR_PCR_NOT_MATCH:
// We might want to return an error here that will make the device
// reboot.
LOG(ERROR) << "PCR in unexpected state.";
return CryptoError::CE_LE_INVALID_SECRET;
default:
return CryptoError::CE_OTHER_FATAL;
}
}
void LogLERetCode(int le_error) {
switch (le_error) {
case LE_CRED_ERROR_NO_FREE_LABEL:
LOG(ERROR) << "No free label available in hash tree.";
break;
case LE_CRED_ERROR_HASH_TREE:
LOG(ERROR) << "Hash tree error.";
break;
}
}
bool GetValidPCRValues(const std::string& obfuscated_username,
ValidPcrCriteria* valid_pcr_criteria) {
brillo::Blob default_pcr_str(std::begin(kDefaultPcrValue),
std::end(kDefaultPcrValue));
// The digest used for validation of PCR values by pinweaver is sha256 of
// the current PCR value of index 4.
// Step 1 - calculate expected values of PCR4 initially
// (kDefaultPcrValue = 0) and after user logins
// (sha256(initial_value | user_specific_digest)).
// Step 2 - calculate digest of those values, to support multi-PCR case,
// where all those expected values for all PCRs are sha256'ed togetheri
brillo::Blob default_digest = CryptoLib::Sha256(default_pcr_str);
// The second valid digest is the one obtained from the future value of
// PCR4, after it's extended by |obfuscated_username|. Compute the value of
// PCR4 after it will be extended first, which is
// sha256(default_value + sha256(extend_text)).
brillo::Blob obfuscated_username_digest = CryptoLib::Sha256(
brillo::Blob(obfuscated_username.begin(), obfuscated_username.end()));
brillo::Blob combined_pcr_and_username(default_pcr_str);
combined_pcr_and_username.insert(
combined_pcr_and_username.end(),
std::make_move_iterator(obfuscated_username_digest.begin()),
std::make_move_iterator(obfuscated_username_digest.end()));
brillo::Blob extended_arc_pcr_value =
CryptoLib::Sha256(combined_pcr_and_username);
// The second valid digest used by pinweaver for validation will be
// sha256 of the extended value of pcr4.
brillo::Blob extended_digest = CryptoLib::Sha256(extended_arc_pcr_value);
ValidPcrValue default_pcr_value;
memset(default_pcr_value.bitmask, 0, 2);
default_pcr_value.bitmask[kTpmSingleUserPCR / 8] = 1u << kTpmSingleUserPCR;
default_pcr_value.digest =
std::string(default_digest.begin(), default_digest.end());
valid_pcr_criteria->push_back(default_pcr_value);
ValidPcrValue extended_pcr_value;
memset(extended_pcr_value.bitmask, 0, 2);
extended_pcr_value.bitmask[kTpmSingleUserPCR / 8] = 1u << kTpmSingleUserPCR;
extended_pcr_value.digest =
std::string(extended_digest.begin(), extended_digest.end());
valid_pcr_criteria->push_back(extended_pcr_value);
return true;
}
// String used as vector in HMAC operation to derive vkk_seed from High Entropy
// secret.
const char kHESecretHmacData[] = "vkk_seed";
// A default delay schedule to be used for LE Credentials.
// The format for a delay schedule entry is as follows:
//
// (number_of_incorrect_attempts, delay before_next_attempt)
//
// The default schedule is for the first 5 incorrect attempts to have no delay,
// and no further attempts allowed.
const struct {
uint32_t attempts;
uint32_t delay;
} kDefaultDelaySchedule[] = {
{5, UINT32_MAX},
};
} // namespace
PinWeaverAuthBlock::PinWeaverAuthBlock(LECredentialManager* le_manager,
TpmInit* tpm_init)
: le_manager_(le_manager), tpm_init_(tpm_init) {
CHECK_NE(le_manager, nullptr);
CHECK_NE(tpm_init, nullptr);
}
base::Optional<AuthBlockState> PinWeaverAuthBlock::Create(
const AuthInput& auth_input, KeyBlobs* key_blobs, CryptoError* error) {
DCHECK(key_blobs);
// TODO(kerrnel): This may not be needed, but is currently retained to
// maintain the original logic.
if (!tpm_init_->HasCryptohomeKey())
tpm_init_->SetupTpm(/*load_key=*/true);
brillo::SecureBlob le_secret(kDefaultSecretSize);
brillo::SecureBlob kdf_skey(kDefaultSecretSize);
brillo::SecureBlob le_iv(kAesBlockSize);
if (!CryptoLib::DeriveSecretsScrypt(auth_input.user_input.value(),
auth_input.salt.value(),
{&le_secret, &kdf_skey, &le_iv})) {
return base::nullopt;
}
// Create a randomly generated high entropy secret, derive VKKSeed from it,
// and use that to generate a VKK. The HE secret will be stored in the
// LECredentialManager, along with the LE secret (which is |le_secret| here).
brillo::SecureBlob he_secret =
CryptoLib::CreateSecureRandomBlob(kDefaultSecretSize);
// Derive the VKK_seed by performing an HMAC on he_secret.
brillo::SecureBlob vkk_seed = CryptoLib::HmacSha256(
he_secret, brillo::BlobFromString(kHESecretHmacData));
// Generate and store random new IVs for file-encryption keys and
// chaps key encryption.
const auto fek_iv = CryptoLib::CreateSecureRandomBlob(kAesBlockSize);
const auto chaps_iv = CryptoLib::CreateSecureRandomBlob(kAesBlockSize);
SerializedVaultKeyset serialized;
serialized.set_le_fek_iv(fek_iv.data(), fek_iv.size());
serialized.set_le_chaps_iv(chaps_iv.data(), chaps_iv.size());
brillo::SecureBlob vkk_key = CryptoLib::HmacSha256(kdf_skey, vkk_seed);
key_blobs->vkk_key = vkk_key;
key_blobs->vkk_iv = fek_iv;
key_blobs->chaps_iv = chaps_iv;
key_blobs->auth_iv = le_iv;
// Once we are able to correctly set up the VaultKeyset encryption,
// store the LE and HE credential in the LECredentialManager.
// Use the default delay schedule for now.
std::map<uint32_t, uint32_t> delay_sched;
for (const auto& entry : kDefaultDelaySchedule) {
delay_sched[entry.attempts] = entry.delay;
}
brillo::SecureBlob reset_secret = auth_input.reset_secret.value();
ValidPcrCriteria valid_pcr_criteria;
if (!GetValidPCRValues(auth_input.obfuscated_username.value(),
&valid_pcr_criteria)) {
return base::nullopt;
}
uint64_t label;
int ret =
le_manager_->InsertCredential(le_secret, he_secret, reset_secret,
delay_sched, valid_pcr_criteria, &label);
if (ret != LE_CRED_SUCCESS) {
LogLERetCode(ret);
PopulateError(error, ConvertLeError(ret));
return base::nullopt;
}
serialized.set_flags(SerializedVaultKeyset::LE_CREDENTIAL);
serialized.set_le_label(label);
return {{serialized}};
}
bool PinWeaverAuthBlock::Derive(const AuthInput& auth_input,
const AuthBlockState& state,
KeyBlobs* key_blobs,
CryptoError* error) {
DCHECK(key_blobs);
DCHECK(state.vault_keyset != base::nullopt);
const SerializedVaultKeyset& serialized = state.vault_keyset.value();
CHECK(serialized.flags() & SerializedVaultKeyset::LE_CREDENTIAL);
brillo::SecureBlob le_secret(kDefaultAesKeySize);
brillo::SecureBlob kdf_skey(kDefaultAesKeySize);
brillo::SecureBlob le_iv(kAesBlockSize);
brillo::SecureBlob salt(serialized.salt().begin(), serialized.salt().end());
if (!CryptoLib::DeriveSecretsScrypt(auth_input.user_input.value(), salt,
{&le_secret, &kdf_skey, &le_iv})) {
PopulateError(error, CryptoError::CE_OTHER_FATAL);
return false;
}
key_blobs->reset_secret = brillo::SecureBlob();
key_blobs->chaps_iv = brillo::SecureBlob(serialized.le_chaps_iv().begin(),
serialized.le_chaps_iv().end());
// Try to obtain the HE Secret from the LECredentialManager.
brillo::SecureBlob he_secret;
int ret =
le_manager_->CheckCredential(serialized.le_label(), le_secret, &he_secret,
&key_blobs->reset_secret.value());
if (ret != LE_CRED_SUCCESS) {
PopulateError(error, ConvertLeError(ret));
return false;
}
key_blobs->vkk_iv = brillo::SecureBlob(serialized.le_fek_iv().begin(),
serialized.le_fek_iv().end());
brillo::SecureBlob vkk_seed = CryptoLib::HmacSha256(
he_secret, brillo::BlobFromString(kHESecretHmacData));
key_blobs->vkk_key = CryptoLib::HmacSha256(kdf_skey, vkk_seed);
return true;
}
} // namespace cryptohome