blob: 1ed79c4608156d6d2e57d2853be656a1efd72b89 [file] [log] [blame]
// Copyright 2018 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/pinweaver_le_credential_backend.h"
#include <stdint.h> // required by pinweaver_types.h
#include <algorithm>
#include <utility>
#include <base/check_op.h>
#include <base/logging.h>
#include <trunks/error_codes.h>
#include <trunks/pinweaver.pb.h>
#include <trunks/tpm_utility.h>
#include <trunks/trunks_factory.h>
extern "C" {
#define __packed __attribute((packed))
#define __aligned(x) __attribute((aligned(x)))
#include <trunks/cr50_headers/pinweaver_types.h>
}
#include "cryptohome/tpm2_impl.h"
namespace cryptohome {
namespace {
// Translates a pair of trunks error code and pinweaver status code to the
// appropriate LECredBackendError.
LECredBackendError ConvertStatus(const std::string& operation,
trunks::TPM_RC result,
int32_t pinweaver_status) {
if (result != trunks::TPM_RC_SUCCESS) {
LOG(ERROR) << "TPM error on pinweaver " << operation
<< " operation: " << trunks::GetErrorString(result);
return LE_TPM_ERROR_TPM_OP_FAILED;
}
if (pinweaver_status != 0 /* EC_SUCCESS */) {
LOG(WARNING) << "Pinweaver " << operation << ": status "
<< pinweaver_status;
}
switch (pinweaver_status) {
case 0: // EC_SUCCESS
return LE_TPM_SUCCESS;
case PW_ERR_LOWENT_AUTH_FAILED:
return LE_TPM_ERROR_INVALID_LE_SECRET;
case PW_ERR_RESET_AUTH_FAILED:
return LE_TPM_ERROR_INVALID_RESET_SECRET;
case PW_ERR_RATE_LIMIT_REACHED:
return LE_TPM_ERROR_TOO_MANY_ATTEMPTS;
case PW_ERR_PATH_AUTH_FAILED:
return LE_TPM_ERROR_HASH_TREE_SYNC;
// This could happen (by design) only if the device is hacked. Treat the
// error as like an invalid PIN was provided.
case PW_ERR_PCR_NOT_MATCH:
return LE_TPM_ERROR_PCR_NOT_MATCH;
}
LOG(ERROR) << "Pinweaver error on pinweaver " << operation
<< " operation: " << pinweaver_status;
return LE_TPM_ERROR_TPM_OP_FAILED;
}
std::string EncodeAuxHashes(const std::vector<std::vector<uint8_t>>& h_aux) {
std::string result;
result.reserve(h_aux.size() * PW_HASH_SIZE);
for (const std::vector<uint8_t>& hash : h_aux) {
CHECK_EQ(PW_HASH_SIZE, hash.size());
result.append(hash.begin(), hash.end());
}
return result;
}
std::vector<uint8_t> StringToBlob(const std::string& str) {
return std::vector<uint8_t>(str.begin(), str.end());
}
std::string BlobToString(const std::vector<uint8_t>& blob) {
return std::string(blob.begin(), blob.end());
}
void ConvertPinWeaverLogToLELog(
const std::vector<trunks::PinWeaverLogEntry>& orig_log,
std::vector<cryptohome::LELogEntry>* log) {
for (const auto& log_entry : orig_log) {
cryptohome::LELogEntry entry;
if (log_entry.has_insert_leaf()) {
entry.type = LE_LOG_INSERT;
entry.mac = StringToBlob(log_entry.insert_leaf().hmac());
} else if (log_entry.has_remove_leaf()) {
entry.type = LE_LOG_REMOVE;
} else if (log_entry.has_auth()) {
entry.type = LE_LOG_CHECK;
} else if (log_entry.has_reset_tree()) {
entry.type = LE_LOG_RESET;
} else {
entry.type = LE_LOG_INVALID;
}
entry.root = StringToBlob(log_entry.root());
entry.label = log_entry.label();
log->push_back(entry);
}
}
} // namespace
PinweaverLECredentialBackend::PinweaverLECredentialBackend(Tpm2Impl* tpm)
: tpm_(tpm) {}
bool PinweaverLECredentialBackend::Reset(std::vector<uint8_t>* new_root) {
return PerformPinweaverOperation(
"Reset", nullptr, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
trunks::TPM_RC result = tpm_utility->PinWeaverResetTree(
protocol_version_, kBitsPerLevel, kLengthLabels / kBitsPerLevel,
&pinweaver_status, &root);
*new_root = StringToBlob(root);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::IsSupported() {
return PerformPinweaverOperation(
"IsSupported", nullptr, [&](trunks::TpmUtility* tpm_utility) {
trunks::TPM_RC result = tpm_utility->PinWeaverIsSupported(
PW_PROTOCOL_VERSION, &protocol_version_);
if (result == trunks::SAPI_RC_ABI_MISMATCH)
result = tpm_utility->PinWeaverIsSupported(0, &protocol_version_);
if (result == trunks::TPM_RC_SUCCESS)
protocol_version_ = std::min(
protocol_version_, static_cast<uint8_t>(PW_PROTOCOL_VERSION));
return std::make_pair(result, 0 /* EC_SUCCESS */);
});
}
bool PinweaverLECredentialBackend::InsertCredential(
const uint64_t label,
const std::vector<std::vector<uint8_t>>& h_aux,
const brillo::SecureBlob& le_secret,
const brillo::SecureBlob& he_secret,
const brillo::SecureBlob& reset_secret,
const std::map<uint32_t, uint32_t>& delay_schedule,
const ValidPcrCriteria& valid_pcr_criteria,
std::vector<uint8_t>* cred_metadata,
std::vector<uint8_t>* mac,
std::vector<uint8_t>* new_root) {
trunks::ValidPcrCriteria pcr_criteria;
if (protocol_version_ > 0) {
for (ValidPcrValue value : valid_pcr_criteria) {
trunks::ValidPcrValue* new_value = pcr_criteria.add_valid_pcr_values();
new_value->set_bitmask(&value.bitmask, 2);
new_value->set_digest(value.digest);
}
}
return PerformPinweaverOperation(
"InsertCredential", nullptr, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
std::string cred_metadata_string;
std::string mac_string;
trunks::TPM_RC result = tpm_utility->PinWeaverInsertLeaf(
protocol_version_, label, EncodeAuxHashes(h_aux), le_secret,
he_secret, reset_secret, delay_schedule, pcr_criteria,
&pinweaver_status, &root, &cred_metadata_string, &mac_string);
*cred_metadata = StringToBlob(cred_metadata_string);
*mac = StringToBlob(mac_string);
*new_root = StringToBlob(root);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::NeedsPCRBinding(
const std::vector<uint8_t>& cred_metadata) {
if (protocol_version_ == 0)
return false;
const struct unimported_leaf_data_t* unimported =
reinterpret_cast<const struct unimported_leaf_data_t*>(
cred_metadata.data());
if (unimported->head.leaf_version.minor == 0 &&
unimported->head.leaf_version.major == 0)
return true;
if (cred_metadata.size() <
offsetof(unimported_leaf_data_t, payload) +
offsetof(leaf_public_data_t, valid_pcr_criteria) +
PW_MAX_PCR_CRITERIA_COUNT * sizeof(struct valid_pcr_value_t)) {
LOG(ERROR) << "PinweaverLECredentialBackend metadata too short "
<< cred_metadata.size();
return true;
}
const struct leaf_public_data_t* leaf_data =
reinterpret_cast<const struct leaf_public_data_t*>(unimported->payload);
return leaf_data->valid_pcr_criteria[0].bitmask[0] == 0 &&
leaf_data->valid_pcr_criteria[0].bitmask[1] == 0;
}
int PinweaverLECredentialBackend::GetWrongAuthAttempts(
const std::vector<uint8_t>& cred_metadata) {
// Just like in NeedsPCRBinding, the assumption is that leaf_public_data_t
// structure will have the existing part immutable in the future.
const struct unimported_leaf_data_t* unimported =
reinterpret_cast<const struct unimported_leaf_data_t*>(
cred_metadata.data());
if (cred_metadata.size() < offsetof(unimported_leaf_data_t, payload) +
offsetof(leaf_public_data_t, attempt_count) +
sizeof(struct attempt_count_t)) {
LOG(ERROR) << "GetWrongAuthAttempts metadata too short "
<< cred_metadata.size();
return -1;
}
const struct leaf_public_data_t* leaf_data =
reinterpret_cast<const struct leaf_public_data_t*>(unimported->payload);
return leaf_data->attempt_count.v;
}
bool PinweaverLECredentialBackend::CheckCredential(
const uint64_t label,
const std::vector<std::vector<uint8_t>>& h_aux,
const std::vector<uint8_t>& orig_cred_metadata,
const brillo::SecureBlob& le_secret,
std::vector<uint8_t>* new_cred_metadata,
std::vector<uint8_t>* new_mac,
brillo::SecureBlob* he_secret,
brillo::SecureBlob* reset_secret,
LECredBackendError* err,
std::vector<uint8_t>* new_root) {
return PerformPinweaverOperation(
"CheckCredential", err, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
uint32_t seconds_to_wait;
std::string cred_metadata_string;
std::string mac_string;
trunks::TPM_RC result = tpm_utility->PinWeaverTryAuth(
protocol_version_, le_secret, EncodeAuxHashes(h_aux),
BlobToString(orig_cred_metadata), &pinweaver_status, &root,
&seconds_to_wait, he_secret, reset_secret, &cred_metadata_string,
&mac_string);
*new_cred_metadata = StringToBlob(cred_metadata_string);
*new_mac = StringToBlob(mac_string);
*new_root = StringToBlob(root);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::ResetCredential(
const uint64_t label,
const std::vector<std::vector<uint8_t>>& h_aux,
const std::vector<uint8_t>& orig_cred_metadata,
const brillo::SecureBlob& reset_secret,
std::vector<uint8_t>* new_cred_metadata,
std::vector<uint8_t>* new_mac,
LECredBackendError* err,
std::vector<uint8_t>* new_root) {
return PerformPinweaverOperation(
"ResetCredential", err, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
brillo::SecureBlob he_secret;
std::string cred_metadata_string;
std::string mac_string;
trunks::TPM_RC result = tpm_utility->PinWeaverResetAuth(
protocol_version_, reset_secret, EncodeAuxHashes(h_aux),
BlobToString(orig_cred_metadata), &pinweaver_status, &root,
&he_secret, &cred_metadata_string, &mac_string);
*new_cred_metadata = StringToBlob(cred_metadata_string);
*new_mac = StringToBlob(mac_string);
*new_root = StringToBlob(root);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::RemoveCredential(
const uint64_t label,
const std::vector<std::vector<uint8_t>>& h_aux,
const std::vector<uint8_t>& mac,
std::vector<uint8_t>* new_root) {
return PerformPinweaverOperation(
"RemoveCredential", nullptr, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
trunks::TPM_RC result = tpm_utility->PinWeaverRemoveLeaf(
protocol_version_, label, EncodeAuxHashes(h_aux), BlobToString(mac),
&pinweaver_status, &root);
*new_root = StringToBlob(root);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::GetLog(
const std::vector<uint8_t>& cur_disk_root_hash,
std::vector<uint8_t>* root_hash,
std::vector<LELogEntry>* log) {
return PerformPinweaverOperation(
"GetLog", nullptr, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string cur_root = BlobToString(cur_disk_root_hash);
std::string root;
std::vector<trunks::PinWeaverLogEntry> log_ret;
trunks::TPM_RC result = tpm_utility->PinWeaverGetLog(
protocol_version_, cur_root, &pinweaver_status, &root, &log_ret);
*root_hash = StringToBlob(root);
ConvertPinWeaverLogToLELog(log_ret, log);
return std::make_pair(result, pinweaver_status);
});
}
bool PinweaverLECredentialBackend::ReplayLogOperation(
const std::vector<uint8_t>& log_entry_root,
const std::vector<std::vector<uint8_t>>& h_aux,
const std::vector<uint8_t>& orig_cred_metadata,
std::vector<uint8_t>* new_cred_metadata,
std::vector<uint8_t>* new_mac) {
return PerformPinweaverOperation(
"LogReplay", nullptr, [&](trunks::TpmUtility* tpm_utility) {
uint32_t pinweaver_status;
std::string root;
std::string cred_metadata_string;
std::string mac_string;
trunks::TPM_RC result = tpm_utility->PinWeaverLogReplay(
protocol_version_, BlobToString(log_entry_root),
EncodeAuxHashes(h_aux), BlobToString(orig_cred_metadata),
&pinweaver_status, &root, &cred_metadata_string, &mac_string);
*new_cred_metadata = StringToBlob(cred_metadata_string);
*new_mac = StringToBlob(mac_string);
return std::make_pair(result, pinweaver_status);
});
}
template <typename Operation>
bool PinweaverLECredentialBackend::PerformPinweaverOperation(
const std::string& name, LECredBackendError* err, Operation op) {
Tpm2Impl::TrunksClientContext* trunks = nullptr;
if (!tpm_->GetTrunksContext(&trunks)) {
LOG(ERROR) << "Error getting trunks context for " << name;
return false;
}
if (protocol_version_ > PW_PROTOCOL_VERSION && name != "IsSupported") {
LOG(ERROR) << "Protocol version not initialized for " << name;
return false;
}
std::pair<trunks::TPM_RC, int32_t> status = op(trunks->tpm_utility.get());
LECredBackendError error = ConvertStatus(name, status.first, status.second);
if (err) {
*err = error;
}
return error == LE_TPM_SUCCESS;
}
} // namespace cryptohome