blob: 20e2c336e99d5cc0156ae4f2bb07a36b86d7bcb0 [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.
// pinweaver_client is a command line tool for executing PinWeaver vendor
// specific commands to Cr50.
#include <base/command_line.h>
#include <base/json/json_writer.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/values.h>
#include <brillo/syslog_logging.h>
#include <openssl/sha.h>
#include <algorithm>
#include <cinttypes>
#include <memory>
#include "trunks/tpm_pinweaver.h"
#include "trunks/tpm_utility.h"
#include "trunks/trunks_client_test.h"
#include "trunks/trunks_factory_impl.h"
namespace {
enum return_codes {
EXIT_SUCCESS_RESERVED = EXIT_SUCCESS,
EXIT_FAILURE_RESERVED = EXIT_FAILURE,
EXIT_PINWEAVER_NOT_SUPPORTED = 2,
};
const uint8_t DEFAULT_BITS_PER_LEVEL = 2;
const uint8_t DEFAULT_HEIGHT = 6;
const uint8_t DEFAULT_LE_SECRET[PW_SECRET_SIZE] = {
// clang-format off
0xba, 0xbc, 0x98, 0x9d, 0x97, 0x20, 0xcf, 0xea,
0xaa, 0xbd, 0xb2, 0xe3, 0xe0, 0x2c, 0x5c, 0x55,
0x06, 0x60, 0x93, 0xbd, 0x07, 0xe2, 0xba, 0x92,
0x10, 0x19, 0x24, 0xb1, 0x29, 0x33, 0x5a, 0xe2
// clang-format on
};
const uint8_t DEFAULT_HE_SECRET[PW_SECRET_SIZE] = {
// clang-format off
0xe3, 0x46, 0xe3, 0x62, 0x01, 0x5d, 0xfe, 0x0a,
0xd3, 0x67, 0xd7, 0xef, 0xab, 0x01, 0xad, 0x0e,
0x3a, 0xed, 0xe8, 0x2f, 0x99, 0xd1, 0x2d, 0x13,
0x4d, 0x4e, 0xe4, 0x02, 0xbe, 0x71, 0x8e, 0x40
// clang-format on
};
const uint8_t DEFAULT_RESET_SECRET[PW_SECRET_SIZE] = {
// clang-format off
0x8c, 0x33, 0x8c, 0xa7, 0x0f, 0x81, 0xa4, 0xee,
0x24, 0xcd, 0x04, 0x84, 0x9c, 0xa8, 0xfd, 0xdd,
0x14, 0xb0, 0xad, 0xe6, 0xb7, 0x6a, 0x10, 0xfc,
0x03, 0x22, 0xcb, 0x71, 0x31, 0xd3, 0x74, 0xd6
// clang-format on
};
uint8_t protocol_version = PW_PROTOCOL_VERSION;
using trunks::CommandTransceiver;
using trunks::TrunksFactory;
using trunks::TrunksFactoryImpl;
void PrintUsage() {
puts("Usage:");
puts(" help - prints this help message.");
puts(" resettree [<bits_per_level> <height> --protocol=<protocol>]");
puts(" - sends a reset tree command.");
puts(" The default parameters are bits_per_level=2 height=6 protocol=");
puts(" PW_PROTOCOL_VERSION.");
puts(" insert [...] - sends an insert leaf command.");
puts(" remove [...] - sends an remove leaf command.");
puts(" auth [...] - sends an try auth command.");
puts(" resetleaf [...] - sends an reset auth command.");
puts(" getlog [...] - sends an get log command.");
puts(" replay [...] - sends an log replay command.");
puts(" selftest [--protocol=<version>] - runs a self test with the");
puts(" following commands:");
}
std::string HexEncode(const std::string& bytes) {
return base::HexEncode(bytes.data(), bytes.size());
}
std::string HexDecode(const std::string& hex) {
std::vector<uint8_t> output;
CHECK(base::HexStringToBytes(hex, &output));
return std::string(output.begin(), output.end());
}
std::string PwErrorStr(int code) {
switch (code) {
case 0:
return "EC_SUCCESS";
case 1:
return "EC_ERROR_UNKNOWN";
case 2:
return "EC_ERROR_UNIMPLEMENTED";
case PW_ERR_VERSION_MISMATCH:
return "PW_ERR_VERSION_MISMATCH";
case PW_ERR_LENGTH_INVALID:
return "PW_ERR_LENGTH_INVALID";
case PW_ERR_TYPE_INVALID:
return "PW_ERR_TYPE_INVALID";
case PW_ERR_BITS_PER_LEVEL_INVALID:
return "PW_ERR_BITS_PER_LEVEL_INVALID";
case PW_ERR_HEIGHT_INVALID:
return "PW_ERR_HEIGHT_INVALID";
case PW_ERR_LABEL_INVALID:
return "PW_ERR_LABEL_INVALID";
case PW_ERR_DELAY_SCHEDULE_INVALID:
return "PW_ERR_DELAY_SCHEDULE_INVALID";
case PW_ERR_PATH_AUTH_FAILED:
return "PW_ERR_PATH_AUTH_FAILED";
case PW_ERR_LEAF_VERSION_MISMATCH:
return "PW_ERR_LEAF_VERSION_MISMATCH";
case PW_ERR_HMAC_AUTH_FAILED:
return "PW_ERR_HMAC_AUTH_FAILED";
case PW_ERR_LOWENT_AUTH_FAILED:
return "PW_ERR_LOWENT_AUTH_FAILED";
case PW_ERR_RESET_AUTH_FAILED:
return "PW_ERR_RESET_AUTH_FAILED";
case PW_ERR_CRYPTO_FAILURE:
return "PW_ERR_CRYPTO_FAILURE";
case PW_ERR_RATE_LIMIT_REACHED:
return "PW_ERR_RATE_LIMIT_REACHED";
case PW_ERR_ROOT_NOT_FOUND:
return "PW_ERR_ROOT_NOT_FOUND";
case PW_ERR_NV_EMPTY:
return "PW_ERR_NV_EMPTY";
case PW_ERR_NV_LENGTH_MISMATCH:
return "PW_ERR_NV_LENGTH_MISMATCH";
case PW_ERR_NV_VERSION_MISMATCH:
return "PW_ERR_NV_VERSION_MISMATCH";
default:
return "?";
}
}
void GetEmptyPath(uint8_t bits_per_level, uint8_t height, std::string* h_aux) {
static_assert(SHA256_DIGEST_SIZE >= PW_HASH_SIZE, "");
std::vector<uint8_t> hash(SHA256_DIGEST_SIZE, 0);
uint8_t num_siblings = (1 << bits_per_level) - 1;
size_t level_size = num_siblings * PW_HASH_SIZE;
h_aux->resize(height * level_size);
for (auto level_ptr = h_aux->begin(); level_ptr < h_aux->end();
level_ptr += level_size) {
for (auto index_ptr = level_ptr; index_ptr < level_ptr + level_size;
index_ptr += PW_HASH_SIZE) {
std::copy(hash.begin(), hash.begin() + PW_HASH_SIZE, index_ptr);
}
SHA256_CTX ctx;
SHA256_Init(&ctx);
for (uint8_t x = 0; x <= num_siblings; ++x) {
SHA256_Update(&ctx, hash.data(), PW_HASH_SIZE);
}
SHA256_Final(hash.data(), &ctx);
}
}
void GetInsertLeafDefaults(uint64_t* label,
std::string* h_aux,
brillo::SecureBlob* le_secret,
brillo::SecureBlob* he_secret,
brillo::SecureBlob* reset_secret,
std::map<uint32_t, uint32_t>* delay_schedule,
trunks::ValidPcrCriteria* valid_pcr_criteria) {
*label = 0x1b1llu; // {0, 1, 2, 3, 0, 1}
GetEmptyPath(DEFAULT_BITS_PER_LEVEL, DEFAULT_HEIGHT, h_aux);
le_secret->assign(DEFAULT_LE_SECRET,
DEFAULT_LE_SECRET + sizeof(DEFAULT_LE_SECRET));
he_secret->assign(DEFAULT_HE_SECRET,
DEFAULT_HE_SECRET + sizeof(DEFAULT_HE_SECRET));
reset_secret->assign(DEFAULT_RESET_SECRET,
DEFAULT_RESET_SECRET + sizeof(DEFAULT_RESET_SECRET));
delay_schedule->clear();
delay_schedule->emplace(5, 20);
delay_schedule->emplace(6, 60);
delay_schedule->emplace(7, 300);
delay_schedule->emplace(8, 600);
delay_schedule->emplace(9, 1800);
delay_schedule->emplace(10, 3600);
delay_schedule->emplace(50, PW_BLOCK_ATTEMPTS);
valid_pcr_criteria->Clear();
if (protocol_version > 0) {
trunks::ValidPcrValue* default_pcr_value =
valid_pcr_criteria->add_valid_pcr_values();
uint8_t bitmask[2]{0, 0};
default_pcr_value->set_bitmask(&bitmask, sizeof(bitmask));
}
}
base::Value SetupBaseOutcome(uint32_t result_code, const std::string& root) {
// This is exported as a string because the API handles integers as signed.
base::Value outcome(base::Value::Type::DICTIONARY);
outcome.SetStringPath("result_code.value", std::to_string(result_code));
outcome.SetStringPath("result_code.name", PwErrorStr(result_code));
outcome.SetStringKey("root_hash", HexEncode(root));
return outcome;
}
std::string GetOutcomeJson(const base::Value& outcome) {
std::string json;
base::JSONWriter::WriteWithOptions(
outcome, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
return json;
}
int HandleResetTree(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
uint8_t bits_per_level;
uint8_t height;
if (begin == end) {
bits_per_level = DEFAULT_BITS_PER_LEVEL;
height = DEFAULT_HEIGHT;
} else if (end - begin != 2) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else {
bits_per_level = std::stoi(begin[0]);
height = std::stoi(begin[1]);
}
uint32_t result_code = 0;
std::string root;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverResetTree(
protocol_version, bits_per_level, height, &result_code, &root);
if (result) {
LOG(ERROR) << "PinWeaverResetTree: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleInsert(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
uint64_t label;
std::string h_aux;
brillo::SecureBlob le_secret;
brillo::SecureBlob he_secret;
brillo::SecureBlob reset_secret;
std::map<uint32_t, uint32_t> delay_schedule;
trunks::ValidPcrCriteria valid_pcr_criteria;
if (begin == end) {
GetInsertLeafDefaults(&label, &h_aux, &le_secret, &he_secret, &reset_secret,
&delay_schedule, &valid_pcr_criteria);
} else if (end - begin < 6) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else {
label = std::stoul(begin[0]);
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[1], &bytes))
return EXIT_FAILURE;
h_aux.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[2], &bytes))
return EXIT_FAILURE;
le_secret.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[3], &bytes))
return EXIT_FAILURE;
he_secret.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[4], &bytes))
return EXIT_FAILURE;
reset_secret.assign(bytes.begin(), bytes.end());
begin += 5;
for (size_t x = 0; x < end - begin; x += 2) {
delay_schedule.emplace(static_cast<uint32_t>(std::stoul(begin[x])),
static_cast<uint32_t>(std::stoul(begin[x + 1])));
}
}
uint32_t result_code = 0;
std::string root;
std::string cred_metadata;
std::string mac;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverInsertLeaf(
protocol_version, label, h_aux, le_secret, he_secret, reset_secret,
delay_schedule, valid_pcr_criteria, &result_code, &root, &cred_metadata,
&mac);
if (result) {
LOG(ERROR) << "PinWeaverInsertLeaf: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
outcome.SetStringKey("cred_metadata", HexEncode(cred_metadata));
outcome.SetStringKey("mac", HexEncode(mac));
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleRemove(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
if (end - begin != 3) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
}
uint64_t label = std::stoul(begin[0]);
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[1], &bytes))
return EXIT_FAILURE;
std::string h_aux(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[2], &bytes))
return EXIT_FAILURE;
std::string mac(bytes.begin(), bytes.end());
uint32_t result_code = 0;
std::string root;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverRemoveLeaf(
protocol_version, label, h_aux, mac, &result_code, &root);
if (result) {
LOG(ERROR) << "PinWeaverRemoveLeaf: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleAuth(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
std::string h_aux;
brillo::SecureBlob le_secret;
std::string cred_metadata;
if (end - begin != 3) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else {
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[0], &bytes))
return EXIT_FAILURE;
h_aux.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[1], &bytes))
return EXIT_FAILURE;
le_secret.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[2], &bytes))
return EXIT_FAILURE;
cred_metadata.assign(bytes.begin(), bytes.end());
}
uint32_t result_code = 0;
std::string root;
uint32_t seconds_to_wait;
brillo::SecureBlob he_secret;
brillo::SecureBlob reset_secret;
std::string cred_metadata_out;
std::string mac_out;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverTryAuth(
protocol_version, le_secret, h_aux, cred_metadata, &result_code, &root,
&seconds_to_wait, &he_secret, &reset_secret, &cred_metadata_out,
&mac_out);
if (result) {
LOG(ERROR) << "PinWeaverTryAuth: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
outcome.SetStringKey("seconds_to_wait", std::to_string(seconds_to_wait));
outcome.SetStringKey("he_secret", HexEncode(he_secret.to_string()));
outcome.SetStringKey("cred_metadata", HexEncode(cred_metadata_out));
outcome.SetStringKey("mac", HexEncode(mac_out));
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleResetLeaf(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
std::string h_aux;
brillo::SecureBlob reset_secret;
std::string cred_metadata;
if (end - begin != 3) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else {
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[0], &bytes))
return EXIT_FAILURE;
h_aux.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[1], &bytes))
return EXIT_FAILURE;
reset_secret.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[2], &bytes))
return EXIT_FAILURE;
cred_metadata.assign(bytes.begin(), bytes.end());
}
uint32_t result_code = 0;
std::string root;
brillo::SecureBlob he_secret;
std::string cred_metadata_out;
std::string mac_out;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverResetAuth(
protocol_version, reset_secret, h_aux, cred_metadata, &result_code, &root,
&he_secret, &cred_metadata_out, &mac_out);
if (result) {
LOG(ERROR) << "PinWeaverResetAuth: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
outcome.SetStringKey("he_secret", HexEncode(he_secret.to_string()));
outcome.SetStringKey("cred_metadata", HexEncode(cred_metadata_out));
outcome.SetStringKey("mac", HexEncode(mac_out));
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleGetLog(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
std::string root;
if (end - begin > 1) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else if (end == begin) {
root.assign(static_cast<size_t>(SHA256_DIGEST_SIZE), '\0');
} else {
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[0], &bytes))
return EXIT_FAILURE;
root.assign(bytes.begin(), bytes.end());
}
uint32_t result_code = 0;
std::string root_hash;
std::vector<trunks::PinWeaverLogEntry> log;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverGetLog(
protocol_version, root, &result_code, &root_hash, &log);
if (result) {
LOG(ERROR) << "PinWeaverGetLog: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
base::Value out_entries(base::Value::Type::LIST);
for (const auto& entry : log) {
base::Value out_entry(base::Value::Type::DICTIONARY);
out_entry.SetStringKey("label", std::to_string(entry.label()));
out_entry.SetStringKey("root", HexEncode(entry.root()));
switch (entry.type_case()) {
case trunks::PinWeaverLogEntry::TypeCase::kInsertLeaf:
out_entry.SetStringKey("type", "InsertLeaf");
out_entry.SetStringKey("hmac", HexEncode(entry.insert_leaf().hmac()));
break;
case trunks::PinWeaverLogEntry::TypeCase::kRemoveLeaf:
out_entry.SetStringKey("type", "RemoveLeaf");
break;
case trunks::PinWeaverLogEntry::TypeCase::kAuth:
out_entry.SetStringKey("type", "Auth");
out_entry.SetStringKey(
"timestamp.boot_count",
std::to_string(entry.auth().timestamp().boot_count()));
out_entry.SetStringKey(
"timestamp.timer_value",
std::to_string(entry.auth().timestamp().timer_value()));
out_entry.SetStringPath("return_code.value",
std::to_string(entry.auth().return_code()));
out_entry.SetStringPath(
"return_code.name",
trunks::GetErrorString(entry.auth().return_code()));
break;
case trunks::PinWeaverLogEntry::TypeCase::kResetTree:
out_entry.SetStringKey("type", "ResetTree");
break;
default:
out_entry.SetStringKey("type", std::to_string(entry.type_case()));
}
out_entries.Append(std::move(out_entry));
}
outcome.SetKey("entries", std::move(out_entries));
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleReplay(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
std::string h_aux;
std::string log_root;
std::string cred_metadata;
if (end - begin != 3) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
} else {
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(begin[0], &bytes))
return EXIT_FAILURE;
h_aux.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[1], &bytes))
return EXIT_FAILURE;
log_root.assign(bytes.begin(), bytes.end());
bytes.clear();
if (!base::HexStringToBytes(begin[2], &bytes))
return EXIT_FAILURE;
cred_metadata.assign(bytes.begin(), bytes.end());
}
uint32_t result_code = 0;
std::string root;
std::string cred_metadata_out;
std::string mac_out;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverLogReplay(
protocol_version, log_root, h_aux, cred_metadata, &result_code, &root,
&cred_metadata_out, &mac_out);
if (result) {
LOG(ERROR) << "PinWeaverResetAuth: " << trunks::GetErrorString(result);
}
base::Value outcome = SetupBaseOutcome(result_code, root);
outcome.SetStringKey("cred_metadata", HexEncode(cred_metadata_out));
outcome.SetStringKey("mac", HexEncode(mac_out));
puts(GetOutcomeJson(outcome).c_str());
return result;
}
int HandleSelfTest(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory) {
if (begin != end) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
}
LOG(INFO) << "reset_tree";
uint32_t result_code = 0;
std::string root;
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory->GetTpmUtility();
trunks::TPM_RC result =
tpm_utility->PinWeaverResetTree(protocol_version, DEFAULT_BITS_PER_LEVEL,
DEFAULT_HEIGHT, &result_code, &root);
if (result || result_code) {
LOG(ERROR) << "reset_tree failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
LOG(INFO) << "insert_leaf";
result_code = 0;
uint64_t label;
std::string h_aux;
brillo::SecureBlob le_secret;
brillo::SecureBlob he_secret;
brillo::SecureBlob reset_secret;
brillo::SecureBlob test_reset_secret;
std::map<uint32_t, uint32_t> delay_schedule;
trunks::ValidPcrCriteria valid_pcr_criteria;
GetInsertLeafDefaults(&label, &h_aux, &le_secret, &he_secret, &reset_secret,
&delay_schedule, &valid_pcr_criteria);
std::string cred_metadata;
std::string mac;
result = tpm_utility->PinWeaverInsertLeaf(
protocol_version, label, h_aux, le_secret, he_secret, reset_secret,
delay_schedule, valid_pcr_criteria, &result_code, &root, &cred_metadata,
&mac);
if (result || result_code) {
LOG(ERROR) << "insert_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
LOG(INFO) << "try_auth auth success";
result_code = 0;
uint32_t seconds_to_wait;
result = tpm_utility->PinWeaverTryAuth(
protocol_version, le_secret, h_aux, cred_metadata, &result_code, &root,
&seconds_to_wait, &he_secret, &test_reset_secret, &cred_metadata, &mac);
if (result || result_code) {
LOG(ERROR) << "try_auth failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
if (he_secret.size() != PW_SECRET_SIZE ||
std::mismatch(he_secret.begin(), he_secret.end(), DEFAULT_HE_SECRET)
.first != he_secret.end()) {
LOG(ERROR) << "try_auth credential retrieval failed!";
return EXIT_FAILURE;
}
if (protocol_version > 0 &&
(test_reset_secret.size() != PW_SECRET_SIZE ||
std::mismatch(test_reset_secret.begin(), test_reset_secret.end(),
DEFAULT_RESET_SECRET)
.first != test_reset_secret.end())) {
LOG(ERROR) << "try_auth reset_secret retrieval failed!";
return EXIT_FAILURE;
}
LOG(INFO) << "try_auth auth fail";
result_code = 0;
std::string pre_fail_root = root;
std::string old_metadata = cred_metadata;
brillo::SecureBlob wrong_le_secret = he_secret;
result = tpm_utility->PinWeaverTryAuth(
protocol_version, wrong_le_secret, h_aux, cred_metadata, &result_code,
&root, &seconds_to_wait, &he_secret, &test_reset_secret, &cred_metadata,
&mac);
if (result) {
LOG(ERROR) << "try_auth failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
// Most of the checks covered by the unit tests don't make sense to test here,
// but since authentication is critical this check is justified.
if (result_code != PW_ERR_LOWENT_AUTH_FAILED) {
LOG(ERROR) << "try_auth verification failed!";
return EXIT_FAILURE;
}
LOG(INFO) << "get_log";
result_code = 0;
std::vector<trunks::PinWeaverLogEntry> log;
result = tpm_utility->PinWeaverGetLog(protocol_version, pre_fail_root,
&result_code, &root, &log);
if (result || result_code) {
LOG(ERROR) << "get_log failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
bool fail = false;
if (log.empty()) {
LOG(ERROR) << "get_log verification failed: empty log!";
fail = true;
}
if (log.front().root() != root) {
LOG(ERROR) << "get_log verification failed: wrong root!";
LOG(ERROR) << HexEncode(log.front().root());
fail = true;
}
if (log.front().type_case() != trunks::PinWeaverLogEntry::TypeCase::kAuth) {
LOG(ERROR) << "get_log verification failed: wrong entry type!";
LOG(ERROR) << log.front().type_case();
fail = true;
}
if (log.front().auth().return_code() != PW_ERR_LOWENT_AUTH_FAILED) {
LOG(ERROR) << "get_log verification failed: wrong return code!";
LOG(ERROR) << PwErrorStr(log.front().auth().return_code());
fail = true;
}
if (fail) {
return EXIT_FAILURE;
}
LOG(INFO) << "log_replay";
result_code = 0;
std::string replay_metadata = cred_metadata;
std::string replay_mac = mac;
result = tpm_utility->PinWeaverLogReplay(protocol_version, root, h_aux,
old_metadata, &result_code, &root,
&replay_metadata, &replay_mac);
if (result) {
LOG(ERROR) << "log_replay failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
if (replay_metadata != cred_metadata) {
LOG(ERROR) << "log_replay verification failed: bad metadata!";
return EXIT_FAILURE;
}
if (replay_mac != mac) {
LOG(ERROR) << "log_replay verification failed: bad HMAC!";
return EXIT_FAILURE;
}
LOG(INFO) << "reset_auth";
result_code = 0;
result = tpm_utility->PinWeaverResetAuth(
protocol_version, reset_secret, h_aux, cred_metadata, &result_code, &root,
&he_secret, &cred_metadata, &mac);
if (result || result_code) {
LOG(ERROR) << "reset_auth failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
if (he_secret.size() != PW_SECRET_SIZE ||
std::mismatch(he_secret.begin(), he_secret.end(), DEFAULT_HE_SECRET)
.first != he_secret.end()) {
LOG(ERROR) << "reset_auth credential retrieval failed!";
return EXIT_FAILURE;
}
LOG(INFO) << "remove_leaf";
result_code = 0;
result = tpm_utility->PinWeaverRemoveLeaf(protocol_version, label, h_aux, mac,
&result_code, &root);
if (result || result_code) {
LOG(ERROR) << "remove_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
LOG(INFO) << "insert new leaf with good PCR (PCR4 must be empty)";
GetInsertLeafDefaults(&label, &h_aux, &le_secret, &he_secret, &reset_secret,
&delay_schedule, &valid_pcr_criteria);
if (protocol_version > 0) {
std::string digest = HexDecode(
"66687AADF862BD776C8FC18B8E9F8E20089714856EE233B3902A591D0D5F2925");
trunks::ValidPcrValue* value =
valid_pcr_criteria.mutable_valid_pcr_values(0);
const uint8_t bitmask[2] = {1 << 4 /* PCR 4 */, 0};
value->set_bitmask(&bitmask, sizeof(bitmask));
value->set_digest(digest);
}
result = tpm_utility->PinWeaverInsertLeaf(
protocol_version, label, h_aux, le_secret, he_secret, reset_secret,
delay_schedule, valid_pcr_criteria, &result_code, &root, &cred_metadata,
&mac);
if (result || result_code) {
LOG(ERROR) << "insert_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
LOG(INFO) << "try_auth should succeed";
result_code = 0;
he_secret.clear();
result = tpm_utility->PinWeaverTryAuth(
protocol_version, le_secret, h_aux, cred_metadata, &result_code, &root,
&seconds_to_wait, &he_secret, &reset_secret, &cred_metadata, &mac);
if (result || result_code) {
LOG(ERROR) << "try_auth failed";
return EXIT_FAILURE;
}
if (he_secret.size() != PW_SECRET_SIZE ||
std::mismatch(he_secret.begin(), he_secret.end(), DEFAULT_HE_SECRET)
.first != he_secret.end()) {
LOG(ERROR) << "try_auth credential retrieval failed!";
return EXIT_FAILURE;
}
LOG(INFO) << "remove_leaf";
result_code = 0;
result = tpm_utility->PinWeaverRemoveLeaf(protocol_version, label, h_aux, mac,
&result_code, &root);
if (result || result_code) {
LOG(ERROR) << "remove_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
if (protocol_version > 0) {
LOG(INFO) << "insert new leaf with bad PCR";
GetInsertLeafDefaults(&label, &h_aux, &le_secret, &he_secret, &reset_secret,
&delay_schedule, &valid_pcr_criteria);
trunks::ValidPcrValue* value =
valid_pcr_criteria.mutable_valid_pcr_values(0);
const uint8_t bitmask[2] = {16, 0};
value->set_bitmask(&bitmask, sizeof(bitmask));
value->set_digest("bad_digest");
result = tpm_utility->PinWeaverInsertLeaf(
protocol_version, label, h_aux, le_secret, he_secret, reset_secret,
delay_schedule, valid_pcr_criteria, &result_code, &root, &cred_metadata,
&mac);
if (result || result_code) {
LOG(ERROR) << "insert_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
LOG(INFO) << "try_auth should fail";
result_code = 0;
he_secret.clear();
replay_mac = mac;
result = tpm_utility->PinWeaverTryAuth(
protocol_version, le_secret, h_aux, cred_metadata, &result_code, &root,
&seconds_to_wait, &he_secret, &test_reset_secret, &cred_metadata, &mac);
if (!result && !result_code) {
LOG(ERROR) << "try_auth with wrong PCR failed to fail";
return EXIT_FAILURE;
}
// Make sure that he_secret was not leaked.
if (he_secret.size() > 0 || test_reset_secret.size() > 0) {
LOG(ERROR) << "try_auth populated the he_secret";
return EXIT_FAILURE;
}
LOG(INFO) << "remove_leaf";
result_code = 0;
result = tpm_utility->PinWeaverRemoveLeaf(protocol_version, label, h_aux,
replay_mac, &result_code, &root);
if (result || result_code) {
LOG(ERROR) << "remove_leaf failed! " << result_code << " "
<< PwErrorStr(result_code);
return EXIT_FAILURE;
}
}
puts("Success!");
return EXIT_SUCCESS;
}
} // namespace
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
brillo::InitLog(brillo::kLogToStderr);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
int requested_protocol = PW_PROTOCOL_VERSION;
if (cl->HasSwitch("protocol")) {
requested_protocol = std::min(
PW_PROTOCOL_VERSION, std::stoi(cl->GetSwitchValueASCII("protocol")));
}
const auto& args = cl->GetArgs();
if (args.size() < 1) {
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
}
const auto& command = args[0];
if (command == "help") {
puts("Pinweaver Client: A command line tool to invoke PinWeaver on Cr50.");
PrintUsage();
return EXIT_SUCCESS;
}
TrunksFactoryImpl factory;
CHECK(factory.Initialize()) << "Failed to initialize trunks factory.";
{
std::unique_ptr<trunks::TpmUtility> tpm_utility = factory.GetTpmUtility();
trunks::TPM_RC result = tpm_utility->PinWeaverIsSupported(
requested_protocol, &protocol_version);
if (result == trunks::SAPI_RC_ABI_MISMATCH) {
result = tpm_utility->PinWeaverIsSupported(0, &protocol_version);
}
if (result) {
LOG(ERROR) << "PinWeaver is not supported on this device!";
return EXIT_PINWEAVER_NOT_SUPPORTED;
}
protocol_version = std::min(protocol_version, (uint8_t)requested_protocol);
LOG(INFO) << "Protocol version: " << static_cast<int>(protocol_version);
}
auto command_args_start = args.begin() + 1;
const struct {
const std::string command;
int (*handler)(base::CommandLine::StringVector::const_iterator begin,
base::CommandLine::StringVector::const_iterator end,
TrunksFactoryImpl* factory);
} command_handlers[] = {
// clang-format off
{"resettree", HandleResetTree},
{"insert", HandleInsert},
{"remove", HandleRemove},
{"auth", HandleAuth},
{"resetleaf", HandleResetLeaf},
{"getlog", HandleGetLog},
{"replay", HandleReplay},
{"selftest", HandleSelfTest},
// clang-format on
};
for (const auto& command_handler : command_handlers) {
if (command_handler.command == command) {
return command_handler.handler(command_args_start, args.end(), &factory);
}
}
puts("Invalid options!");
PrintUsage();
return EXIT_FAILURE;
}