cr50_fuzz: Add libprotobuf-mutator support.
This uses protocol buffers to model what actions can be taken with pinweaver at a higher level of abstraction than the raw requests to greatly increase the coverage that can be achieved by fuzzing, while still allowing for invalid inputs to be checked. BRANCH=none BUG=chromium:876582 TEST=sudo emerge libprotobuf-mutator && make -j buildfuzztests && ./build/host/cr50_fuzz/cr50_fuzz.exe Change-Id: Ie7ce569650ca06866f277f36eae61df2684de60c Signed-off-by: Allen Webb <allenwebb@google.com> Reviewed-on: https://chromium-review.googlesource.com/1184107 Reviewed-by: Mattias Nissler <mnissler@chromium.org> Reviewed-by: Mike Frysinger <vapier@chromium.org>
This commit is contained in:
parent
b343c963b3
commit
a5e1a639e5
|
@ -20,8 +20,14 @@ fuzz-test-list-host = cr50_fuzz host_command_fuzz
|
|||
# Does your object file need to link against cstdlib?
|
||||
# Yes -> use <obj_name>-rw
|
||||
# Otherwise use <obj_name>-y
|
||||
cr50_fuzz-rw = cr50_fuzz.o
|
||||
cr50_fuzz-rw = cr50_fuzz.o pinweaver_model.o mem_hash_tree.o
|
||||
host_command_fuzz-y = host_command_fuzz.o
|
||||
|
||||
$(out)/cr50_fuzz.exe: $(out)/cryptoc/libcryptoc.a
|
||||
$(out)/cr50_fuzz.exe: LDFLAGS_EXTRA+=-lcrypto
|
||||
$(out)/RW/fuzz/cr50_fuzz.o: $(out)/gen/fuzz/cr50_fuzz.pb.h
|
||||
$(out)/RW/fuzz/cr50_fuzz.o: CPPFLAGS+=${LIBPROTOBUF_MUTATOR_CFLAGS}
|
||||
|
||||
$(out)/cr50_fuzz.exe: $(out)/cryptoc/libcryptoc.a \
|
||||
$(out)/gen/fuzz/cr50_fuzz.pb.o \
|
||||
$(out)/gen/fuzz/pinweaver/pinweaver.pb.o \
|
||||
|
||||
$(out)/cr50_fuzz.exe: LDFLAGS_EXTRA+=-lcrypto ${LIBPROTOBUF_MUTATOR_LDLIBS}
|
||||
|
|
|
@ -1,78 +1,97 @@
|
|||
/* 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.
|
||||
*
|
||||
* Fuzzer for the TPM2 and vendor specific Cr50 commands.
|
||||
*/
|
||||
// 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.
|
||||
|
||||
// Fuzzer for the TPM2 and vendor specific Cr50 commands.
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||
#include <src/mutator.h>
|
||||
|
||||
#define HIDE_EC_STDLIB
|
||||
#include "fuzz_config.h"
|
||||
#include "nvmem.h"
|
||||
#include "nvmem_vars.h"
|
||||
#include "persistence.h"
|
||||
#include "pinweaver.h"
|
||||
#include "chip/host/persistence.h"
|
||||
#include "fuzz/cr50_fuzz.pb.h"
|
||||
#include "fuzz/fuzz_config.h"
|
||||
#include "fuzz/pinweaver_model.h"
|
||||
#include "fuzz/span.h"
|
||||
#include "include/nvmem.h"
|
||||
#include "include/nvmem_vars.h"
|
||||
#include "include/pinweaver.h"
|
||||
|
||||
#define NVMEM_TPM_SIZE ((sizeof((struct nvmem_partition *)0)->buffer) \
|
||||
- NVMEM_CR50_SIZE)
|
||||
using protobuf_mutator::libfuzzer::LoadProtoInput;
|
||||
|
||||
extern "C" uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {
|
||||
NVMEM_TPM_SIZE,
|
||||
NVMEM_CR50_SIZE
|
||||
};
|
||||
namespace {
|
||||
constexpr size_t kBufferAlignment = alignof(pw_request_t) >
|
||||
alignof(pw_response_t)
|
||||
? alignof(pw_request_t)
|
||||
: alignof(pw_response_t);
|
||||
} // namespace
|
||||
|
||||
extern "C" void rand_bytes(void *buffer, size_t len)
|
||||
{
|
||||
size_t x = 0;
|
||||
extern "C" uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {NVMEM_TPM_SIZE,
|
||||
NVMEM_CR50_SIZE};
|
||||
|
||||
for (; x < len; ++x)
|
||||
((uint8_t *)buffer)[x] = rand();
|
||||
extern "C" void rand_bytes(void* data, size_t len) {
|
||||
size_t x = 0;
|
||||
|
||||
uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
|
||||
for (; x < len; ++x) {
|
||||
buffer[x] = rand();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void get_storage_seed(void *buf, size_t *len)
|
||||
{
|
||||
memset(buf, 0x77, *len);
|
||||
extern "C" void get_storage_seed(void* buf, size_t* len) {
|
||||
memset(buf, 0x77, *len);
|
||||
}
|
||||
|
||||
extern "C" uint8_t get_current_pcr_digest(const uint8_t bitmask[2],
|
||||
uint8_t sha256_of_selected_pcr[32])
|
||||
{
|
||||
memset(sha256_of_selected_pcr, 0, 32);
|
||||
return 0;
|
||||
uint8_t sha256_of_selected_pcr[32]) {
|
||||
memset(sha256_of_selected_pcr, 0, 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void run_test(void)
|
||||
{
|
||||
// Needed for test targets to build.
|
||||
extern "C" void run_test(void) {}
|
||||
|
||||
void InitializeFuzzerRun() {
|
||||
memset(__host_flash, 0xff, sizeof(__host_flash));
|
||||
nvmem_init();
|
||||
nvmem_enable_commits();
|
||||
initvars();
|
||||
srand(0);
|
||||
}
|
||||
|
||||
static void assign_pw_field_from_bytes(const uint8_t *data, unsigned int size,
|
||||
uint8_t *destination, size_t dest_size)
|
||||
{
|
||||
if (size >= dest_size) {
|
||||
memcpy(destination, data, dest_size);
|
||||
} else {
|
||||
memcpy(destination, data, size);
|
||||
memset(destination + size, 0, dest_size - size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent this from being stack allocated. */
|
||||
static uint8_t tpm_io_buffer[PW_MAX_MESSAGE_SIZE];
|
||||
|
||||
extern "C" int test_fuzz_one_input(const uint8_t *data, unsigned int size)
|
||||
{
|
||||
struct merkle_tree_t merkle_tree = {};
|
||||
struct pw_request_t *request = (struct pw_request_t *)tpm_io_buffer;
|
||||
struct pw_response_t *response = (struct pw_response_t *)tpm_io_buffer;
|
||||
|
||||
memset(__host_flash, 0xff, sizeof(__host_flash));
|
||||
pinweaver_init();
|
||||
assign_pw_field_from_bytes(data, size, tpm_io_buffer, sizeof(tpm_io_buffer));
|
||||
pw_handle_request(&merkle_tree, request, response);
|
||||
return 0;
|
||||
DEFINE_CUSTOM_PROTO_MUTATOR_IMPL(false, fuzz::Cr50FuzzerInput)
|
||||
DEFINE_CUSTOM_PROTO_CROSSOVER_IMPL(false, fuzz::Cr50FuzzerInput)
|
||||
|
||||
extern "C" int test_fuzz_one_input(const uint8_t* data, unsigned int size) {
|
||||
fuzz::Cr50FuzzerInput input;
|
||||
if (!LoadProtoInput(false, data, size, &input)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
InitializeFuzzerRun();
|
||||
|
||||
PinweaverModel pinweaver_model;
|
||||
alignas(kBufferAlignment) uint8_t buffer[PW_MAX_MESSAGE_SIZE] = {};
|
||||
fuzz::span<uint8_t> buffer_view(buffer, sizeof(buffer));
|
||||
for (const fuzz::Cr50SubAction& action : input.sub_actions()) {
|
||||
switch (action.sub_action_case()) {
|
||||
case fuzz::Cr50SubAction::kRandomBytes:
|
||||
fuzz::CopyWithPadding(action.random_bytes().value(), buffer_view, 0);
|
||||
pinweaver_model.SendBuffer(buffer_view);
|
||||
break;
|
||||
case fuzz::Cr50SubAction::kPinweaver:
|
||||
pinweaver_model.ApplyRequest(action.pinweaver(), buffer_view);
|
||||
break;
|
||||
case fuzz::Cr50SubAction::SUB_ACTION_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package fuzz;
|
||||
|
||||
import public "fuzz/pinweaver/pinweaver.proto";
|
||||
|
||||
message RandomBytes {
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message Cr50SubAction {
|
||||
// Allows a logical representation of an action (PinWeaver) or a literal
|
||||
// representation (RandomBytes). The logical representation fills out the
|
||||
// expected values of particular fields when they are empty or not part of the
|
||||
// proto so that the fuzzer can reach parts of the code without having to
|
||||
// brute force an HMAC. The literal representation allows for the fuzzer to
|
||||
// represent inputs that cannot be represented with the logical
|
||||
// representation.
|
||||
oneof sub_action {
|
||||
RandomBytes random_bytes = 1;
|
||||
pinweaver.Request pinweaver = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Cr50FuzzerInput {
|
||||
repeated Cr50SubAction sub_actions = 1;
|
||||
}
|
|
@ -61,6 +61,10 @@ enum nvmem_users {
|
|||
NVMEM_NUM_USERS
|
||||
};
|
||||
#endif
|
||||
|
||||
#define NVMEM_TPM_SIZE \
|
||||
(sizeof(((nvmem_partition *)(0))->buffer) - NVMEM_CR50_SIZE)
|
||||
|
||||
#define CONFIG_FLASH_NVMEM_VARS_USER_NUM NVMEM_CR50
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
// 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 "fuzz/mem_hash_tree.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
MemHashTree::MemHashTree() : bits_per_level_(0), height_(0) {}
|
||||
|
||||
bool MemHashTree::GetLeaf(uint64_t label, fuzz::span<uint8_t> leaf_hash) const {
|
||||
assert(leaf_hash.size() >= SHA256_DIGEST_SIZE);
|
||||
auto itr = hash_tree_.find(MaskedLabel(label, 0));
|
||||
if (itr == hash_tree_.end()) {
|
||||
std::fill(leaf_hash.begin(), leaf_hash.end(), 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy(itr->second.begin(), itr->second.end(), leaf_hash.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MemHashTree::GetPath(uint64_t label,
|
||||
fuzz::span<uint8_t> path_hashes) const {
|
||||
uint8_t fan_out = 1 << bits_per_level_;
|
||||
uint8_t num_siblings = fan_out - 1;
|
||||
assert(path_hashes.size() >= num_siblings * height_ * SHA256_DIGEST_SIZE);
|
||||
// num_siblings and child_index_mask have the same value, but were named
|
||||
// differently to help convey how they are used.
|
||||
uint64_t child_index_mask = fan_out - 1;
|
||||
uint64_t shifted_parent_label = label;
|
||||
uint8_t* dest_itr = path_hashes.begin();
|
||||
for (uint8_t level = 0; level < height_; ++level) {
|
||||
uint8_t label_index = shifted_parent_label & child_index_mask;
|
||||
shifted_parent_label &= ~child_index_mask;
|
||||
for (uint8_t index = 0; index < fan_out; ++index) {
|
||||
// Only include hashes for sibling nodes.
|
||||
if (index == label_index) {
|
||||
continue;
|
||||
}
|
||||
auto src_itr =
|
||||
hash_tree_.find(MaskedLabel(shifted_parent_label | index, level));
|
||||
if (src_itr == hash_tree_.end()) {
|
||||
std::copy(empty_node_hashes_[level].begin(),
|
||||
empty_node_hashes_[level].end(), dest_itr);
|
||||
} else {
|
||||
std::copy(src_itr->second.begin(), src_itr->second.end(), dest_itr);
|
||||
}
|
||||
dest_itr += SHA256_DIGEST_SIZE;
|
||||
}
|
||||
shifted_parent_label = shifted_parent_label >> bits_per_level_;
|
||||
}
|
||||
return dest_itr - path_hashes.begin();
|
||||
}
|
||||
|
||||
void MemHashTree::UpdatePath(uint64_t label,
|
||||
fuzz::span<const uint8_t> path_hash) {
|
||||
std::array<uint8_t, SHA256_DIGEST_SIZE> hash;
|
||||
if (path_hash.empty()) {
|
||||
std::fill(hash.begin(), hash.end(), 0);
|
||||
hash_tree_.erase(MaskedLabel(label, 0));
|
||||
} else {
|
||||
assert(path_hash.size() == SHA256_DIGEST_SIZE);
|
||||
std::copy(path_hash.begin(), path_hash.end(), hash.begin());
|
||||
hash_tree_[MaskedLabel(label, 0)] = hash;
|
||||
}
|
||||
|
||||
uint8_t fan_out = 1 << bits_per_level_;
|
||||
uint64_t child_index_mask = fan_out - 1;
|
||||
uint64_t shifted_parent_label = label;
|
||||
for (int level = 0; level < height_; ++level) {
|
||||
shifted_parent_label &= ~child_index_mask;
|
||||
|
||||
LITE_SHA256_CTX ctx;
|
||||
DCRYPTO_SHA256_init(&ctx, 1);
|
||||
int empty_nodes = 0;
|
||||
for (int index = 0; index < fan_out; ++index) {
|
||||
auto itr =
|
||||
hash_tree_.find(MaskedLabel(shifted_parent_label | index, level));
|
||||
if (itr == hash_tree_.end()) {
|
||||
HASH_update(&ctx, empty_node_hashes_[level].data(),
|
||||
empty_node_hashes_[level].size());
|
||||
++empty_nodes;
|
||||
} else {
|
||||
HASH_update(&ctx, itr->second.data(), itr->second.size());
|
||||
}
|
||||
}
|
||||
shifted_parent_label = shifted_parent_label >> bits_per_level_;
|
||||
|
||||
const uint8_t* temp = HASH_final(&ctx);
|
||||
std::copy(temp, temp + SHA256_DIGEST_SIZE, hash.begin());
|
||||
MaskedLabel node_key(shifted_parent_label, level + 1);
|
||||
if (empty_nodes == fan_out) {
|
||||
hash_tree_.erase(node_key);
|
||||
} else {
|
||||
hash_tree_[node_key] = hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemHashTree::Reset() {
|
||||
bits_per_level_ = 0;
|
||||
height_ = 0;
|
||||
empty_node_hashes_.clear();
|
||||
hash_tree_.clear();
|
||||
}
|
||||
|
||||
void MemHashTree::Reset(uint8_t bits_per_level, uint8_t height) {
|
||||
bits_per_level_ = bits_per_level;
|
||||
height_ = height;
|
||||
hash_tree_.clear();
|
||||
empty_node_hashes_.resize(height);
|
||||
|
||||
std::array<uint8_t, SHA256_DIGEST_SIZE> hash;
|
||||
std::fill(hash.begin(), hash.end(), 0);
|
||||
empty_node_hashes_[0] = hash;
|
||||
|
||||
uint8_t fan_out = 1 << bits_per_level;
|
||||
for (int level = 1; level < height; ++level) {
|
||||
LITE_SHA256_CTX ctx;
|
||||
DCRYPTO_SHA256_init(&ctx, 1);
|
||||
for (int index = 0; index < fan_out; ++index) {
|
||||
HASH_update(&ctx, hash.data(), hash.size());
|
||||
}
|
||||
const uint8_t* temp = HASH_final(&ctx);
|
||||
std::copy(temp, temp + SHA256_DIGEST_SIZE, hash.begin());
|
||||
empty_node_hashes_[level] = hash;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// 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.
|
||||
|
||||
#ifndef __FUZZ_MEM_HASH_TREE_H
|
||||
#define __FUZZ_MEM_HASH_TREE_H
|
||||
#include <unistd.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "board/host/dcrypto.h"
|
||||
#include "fuzz/span.h"
|
||||
|
||||
// MaskedLabel.first is the label path, this is shifted to the right by the
|
||||
// (bits_per_level * level)
|
||||
// MaskedLabel.second is the level of the label (0 for leaf, height for root)
|
||||
typedef std::pair<uint64_t, uint8_t> MaskedLabel;
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<MaskedLabel> {
|
||||
size_t operator()(const MaskedLabel& lbl) const {
|
||||
static const auto hash_first = hash<uint64_t>();
|
||||
static const auto hash_second = hash<uint8_t>();
|
||||
return hash_first(lbl.first) * hash_second(lbl.second);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
class MemHashTree {
|
||||
public:
|
||||
MemHashTree();
|
||||
|
||||
bool GetLeaf(uint64_t label, fuzz::span<uint8_t> leaf_hash) const;
|
||||
// Writes the result to |path_hashes| and returns the size in bytes of the
|
||||
// returned path for use in serializers that report how much buffer was used.
|
||||
size_t GetPath(uint64_t label, fuzz::span<uint8_t> path_hashes) const;
|
||||
// Updates the hashes in the path of the specified leaf. If |path_hash| is
|
||||
// empty, the entry in hash_tree_ is deleted representing an empty leaf.
|
||||
void UpdatePath(uint64_t label, fuzz::span<const uint8_t> path_hash);
|
||||
|
||||
void Reset();
|
||||
void Reset(uint8_t bits_per_level, uint8_t height);
|
||||
|
||||
private:
|
||||
uint8_t bits_per_level_;
|
||||
uint8_t height_;
|
||||
|
||||
// Only contains hashes for non empty paths in the tree.
|
||||
std::unordered_map<MaskedLabel, std::array<uint8_t, 32>> hash_tree_;
|
||||
std::vector<std::array<uint8_t, 32>> empty_node_hashes_;
|
||||
};
|
||||
|
||||
#endif // __FUZZ_MEM_HASH_TREE_H
|
|
@ -0,0 +1,64 @@
|
|||
// 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package fuzz.pinweaver;
|
||||
|
||||
import public "google/protobuf/wrappers.proto";
|
||||
|
||||
message ResetTree {
|
||||
uint32 bits_per_level = 1;
|
||||
uint32 height = 2;
|
||||
}
|
||||
|
||||
message InsertLeaf {
|
||||
uint64 label = 1;
|
||||
bytes delay_schedule = 2;
|
||||
bytes low_entropy_secret = 3;
|
||||
bytes high_entropy_secret = 4;
|
||||
bytes reset_secret = 5;
|
||||
bytes path_hashes = 6;
|
||||
}
|
||||
|
||||
message RemoveLeaf {
|
||||
uint64 label = 1;
|
||||
bytes leaf_hmac = 2;
|
||||
bytes path_hashes = 3;
|
||||
}
|
||||
|
||||
message TryAuth {
|
||||
uint64 label = 1;
|
||||
bytes low_entropy_secret = 2;
|
||||
bytes unimported_leaf_data = 3;
|
||||
}
|
||||
|
||||
message ResetAuth {
|
||||
uint64 label = 1;
|
||||
bytes reset_secret = 2;
|
||||
bytes unimported_leaf_data = 3;
|
||||
}
|
||||
|
||||
message GetLog {
|
||||
uint32 index_of_root = 1;
|
||||
}
|
||||
|
||||
message LogReplay {
|
||||
uint32 index_of_root = 1;
|
||||
bytes unimported_leaf_data = 2;
|
||||
}
|
||||
|
||||
message Request {
|
||||
// A work around to provide the has_version() function.
|
||||
google.protobuf.UInt32Value version = 1;
|
||||
oneof request {
|
||||
ResetTree reset_tree = 2;
|
||||
InsertLeaf insert_leaf = 3;
|
||||
RemoveLeaf remove_leaf = 4;
|
||||
TryAuth try_auth = 5;
|
||||
ResetAuth reset_auth = 6;
|
||||
GetLog get_log = 7;
|
||||
LogReplay log_replay = 8;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
// 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 "fuzz/pinweaver_model.h"
|
||||
|
||||
#include "board/host/dcrypto.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct pw_request_t* SerializeCommon(const fuzz::pinweaver::Request& pinweaver,
|
||||
pw_message_type_t message_type,
|
||||
fuzz::span<uint8_t> buffer) {
|
||||
struct pw_request_t* request =
|
||||
reinterpret_cast<struct pw_request_t*>(buffer.begin());
|
||||
if (pinweaver.has_version()) {
|
||||
request->header.version = pinweaver.version().value();
|
||||
} else {
|
||||
request->header.version = PW_PROTOCOL_VERSION;
|
||||
}
|
||||
request->header.type = message_type;
|
||||
return request;
|
||||
}
|
||||
|
||||
void CheckBuffer(fuzz::span<uint8_t> buffer) {
|
||||
uintptr_t ptr = reinterpret_cast<uintptr_t>(buffer.begin());
|
||||
assert(ptr % alignof(pw_request_t) == 0);
|
||||
assert(ptr % alignof(pw_response_t) == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//******************************************************************************
|
||||
// Public member functions.
|
||||
//******************************************************************************
|
||||
|
||||
PinweaverModel::PinweaverModel() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void PinweaverModel::SendBuffer(fuzz::span<uint8_t> buffer) {
|
||||
assert(sizeof(pw_request_t) <= buffer.size());
|
||||
assert(sizeof(pw_response_t) <= buffer.size());
|
||||
CheckBuffer(buffer);
|
||||
pw_request_t* request = reinterpret_cast<pw_request_t*>(buffer.begin());
|
||||
pw_response_t* response = reinterpret_cast<pw_response_t*>(buffer.begin());
|
||||
pw_handle_request(&merkle_tree_, request, response);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeRequest(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
assert(buffer.size() >= PW_MAX_MESSAGE_SIZE);
|
||||
CheckBuffer(buffer);
|
||||
switch (pinweaver.request_case()) {
|
||||
case fuzz::pinweaver::Request::kResetTree:
|
||||
return SerializeResetTree(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kInsertLeaf:
|
||||
return SerializeInsertLeaf(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kRemoveLeaf:
|
||||
return SerializeRemoveLeaf(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kTryAuth:
|
||||
return SerializeTryAuth(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kResetAuth:
|
||||
return SerializeResetAuth(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kGetLog:
|
||||
return SerializeGetLog(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::kLogReplay:
|
||||
return SerializeLogReplay(pinweaver, buffer);
|
||||
case fuzz::pinweaver::Request::REQUEST_NOT_SET:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t PinweaverModel::ApplyRequest(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) {
|
||||
SerializeRequest(pinweaver, buffer);
|
||||
LeafData leaf_data;
|
||||
|
||||
// Size and alignment of buffer are checked in SerializeRequest().
|
||||
pw_request_t* request = reinterpret_cast<pw_request_t*>(buffer.begin());
|
||||
pw_response_t* response = reinterpret_cast<pw_response_t*>(buffer.begin());
|
||||
|
||||
if (pinweaver.request_case() == fuzz::pinweaver::Request::kInsertLeaf) {
|
||||
pw_request_insert_leaf_t& insert = request->data.insert_leaf;
|
||||
std::copy(insert.low_entropy_secret,
|
||||
insert.low_entropy_secret + PW_SECRET_SIZE,
|
||||
leaf_data.low_entropy_secret.begin());
|
||||
std::copy(insert.reset_secret, insert.reset_secret + PW_SECRET_SIZE,
|
||||
leaf_data.reset_secret.begin());
|
||||
}
|
||||
|
||||
pw_handle_request(&merkle_tree_, request, response);
|
||||
if (response->header.result_code != EC_SUCCESS &&
|
||||
pinweaver.request_case() != fuzz::pinweaver::Request::kTryAuth) {
|
||||
return response->header.result_code;
|
||||
}
|
||||
|
||||
switch (pinweaver.request_case()) {
|
||||
case fuzz::pinweaver::Request::kResetTree:
|
||||
ApplyResetTree();
|
||||
break;
|
||||
case fuzz::pinweaver::Request::kInsertLeaf:
|
||||
ApplyInsertLeaf(pinweaver, *response, &leaf_data);
|
||||
break;
|
||||
case fuzz::pinweaver::Request::kRemoveLeaf:
|
||||
ApplyRemoveLeaf(pinweaver, *response);
|
||||
break;
|
||||
case fuzz::pinweaver::Request::kTryAuth:
|
||||
ApplyTryAuth(pinweaver, *response);
|
||||
break;
|
||||
case fuzz::pinweaver::Request::kResetAuth:
|
||||
ApplyResetAuth(pinweaver, *response);
|
||||
break;
|
||||
// GetLog and LogReplay have no side-effects so the model doesn't need
|
||||
// to be updated.
|
||||
case fuzz::pinweaver::Request::kGetLog:
|
||||
case fuzz::pinweaver::Request::kLogReplay:
|
||||
case fuzz::pinweaver::Request::REQUEST_NOT_SET:
|
||||
break;
|
||||
}
|
||||
return response->header.result_code;
|
||||
}
|
||||
|
||||
void PinweaverModel::Reset() {
|
||||
memset(&merkle_tree_, 0, sizeof(merkle_tree_));
|
||||
leaf_metadata_.clear();
|
||||
mem_hash_tree_.Reset();
|
||||
root_history_.clear();
|
||||
};
|
||||
|
||||
//******************************************************************************
|
||||
// Private static fields.
|
||||
//******************************************************************************
|
||||
|
||||
constexpr uint8_t PinweaverModel::kNullRootHash[PW_HASH_SIZE];
|
||||
|
||||
//******************************************************************************
|
||||
// Private member functions.
|
||||
//******************************************************************************
|
||||
|
||||
void PinweaverModel::GetHmac(const std::string& fuzzer_hmac,
|
||||
uint64_t label,
|
||||
fuzz::span<uint8_t> hmac) const {
|
||||
assert(hmac.size() == PW_HASH_SIZE);
|
||||
if (!fuzzer_hmac.empty()) {
|
||||
fuzz::CopyWithPadding(fuzzer_hmac, hmac, 0);
|
||||
return;
|
||||
}
|
||||
mem_hash_tree_.GetLeaf(label, hmac);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::CopyMetadata(
|
||||
uint64_t label,
|
||||
const LeafData& leaf_data,
|
||||
unimported_leaf_data_t* unimported_leaf_data,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const std::vector<uint8_t>& data = leaf_data.wrapped_data;
|
||||
memcpy(unimported_leaf_data, data.data(), data.size());
|
||||
|
||||
fuzz::span<uint8_t> path_hashes(
|
||||
reinterpret_cast<uint8_t*>(unimported_leaf_data) + data.size(),
|
||||
buffer.end());
|
||||
return data.size() + mem_hash_tree_.GetPath(label, path_hashes);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::GetMetadata(uint64_t label,
|
||||
unimported_leaf_data_t* unimported_leaf_data,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
auto itr = leaf_metadata_.find(label);
|
||||
if (itr == leaf_metadata_.end()) {
|
||||
assert(buffer.size() >= sizeof(wrapped_leaf_data_t));
|
||||
std::fill(buffer.begin(), buffer.begin() + sizeof(wrapped_leaf_data_t), 0);
|
||||
return sizeof(wrapped_leaf_data_t);
|
||||
}
|
||||
return CopyMetadata(label, itr->second, unimported_leaf_data, buffer);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::GetPath(const std::string& fuzzer_hashes,
|
||||
uint64_t label,
|
||||
fuzz::span<uint8_t> path_hashes) const {
|
||||
if (!fuzzer_hashes.empty()) {
|
||||
return fuzz::CopyWithPadding(fuzzer_hashes, path_hashes, 0);
|
||||
}
|
||||
return mem_hash_tree_.GetPath(label, path_hashes);
|
||||
}
|
||||
|
||||
void PinweaverModel::LogRootHash(fuzz::span<const uint8_t> root_hash,
|
||||
uint64_t label) {
|
||||
assert(root_hash.size() == PW_HASH_SIZE);
|
||||
std::pair<std::vector<uint8_t>, uint64_t> entry{
|
||||
{root_hash.begin(), root_hash.end()}, label};
|
||||
if (root_history_.size() == PW_LOG_ENTRY_COUNT) {
|
||||
root_history_.pop_front();
|
||||
}
|
||||
root_history_.emplace_back(std::array<uint8_t, PW_HASH_SIZE>{}, label);
|
||||
std::copy(root_hash.begin(), root_hash.end(),
|
||||
root_history_.back().first.begin());
|
||||
}
|
||||
|
||||
fuzz::span<const uint8_t> PinweaverModel::GetRootHashFromLog(
|
||||
size_t index) const {
|
||||
if (index >= root_history_.size()) {
|
||||
return fuzz::span<const uint8_t>(kNullRootHash, PW_HASH_SIZE);
|
||||
}
|
||||
return root_history_.rbegin()[index].first;
|
||||
}
|
||||
|
||||
uint64_t PinweaverModel::GetLabelFromLog(size_t index) const {
|
||||
if (index >= root_history_.size()) {
|
||||
return 0;
|
||||
}
|
||||
return root_history_.rbegin()[index].second;
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeResetTree(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::ResetTree& fuzzer_data = pinweaver.reset_tree();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_RESET_TREE}, buffer);
|
||||
pw_request_reset_tree_t* req_data = &request->data.reset_tree;
|
||||
|
||||
request->header.data_length = sizeof(*req_data);
|
||||
req_data->bits_per_level.v = fuzzer_data.bits_per_level();
|
||||
req_data->height.v = fuzzer_data.height();
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeInsertLeaf(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::InsertLeaf& fuzzer_data = pinweaver.insert_leaf();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_INSERT_LEAF}, buffer);
|
||||
pw_request_insert_leaf_t* req_data = &request->data.insert_leaf;
|
||||
|
||||
req_data->label.v = fuzzer_data.label();
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.delay_schedule(),
|
||||
fuzz::span<uint8_t>(reinterpret_cast<uint8_t*>(req_data->delay_schedule),
|
||||
sizeof(req_data->delay_schedule)),
|
||||
0);
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.low_entropy_secret(),
|
||||
fuzz::span<uint8_t>(req_data->low_entropy_secret, PW_SECRET_SIZE), 0);
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.high_entropy_secret(),
|
||||
fuzz::span<uint8_t>(req_data->high_entropy_secret, PW_SECRET_SIZE), 0);
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.reset_secret(),
|
||||
fuzz::span<uint8_t>(req_data->reset_secret, PW_SECRET_SIZE), 0);
|
||||
|
||||
fuzz::span<uint8_t> path_hashes(
|
||||
reinterpret_cast<uint8_t*>(req_data->path_hashes), buffer.end());
|
||||
size_t path_hash_size =
|
||||
GetPath(fuzzer_data.path_hashes(), fuzzer_data.label(), path_hashes);
|
||||
request->header.data_length = sizeof(*req_data) + path_hash_size;
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeRemoveLeaf(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::RemoveLeaf& fuzzer_data = pinweaver.remove_leaf();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_REMOVE_LEAF}, buffer);
|
||||
pw_request_remove_leaf_t* req_data = &request->data.remove_leaf;
|
||||
|
||||
req_data->leaf_location.v = fuzzer_data.label();
|
||||
GetHmac(fuzzer_data.leaf_hmac(), fuzzer_data.label(),
|
||||
fuzz::span<uint8_t>(req_data->leaf_hmac, PW_HASH_SIZE));
|
||||
|
||||
fuzz::span<uint8_t> path_hashes(
|
||||
reinterpret_cast<uint8_t*>(req_data->path_hashes), buffer.end());
|
||||
size_t path_hash_size =
|
||||
GetPath(fuzzer_data.path_hashes(), fuzzer_data.label(), path_hashes);
|
||||
request->header.data_length = sizeof(*req_data) + path_hash_size;
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeTryAuth(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::TryAuth& fuzzer_data = pinweaver.try_auth();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_TRY_AUTH}, buffer);
|
||||
pw_request_try_auth_t* req_data = &request->data.try_auth;
|
||||
|
||||
request->header.data_length =
|
||||
sizeof(*req_data) - sizeof(req_data->unimported_leaf_data);
|
||||
|
||||
auto itr = leaf_metadata_.find(fuzzer_data.label());
|
||||
if (fuzzer_data.low_entropy_secret().empty() && itr != leaf_metadata_.end()) {
|
||||
const auto& low_entropy_secret = itr->second.low_entropy_secret;
|
||||
std::copy(low_entropy_secret.begin(), low_entropy_secret.end(),
|
||||
req_data->low_entropy_secret);
|
||||
} else {
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.low_entropy_secret(),
|
||||
fuzz::span<uint8_t>(req_data->low_entropy_secret, PW_SECRET_SIZE), 0);
|
||||
}
|
||||
|
||||
if (fuzzer_data.unimported_leaf_data().empty() &&
|
||||
itr != leaf_metadata_.end()) {
|
||||
request->header.data_length +=
|
||||
CopyMetadata(fuzzer_data.label(), itr->second,
|
||||
&req_data->unimported_leaf_data, buffer);
|
||||
} else {
|
||||
request->header.data_length += fuzz::CopyWithPadding(
|
||||
fuzzer_data.unimported_leaf_data(),
|
||||
fuzz::span<uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(&req_data->unimported_leaf_data),
|
||||
sizeof(wrapped_leaf_data_t)),
|
||||
0);
|
||||
}
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeResetAuth(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::ResetAuth& fuzzer_data = pinweaver.reset_auth();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_RESET_AUTH}, buffer);
|
||||
pw_request_reset_auth_t* req_data = &request->data.reset_auth;
|
||||
|
||||
request->header.data_length =
|
||||
sizeof(*req_data) - sizeof(req_data->unimported_leaf_data);
|
||||
|
||||
auto itr = leaf_metadata_.find(fuzzer_data.label());
|
||||
if (fuzzer_data.reset_secret().empty() && itr != leaf_metadata_.end()) {
|
||||
const auto& reset_secret = itr->second.reset_secret;
|
||||
std::copy(reset_secret.begin(), reset_secret.end(), req_data->reset_secret);
|
||||
} else {
|
||||
fuzz::CopyWithPadding(
|
||||
fuzzer_data.reset_secret(),
|
||||
fuzz::span<uint8_t>(req_data->reset_secret, PW_SECRET_SIZE), 0);
|
||||
}
|
||||
|
||||
if (fuzzer_data.unimported_leaf_data().empty() &&
|
||||
itr != leaf_metadata_.end()) {
|
||||
request->header.data_length +=
|
||||
CopyMetadata(fuzzer_data.label(), itr->second,
|
||||
&req_data->unimported_leaf_data, buffer);
|
||||
} else {
|
||||
request->header.data_length += fuzz::CopyWithPadding(
|
||||
fuzzer_data.unimported_leaf_data(),
|
||||
fuzz::span<uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(&req_data->unimported_leaf_data),
|
||||
sizeof(wrapped_leaf_data_t)),
|
||||
0);
|
||||
}
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeGetLog(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::GetLog& fuzzer_data = pinweaver.get_log();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_GET_LOG}, buffer);
|
||||
pw_request_get_log_t* req_data = &request->data.get_log;
|
||||
|
||||
memcpy(req_data->root,
|
||||
GetRootHashFromLog(fuzzer_data.index_of_root()).begin(), PW_HASH_SIZE);
|
||||
request->header.data_length = sizeof(*req_data);
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
size_t PinweaverModel::SerializeLogReplay(
|
||||
const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const {
|
||||
const fuzz::pinweaver::LogReplay& fuzzer_data = pinweaver.log_replay();
|
||||
pw_request_t* request = SerializeCommon(pinweaver, {PW_LOG_REPLAY}, buffer);
|
||||
pw_request_log_replay_t* req_data = &request->data.log_replay;
|
||||
|
||||
memcpy(req_data->log_root,
|
||||
GetRootHashFromLog(fuzzer_data.index_of_root()).begin(), PW_HASH_SIZE);
|
||||
request->header.data_length =
|
||||
sizeof(*req_data) - sizeof(req_data->unimported_leaf_data);
|
||||
|
||||
if (fuzzer_data.unimported_leaf_data().empty()) {
|
||||
request->header.data_length +=
|
||||
GetMetadata(GetLabelFromLog(fuzzer_data.index_of_root()),
|
||||
&req_data->unimported_leaf_data, buffer);
|
||||
} else {
|
||||
request->header.data_length += fuzz::CopyWithPadding(
|
||||
fuzzer_data.unimported_leaf_data(),
|
||||
fuzz::span<uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(&req_data->unimported_leaf_data),
|
||||
sizeof(wrapped_leaf_data_t)),
|
||||
0);
|
||||
}
|
||||
|
||||
return request->header.data_length + sizeof(request->header);
|
||||
}
|
||||
|
||||
void PinweaverModel::UpdateMetadata(
|
||||
uint64_t label,
|
||||
const pw_response_header_t& header,
|
||||
const unimported_leaf_data_t* unimported_leaf_data,
|
||||
size_t unimported_leaf_data_length,
|
||||
const LeafData* leaf_data) {
|
||||
LogRootHash(fuzz::span<const uint8_t>(header.root, PW_HASH_SIZE), label);
|
||||
if (unimported_leaf_data) {
|
||||
const uint8_t* data =
|
||||
reinterpret_cast<const uint8_t*>(unimported_leaf_data);
|
||||
LeafData& stored_leaf_data = leaf_metadata_[label];
|
||||
if (leaf_data) {
|
||||
stored_leaf_data = *leaf_data;
|
||||
}
|
||||
stored_leaf_data.wrapped_data.assign(data,
|
||||
data + unimported_leaf_data_length);
|
||||
mem_hash_tree_.UpdatePath(
|
||||
label,
|
||||
fuzz::span<const uint8_t>(unimported_leaf_data->hmac, PW_HASH_SIZE));
|
||||
} else {
|
||||
leaf_metadata_.erase(label);
|
||||
mem_hash_tree_.UpdatePath(label, fuzz::span<const uint8_t>() /*path_hash*/);
|
||||
}
|
||||
}
|
||||
|
||||
void PinweaverModel::ApplyResetTree() {
|
||||
leaf_metadata_.clear();
|
||||
mem_hash_tree_.Reset(merkle_tree_.bits_per_level.v, merkle_tree_.height.v);
|
||||
}
|
||||
|
||||
void PinweaverModel::ApplyInsertLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response,
|
||||
const LeafData* leaf_data) {
|
||||
const pw_response_insert_leaf_t* resp = &response.data.insert_leaf;
|
||||
size_t unimported_leaf_data_length = response.header.data_length -
|
||||
sizeof(*resp) +
|
||||
sizeof(resp->unimported_leaf_data);
|
||||
UpdateMetadata(pinweaver.insert_leaf().label(), response.header,
|
||||
&resp->unimported_leaf_data, unimported_leaf_data_length,
|
||||
leaf_data);
|
||||
}
|
||||
|
||||
void PinweaverModel::ApplyRemoveLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response) {
|
||||
UpdateMetadata(pinweaver.remove_leaf().label(), response.header,
|
||||
nullptr /*unimported_leaf_data*/,
|
||||
0 /*unimported_leaf_data_length*/, nullptr /*leaf_data*/);
|
||||
}
|
||||
|
||||
void PinweaverModel::ApplyTryAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response) {
|
||||
const pw_response_try_auth_t* resp = &response.data.try_auth;
|
||||
|
||||
if (response.header.result_code != EC_SUCCESS &&
|
||||
response.header.result_code != PW_ERR_LOWENT_AUTH_FAILED) {
|
||||
return;
|
||||
}
|
||||
size_t unimported_leaf_data_length = response.header.data_length -
|
||||
sizeof(*resp) +
|
||||
sizeof(resp->unimported_leaf_data);
|
||||
UpdateMetadata(pinweaver.try_auth().label(), response.header,
|
||||
&resp->unimported_leaf_data, unimported_leaf_data_length,
|
||||
nullptr /*leaf_data*/);
|
||||
}
|
||||
|
||||
void PinweaverModel::ApplyResetAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response) {
|
||||
const pw_response_reset_auth_t* resp = &response.data.reset_auth;
|
||||
size_t unimported_leaf_data_length = response.header.data_length -
|
||||
sizeof(*resp) +
|
||||
sizeof(resp->unimported_leaf_data);
|
||||
UpdateMetadata(pinweaver.reset_auth().label(), response.header,
|
||||
&resp->unimported_leaf_data, unimported_leaf_data_length,
|
||||
nullptr /*leaf_data*/);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
// 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 specific model to facilitate fuzzing.
|
||||
|
||||
#ifndef __FUZZ_PINWEAVER_MODEL_H
|
||||
#define __FUZZ_PINWEAVER_MODEL_H
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#define HIDE_EC_STDLIB
|
||||
#include "fuzz/cr50_fuzz.pb.h"
|
||||
#include "fuzz/mem_hash_tree.h"
|
||||
#include "fuzz/span.h"
|
||||
#include "include/pinweaver.h"
|
||||
#include "include/pinweaver_types.h"
|
||||
|
||||
// Provides enough state tracking to send valid PinWeaver requests. This is
|
||||
// necessary because of the authentication dependent fields used by the Merkle
|
||||
// tree such as HMACs and a set of sibling path hashes that must be correct to
|
||||
// reach some parts of the PinWeaver code.
|
||||
class PinweaverModel {
|
||||
public:
|
||||
PinweaverModel();
|
||||
|
||||
void SendBuffer(fuzz::span<uint8_t> buffer);
|
||||
|
||||
// Converts the logical representation of a request used in fuzzing into bytes
|
||||
// that can be processed by the pinweaver code for fuzzing.
|
||||
size_t SerializeRequest(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
|
||||
// Executes a request in the form of a fuzz::pinweaver::Request proto, and
|
||||
// updates the model, so that future requests will be valid.
|
||||
uint32_t ApplyRequest(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer);
|
||||
|
||||
// Clears any state. This shoudl be called at the beginning of each fuzzing
|
||||
// iteration.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
static constexpr uint8_t kNullRootHash[PW_HASH_SIZE] = {};
|
||||
|
||||
struct LeafData {
|
||||
std::vector<uint8_t> wrapped_data;
|
||||
std::array<uint8_t, PW_SECRET_SIZE> low_entropy_secret;
|
||||
std::array<uint8_t, PW_SECRET_SIZE> reset_secret;
|
||||
};
|
||||
|
||||
// Functions for retrieving the current state of the metadata.
|
||||
void GetHmac(const std::string& fuzzer_hmac,
|
||||
uint64_t label,
|
||||
fuzz::span<uint8_t> hmac) const;
|
||||
size_t CopyMetadata(uint64_t label,
|
||||
const LeafData& leaf_data,
|
||||
unimported_leaf_data_t* unimported_leaf_data,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t GetMetadata(uint64_t label,
|
||||
unimported_leaf_data_t* unimported_leaf_data,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t GetPath(const std::string& fuzzer_hashes,
|
||||
uint64_t label,
|
||||
fuzz::span<uint8_t> path_hashes) const;
|
||||
|
||||
// Store copies of the root hash of the Merkle tree, and label of the leaf
|
||||
// associated with a request so that valid get log requests can be generated.
|
||||
void LogRootHash(fuzz::span<const uint8_t> root_hash, uint64_t label);
|
||||
// Retrieve a root hash from the log at the given index.
|
||||
fuzz::span<const uint8_t> GetRootHashFromLog(size_t index) const;
|
||||
// Retrieve a leaf label from the log at the given index.
|
||||
uint64_t GetLabelFromLog(size_t index) const;
|
||||
|
||||
// Helper functions used by SerializePinweaver to convert
|
||||
size_t SerializeResetTree(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeInsertLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeRemoveLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeTryAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeResetAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeGetLog(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
size_t SerializeLogReplay(const fuzz::pinweaver::Request& pinweaver,
|
||||
fuzz::span<uint8_t> buffer) const;
|
||||
|
||||
// Updates the metadata storage for a particular leaf. |leaf_data| is only
|
||||
// required for insert operations so the metadata, low_entropy_secret,
|
||||
// and reset_secret for the leaf can be retrieved to generate valid
|
||||
// authentication requests.
|
||||
void UpdateMetadata(uint64_t label,
|
||||
const pw_response_header_t& header,
|
||||
const unimported_leaf_data_t* unimported_leaf_data,
|
||||
size_t unimported_leaf_data_length,
|
||||
const LeafData* leaf_data);
|
||||
|
||||
// Helper functions for updating the state when responses are received.
|
||||
void ApplyResetTree();
|
||||
void ApplyInsertLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response,
|
||||
const LeafData* leaf_data);
|
||||
void ApplyRemoveLeaf(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response);
|
||||
void ApplyTryAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response);
|
||||
void ApplyResetAuth(const fuzz::pinweaver::Request& pinweaver,
|
||||
const pw_response_t& response);
|
||||
|
||||
merkle_tree_t merkle_tree_;
|
||||
|
||||
MemHashTree mem_hash_tree_;
|
||||
std::deque<std::pair<std::array<uint8_t, PW_HASH_SIZE>, uint64_t>>
|
||||
root_history_;
|
||||
std::unordered_map<uint64_t, LeafData> leaf_metadata_;
|
||||
};
|
||||
|
||||
#endif // __FUZZ_PINWEAVER_MODEL_H
|
|
@ -0,0 +1,56 @@
|
|||
// 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.
|
||||
|
||||
#ifndef __FUZZ_SPAN_H
|
||||
#define __FUZZ_SPAN_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace fuzz {
|
||||
|
||||
template <typename T>
|
||||
class span {
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
constexpr span() : span<T>(nullptr, nullptr) {}
|
||||
constexpr span(T* begin, size_t size) : begin_(begin), end_(begin + size) {}
|
||||
constexpr span(T* begin, T* end) : begin_(begin), end_(end) {}
|
||||
|
||||
template <class Container>
|
||||
constexpr span(Container& container)
|
||||
: begin_(container.begin()), end_(container.end()){};
|
||||
|
||||
constexpr T* begin() const { return begin_; }
|
||||
constexpr T* end() const { return end_; }
|
||||
|
||||
constexpr T* data() const { return begin_; }
|
||||
|
||||
constexpr bool empty() const { return begin_ == end_; }
|
||||
constexpr size_t size() const { return end_ - begin_; }
|
||||
|
||||
private:
|
||||
T* begin_;
|
||||
T* end_;
|
||||
};
|
||||
|
||||
template <typename Source, typename Destination>
|
||||
size_t CopyWithPadding(Source source,
|
||||
Destination destination,
|
||||
typename Destination::value_type fill_value) {
|
||||
if (source.size() >= destination.size()) {
|
||||
std::copy(source.begin(), source.begin() + destination.size(),
|
||||
destination.begin());
|
||||
return destination.size();
|
||||
}
|
||||
std::copy(source.begin(), source.end(), destination.begin());
|
||||
std::fill(destination.begin() + source.size(), destination.end(), fill_value);
|
||||
return source.size();
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
#endif // __FUZZ_SPAN_H
|
Loading…
Reference in New Issue