diff options
Diffstat (limited to 'fuzz/pinweaver_model.cc')
-rw-r--r-- | fuzz/pinweaver_model.cc | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/fuzz/pinweaver_model.cc b/fuzz/pinweaver_model.cc new file mode 100644 index 0000000000..43618e1fa4 --- /dev/null +++ b/fuzz/pinweaver_model.cc @@ -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*/); +} |