diff options
-rw-r--r-- | fuzz/build.mk | 18 | ||||
-rw-r--r-- | fuzz/cr50_fuzz.cc | 148 | ||||
-rw-r--r-- | fuzz/cr50_fuzz.owners | 5 | ||||
-rw-r--r-- | fuzz/cr50_fuzz.proto | 31 | ||||
-rw-r--r-- | fuzz/cr50_fuzz.tasklist | 9 | ||||
-rw-r--r-- | fuzz/mem_hash_tree.cc | 130 | ||||
-rw-r--r-- | fuzz/mem_hash_tree.h | 58 | ||||
-rw-r--r-- | fuzz/nvmem_tpm2_mock.c | 229 | ||||
-rw-r--r-- | fuzz/pinweaver/pinweaver.proto | 64 | ||||
-rw-r--r-- | fuzz/pinweaver_model.cc | 474 | ||||
-rw-r--r-- | fuzz/pinweaver_model.h | 123 | ||||
-rw-r--r-- | test/build.mk | 7 | ||||
-rw-r--r-- | test/nvmem.c | 1569 | ||||
-rw-r--r-- | test/nvmem.tasklist | 11 | ||||
-rw-r--r-- | test/nvmem_tpm2_mock.c | 377 |
15 files changed, 1 insertions, 3252 deletions
diff --git a/fuzz/build.mk b/fuzz/build.mk index f2f23c9cf8..e1ed79b77b 100644 --- a/fuzz/build.mk +++ b/fuzz/build.mk @@ -9,7 +9,7 @@ fuzz-test-list-host = # Fuzzers should only be built for architectures that support sanitizers. ifeq ($(ARCH),amd64) -fuzz-test-list-host += cr50_fuzz host_command_fuzz usb_pd_fuzz usb_tcpm_v2_fuzz +fuzz-test-list-host += host_command_fuzz usb_pd_fuzz usb_tcpm_v2_fuzz endif # For fuzzing targets libec.a is built from the ro objects and hides functions @@ -24,22 +24,6 @@ endif # 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 pinweaver_model.o mem_hash_tree.o nvmem_tpm2_mock.o host_command_fuzz-y = host_command_fuzz.o usb_pd_fuzz-y = usb_pd_fuzz.o usb_tcpm_v2_fuzz-y = usb_pd_fuzz.o usb_tcpm_v2_fuzz.o ../test/fake_battery.o - -CR50_PROTO_HEADERS := $(out)/gen/fuzz/cr50_fuzz.pb.h \ - $(out)/gen/fuzz/pinweaver/pinweaver.pb.h -$(out)/RW/fuzz/pinweaver_model.o: ${CR50_PROTO_HEADERS} -$(out)/RW/fuzz/cr50_fuzz.o: ${CR50_PROTO_HEADERS} -$(out)/RW/fuzz/cr50_fuzz.o: CPPFLAGS+=${LIBPROTOBUF_MUTATOR_CFLAGS} - -TPM2_LIB_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2 -$(out)/RW/fuzz/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_LIB_ROOT) - -$(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} diff --git a/fuzz/cr50_fuzz.cc b/fuzz/cr50_fuzz.cc deleted file mode 100644 index 186700f415..0000000000 --- a/fuzz/cr50_fuzz.cc +++ /dev/null @@ -1,148 +0,0 @@ -// 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 <cassert> -#include <cstdint> -#include <cstring> -#include <unordered_map> -#include <vector> - -#include <src/libfuzzer/libfuzzer_macro.h> -#include <src/mutator.h> - -#define HIDE_EC_STDLIB -#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" - -using protobuf_mutator::libfuzzer::LoadProtoInput; - -namespace { -constexpr size_t kBufferAlignment = alignof(pw_request_t) > - alignof(pw_response_t) - ? alignof(pw_request_t) - : alignof(pw_response_t); -} // namespace - -extern "C" uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {NVMEM_TPM_SIZE, - NVMEM_CR50_SIZE}; - -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" 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; -} - -extern "C" int DCRYPTO_ladder_is_enabled(void) { - return 1; -} - -extern "C" void nvmem_wipe_cache(void) { - // Nothing to do since there is no cache in this implementation. -} - -// 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(); - srand(0); -} - -// Used to verify the model hasn't become out of sync with the implementation. -// The usefulness of this fuzzer comes from its ability to reach all the code -// paths. -bool SelfTest() { - InitializeFuzzerRun(); - - PinweaverModel pinweaver_model; - alignas(kBufferAlignment) uint8_t buffer[PW_MAX_MESSAGE_SIZE] = {}; - fuzz::span<uint8_t> buffer_view(buffer, sizeof(buffer)); - fuzz::pinweaver::Request request; - - fuzz::pinweaver::ResetTree* reset_tree = request.mutable_reset_tree(); - reset_tree->set_height(2); - reset_tree->set_bits_per_level(2); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - fuzz::pinweaver::InsertLeaf* insert_leaf = request.mutable_insert_leaf(); - constexpr char delay_schedule[] = "\000\000\000\005\377\377\377\377"; - insert_leaf->mutable_delay_schedule()->assign( - delay_schedule, delay_schedule + sizeof(delay_schedule)); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - request.mutable_try_auth(); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - request.mutable_get_log(); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - request.mutable_log_replay(); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - request.mutable_reset_auth(); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - request.mutable_remove_leaf(); - assert(pinweaver_model.ApplyRequest(request, buffer_view) == EC_SUCCESS); - - return true; -} - -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) { - static bool initialized = SelfTest(); - assert(initialized); - - 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; -} diff --git a/fuzz/cr50_fuzz.owners b/fuzz/cr50_fuzz.owners deleted file mode 100644 index 80e5dc1dee..0000000000 --- a/fuzz/cr50_fuzz.owners +++ /dev/null @@ -1,5 +0,0 @@ -# Emails to CC on clusterfuzz bugs for this target: -allenwebb@google.com -mnissler@google.com -rspangler@chromium.org -vbendeb@chromium.org diff --git a/fuzz/cr50_fuzz.proto b/fuzz/cr50_fuzz.proto deleted file mode 100644 index 0291eacd88..0000000000 --- a/fuzz/cr50_fuzz.proto +++ /dev/null @@ -1,31 +0,0 @@ -// 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; -} diff --git a/fuzz/cr50_fuzz.tasklist b/fuzz/cr50_fuzz.tasklist deleted file mode 100644 index 24870f2abb..0000000000 --- a/fuzz/cr50_fuzz.tasklist +++ /dev/null @@ -1,9 +0,0 @@ -/* 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. - */ - -/** - * See CONFIG_TASK_LIST in config.h for details. - */ -#define CONFIG_TEST_TASK_LIST diff --git a/fuzz/mem_hash_tree.cc b/fuzz/mem_hash_tree.cc deleted file mode 100644 index 15c9de4142..0000000000 --- a/fuzz/mem_hash_tree.cc +++ /dev/null @@ -1,130 +0,0 @@ -// 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; - } -} diff --git a/fuzz/mem_hash_tree.h b/fuzz/mem_hash_tree.h deleted file mode 100644 index 34f4a40f9a..0000000000 --- a/fuzz/mem_hash_tree.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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> - -#define HIDE_EC_STDLIB -#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 diff --git a/fuzz/nvmem_tpm2_mock.c b/fuzz/nvmem_tpm2_mock.c deleted file mode 100644 index b864953b5d..0000000000 --- a/fuzz/nvmem_tpm2_mock.c +++ /dev/null @@ -1,229 +0,0 @@ -/* Copyright 2019 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. - */ -/* Stuff from tpm2 directory. */ -#define HIDE_EC_STDLIB -#define NV_C -#include "Global.h" -#undef NV_C -#include "NV_fp.h" -#include "tpm_generated.h" - -#include "nvmem.h" -#include "util.h" - -#define NVMEM_CR50_SIZE 272 - -#ifndef TEST_FUZZ -uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {MOCK_NV_MEMORY_SIZE, - NVMEM_CR50_SIZE}; -#endif - -uint32_t s_evictNvStart; -uint32_t s_evictNvEnd; - -/* Calculate size of TPM NVMEM. */ -#define MOCK_NV_MEMORY_SIZE \ - (NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag) - NVMEM_CR50_SIZE) - -/* - * Sizes of the reserved objects stored in the TPM NVMEM. Note that the second - * last object is in fact a variable size field starting with 4 bytes of size - * and then up to 512 bytes of actual index data. The array below assumes that - * the full 512 bytes of the index space are used. - */ -const uint16_t res_sizes[] = {4, 2, 2, 2, 66, 66, 66, 66, 66, 66, - 34, 34, 34, 66, 66, 66, 8, 4, 134, 28, - 3, 4, 4, 4, 4, 4, 2, 15, 2, 8, - 4, 4, 4, 96, 2844, 424, 516, 8}; - -static uint16_t res_addrs[ARRAY_SIZE(res_sizes)]; - -BOOL NvEarlyStageFindHandle(TPM_HANDLE handle) -{ - size_t i; - - res_addrs[0] = 0; - - for (i = 1; i < ARRAY_SIZE(res_addrs); i++) - res_addrs[i] = res_addrs[i - 1] + res_sizes[i - 1]; - - s_evictNvStart = res_addrs[i - 1] + res_sizes[i - 1]; - - s_evictNvEnd = MOCK_NV_MEMORY_SIZE; - return 0; -} - -void NvGetReserved(UINT32 index, NV_RESERVED_ITEM *ri) -{ - if (index < ARRAY_SIZE(res_sizes)) { - ri->size = res_sizes[index]; - ri->offset = res_addrs[index]; - } else { - ri->size = 0; - } -} - -UINT16 UINT16_Marshal(UINT16 *source, BYTE **buffer, INT32 *size) -{ - uint16_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe16(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 UINT32_Marshal(UINT32 *source, BYTE **buffer, INT32 *size) -{ - uint32_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe32(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 UINT64_Marshal(UINT64 *source, BYTE **buffer, INT32 *size) -{ - uint64_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe64(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 TPM2B_DIGEST_Marshal(TPM2B_DIGEST *source, BYTE **buffer, INT32 *size) -{ - UINT16 total_size; - INT32 i; - uint8_t *p; - - total_size = UINT16_Marshal(&source->t.size, buffer, size); - p = *buffer; - - for (i = 0; (i < source->t.size) && *size; ++i) { - *p++ = source->t.buffer[i]; - *size -= 1; - } - - total_size += i; - *buffer = p; - - return total_size; -} - -uint16_t TPM2B_AUTH_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Marshal(source, buffer, size); -} - -uint16_t TPM2B_NONCE_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Marshal(source, buffer, size); -} - -TPM_RC UINT16_Unmarshal(UINT16 *target, BYTE **buffer, INT32 *size) -{ - uint16_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be16toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC UINT32_Unmarshal(UINT32 *target, BYTE **buffer, INT32 *size) -{ - uint32_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be32toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC UINT64_Unmarshal(UINT64 *target, BYTE **buffer, INT32 *size) -{ - uint64_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be64toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC TPM2B_DIGEST_Unmarshal(TPM2B_DIGEST *target, BYTE **buffer, INT32 *size) -{ - TPM_RC result; - INT32 i; - uint8_t *p; - - result = UINT16_Unmarshal(&target->t.size, buffer, size); - - if (result != TPM_RC_SUCCESS) - return result; - - if (target->t.size == 0) - return TPM_RC_SUCCESS; - - if ((target->t.size > sizeof(TPMU_HA)) || (target->t.size > *size)) - return TPM_RC_SIZE; - - p = *buffer; - for (i = 0; i < target->t.size; ++i) - target->t.buffer[i] = *p++; - - *buffer = p; - *size -= i; - - return TPM_RC_SUCCESS; -} - -TPM_RC TPM2B_AUTH_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Unmarshal(target, buffer, size); -} - -TPM_RC TPM2B_NONCE_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Unmarshal(target, buffer, size); -} diff --git a/fuzz/pinweaver/pinweaver.proto b/fuzz/pinweaver/pinweaver.proto deleted file mode 100644 index 40e74f71de..0000000000 --- a/fuzz/pinweaver/pinweaver.proto +++ /dev/null @@ -1,64 +0,0 @@ -// 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; - } -} diff --git a/fuzz/pinweaver_model.cc b/fuzz/pinweaver_model.cc deleted file mode 100644 index 43618e1fa4..0000000000 --- a/fuzz/pinweaver_model.cc +++ /dev/null @@ -1,474 +0,0 @@ -// 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*/); -} diff --git a/fuzz/pinweaver_model.h b/fuzz/pinweaver_model.h deleted file mode 100644 index 84508786f3..0000000000 --- a/fuzz/pinweaver_model.h +++ /dev/null @@ -1,123 +0,0 @@ -// 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 diff --git a/test/build.mk b/test/build.mk index c7462da559..2b84ffe907 100644 --- a/test/build.mk +++ b/test/build.mk @@ -56,7 +56,6 @@ test-list-host += motion_lid test-list-host += motion_sense_fifo test-list-host += mutex test-list-host += newton_fit -test-list-host += nvmem test-list-host += pingpong test-list-host += pinweaver test-list-host += power_button @@ -140,7 +139,6 @@ motion_sense_fifo-y=motion_sense_fifo.o kasa-y=kasa.o mutex-y=mutex.o newton_fit-y=newton_fit.o -nvmem-y=nvmem.o nvmem_tpm2_mock.o pingpong-y=pingpong.o pinweaver-y=pinweaver.o power_button-y=power_button.o @@ -188,11 +186,6 @@ fp-y=fp.o x25519-y=x25519.o stillness_detector-y=stillness_detector.o -TPM2_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2 -$(out)/RO/common/new_nvmem.o: CFLAGS += -I$(TPM2_ROOT) -I chip/g -$(out)/RO/test/nvmem.o: CFLAGS += -I$(TPM2_ROOT) -$(out)/RO/test/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_ROOT) - host-is_enabled_error: TEST_SCRIPT=is_enabled_error.sh is_enabled_error-y=is_enabled_error.o.cmd diff --git a/test/nvmem.c b/test/nvmem.c deleted file mode 100644 index c07637e8de..0000000000 --- a/test/nvmem.c +++ /dev/null @@ -1,1569 +0,0 @@ -/* Copyright 2016 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. - * - * Test Cr-50 Non-Voltatile memory module - */ - -#include "nvmem_test.h" - -#include "common.h" -#include "console.h" -#include "crc.h" -#include "flash.h" -#include "flash_log.h" -#include "new_nvmem.h" -#include "nvmem.h" -#include "printf.h" -#include "shared_mem.h" -#include "task.h" -#include "test_util.h" -#include "timer.h" -#include "util.h" - -#define WRITE_SEGMENT_LEN 200 -#define WRITE_READ_SEGMENTS 4 - -enum test_failure_mode failure_mode; - -static const uint8_t legacy_nvmem_image[] = { -#include "legacy_nvmem_dump.h" -}; - -BUILD_ASSERT(sizeof(legacy_nvmem_image) == NVMEM_PARTITION_SIZE); - -static uint8_t write_buffer[NVMEM_PARTITION_SIZE]; -static int flash_write_fail; - -struct nvmem_test_result { - int var_count; - int reserved_obj_count; - int evictable_obj_count; - int deleted_obj_count; - int delimiter_count; - int unexpected_count; - size_t valid_data_size; - size_t erased_data_size; - size_t tuple_data_size; -}; - -static struct nvmem_test_result test_result; - -int app_cipher(const void *salt_p, void *out_p, const void *in_p, size_t size) -{ - - const uint8_t *in = in_p; - uint8_t *out = out_p; - const uint8_t *salt = salt_p; - size_t i; - - for (i = 0; i < size; i++) - out[i] = in[i] ^ salt[i % CIPHER_SALT_SIZE]; - - return 1; -} - -void app_compute_hash(uint8_t *p_buf, size_t num_bytes, - uint8_t *p_hash, size_t hash_bytes) -{ - uint32_t crc; - uint32_t *p_data; - int n; - size_t tail_size; - - crc32_init(); - /* Assuming here that buffer is 4 byte aligned. */ - p_data = (uint32_t *)p_buf; - for (n = 0; n < num_bytes / 4; n++) - crc32_hash32(*p_data++); - - tail_size = num_bytes % 4; - if (tail_size) { - uint32_t tail; - - tail = 0; - memcpy(&tail, p_data, tail_size); - crc32_hash32(tail); - } - - /* - * Crc32 of 0xffffffff is 0xffffffff. Let's spike the results to avoid - * this unfortunate Crc32 property. - */ - crc = crc32_result() ^ 0x55555555; - - for (n = 0; n < hash_bytes; n += sizeof(crc)) { - size_t copy_bytes = MIN(sizeof(crc), hash_bytes - n); - - memcpy(p_hash + n, &crc, copy_bytes); - } -} - -int crypto_enabled(void) -{ - return 1; -} - -/* Used to allow/prevent Flash erase/write operations */ -int flash_pre_op(void) -{ - return flash_write_fail ? EC_ERROR_UNKNOWN : EC_SUCCESS; -} - -static void dump_nvmem_state(const char *title, - const struct nvmem_test_result *tr) -{ - ccprintf("\n%s:\n", title); - ccprintf("var_count: %d\n", tr->var_count); - ccprintf("reserved_obj_count: %d\n", tr->reserved_obj_count); - ccprintf("evictable_obj_count: %d\n", tr->evictable_obj_count); - ccprintf("deleted_obj_count: %d\n", tr->deleted_obj_count); - ccprintf("deimiter_count: %d\n", tr->delimiter_count); - ccprintf("unexpected_count: %d\n", tr->unexpected_count); - ccprintf("valid_data_size: %zd\n", tr->valid_data_size); - ccprintf("tuple_data_size: %zd\n", tr->tuple_data_size); - ccprintf("erased_data_size: %zd\n\n", tr->erased_data_size); -} - -static void wipe_out_nvmem_cache(void) -{ - memset(nvmem_cache_base(NVMEM_TPM), 0, nvmem_user_sizes[NVMEM_TPM]); -} - -static int prepare_nvmem_contents(void) -{ - struct nvmem_tag *tag; - - memcpy(write_buffer, legacy_nvmem_image, sizeof(write_buffer)); - tag = (struct nvmem_tag *)write_buffer; - - app_compute_hash(tag->padding, NVMEM_PARTITION_SIZE - NVMEM_SHA_SIZE, - tag->sha, sizeof(tag->sha)); - app_cipher(tag->sha, tag + 1, tag + 1, - NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag)); - - return flash_physical_write(CONFIG_FLASH_NVMEM_BASE_A - - CONFIG_PROGRAM_MEMORY_BASE, - sizeof(write_buffer), write_buffer); -} - -static int iterate_over_flash(void) -{ - enum ec_error_list rv; - struct nn_container *ch; - struct access_tracker at = {}; - uint8_t buf[CONFIG_FLASH_BANK_SIZE]; - - memset(&test_result, 0, sizeof(test_result)); - ch = (struct nn_container *)buf; - - while ((rv = get_next_object(&at, ch, 1)) == EC_SUCCESS) - switch (ch->container_type) { - case NN_OBJ_OLD_COPY: - if (ch->container_type_copy == NN_OBJ_TRANSACTION_DEL) { - test_result.delimiter_count++; - } else { - test_result.deleted_obj_count++; - test_result.erased_data_size += ch->size; - } - break; - - case NN_OBJ_TUPLE: - test_result.var_count++; - test_result.valid_data_size += ch->size; - test_result.tuple_data_size += ch->size - - sizeof(struct tuple); - break; - - case NN_OBJ_TPM_RESERVED: - test_result.reserved_obj_count++; - test_result.valid_data_size += ch->size; - break; - - case NN_OBJ_TPM_EVICTABLE: - test_result.evictable_obj_count++; - test_result.valid_data_size += ch->size; - break; - - case NN_OBJ_TRANSACTION_DEL: - test_result.delimiter_count++; - break; - default: - test_result.unexpected_count++; - break; - } - - if (rv != EC_ERROR_MEMORY_ALLOCATION) { - ccprintf("\n%s:%d - unexpected return value %d\n", __func__, - __LINE__, rv); - return rv; - } - - /* Verify that there is a delimiter at the top of the flash. */ - if (at.mt.data_offset > sizeof(*at.mt.ph)) { - if ((at.mt.ph == at.dt.ph) && - (((at.mt.data_offset - sizeof(struct nn_container))) == - at.dt.data_offset)) { - return EC_SUCCESS; - } - } else { - if ((at.dt.ph == list_element_to_ph(at.list_index)) && - (at.dt.data_offset == - (CONFIG_FLASH_BANK_SIZE - sizeof(struct nn_container)))) { - ccprintf("%s:%d edge delimiter case OK\n", __func__, - __LINE__); - return EC_SUCCESS; - } - } - ccprintf("%s:%d bad delimiter location: ph %pP, " - "dt.ph %pP, offset %d, delim offset %d\n", - __func__, __LINE__, at.mt.ph, at.dt.ph, at.mt.data_offset, - at.dt.data_offset); - - return EC_ERROR_INVAL; -} - -static void *page_to_flash_addr(int page_num) -{ - uint32_t base_offset = CONFIG_FLASH_NEW_NVMEM_BASE_A; - - if (page_num > NEW_NVMEM_TOTAL_PAGES) - return NULL; - - if (page_num >= (NEW_NVMEM_TOTAL_PAGES / 2)) { - page_num -= (NEW_NVMEM_TOTAL_PAGES / 2); - base_offset = CONFIG_FLASH_NEW_NVMEM_BASE_B; - } - - return (void *)((uintptr_t)base_offset + - page_num * CONFIG_FLASH_BANK_SIZE); -} - -static int post_init_from_scratch(uint8_t flash_value) -{ - int i; - void *flash_p; - - memset(write_buffer, flash_value, sizeof(write_buffer)); - - /* Overwrite nvmem flash space with junk value. */ - flash_physical_write( - CONFIG_FLASH_NEW_NVMEM_BASE_A - CONFIG_PROGRAM_MEMORY_BASE, - NEW_FLASH_HALF_NVMEM_SIZE, (const char *)write_buffer); - flash_physical_write( - CONFIG_FLASH_NEW_NVMEM_BASE_B - CONFIG_PROGRAM_MEMORY_BASE, - NEW_FLASH_HALF_NVMEM_SIZE, (const char *)write_buffer); - - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(test_result.var_count == 0); - TEST_ASSERT(test_result.reserved_obj_count == 38); - TEST_ASSERT(test_result.evictable_obj_count == 0); - TEST_ASSERT(test_result.deleted_obj_count == 0); - TEST_ASSERT(test_result.unexpected_count == 0); - TEST_ASSERT(test_result.valid_data_size == 1088); - TEST_ASSERT(total_var_space == 0); - - for (i = 0; i < (NEW_NVMEM_TOTAL_PAGES - 1); i++) { - flash_p = page_to_flash_addr(i); - - TEST_ASSERT(!!flash_p); - TEST_ASSERT(is_uninitialized(flash_p, CONFIG_FLASH_BANK_SIZE)); - } - - flash_p = page_to_flash_addr(i); - TEST_ASSERT(!is_uninitialized(flash_p, CONFIG_FLASH_BANK_SIZE)); - - return EC_SUCCESS; -} - -/* - * The purpose of this test is to check NvMem initialization when NvMem is - * completely erased (i.e. following SpiFlash write of program). In this case, - * nvmem_init() is expected to create initial flash storage containing - * reserved objects only. - */ -static int test_fully_erased_nvmem(void) -{ - - return post_init_from_scratch(0xff); -} - -/* - * The purpose of this test is to check nvmem_init() in the case when no valid - * pages exist but flash space is garbled as opposed to be fully erased. In - * this case, the initialization is expected to create one new valid page and - * erase the rest of the pages. - */ -static int test_corrupt_nvmem(void) -{ - return post_init_from_scratch(0x55); -} - -static int prepare_new_flash(void) -{ - TEST_ASSERT(test_fully_erased_nvmem() == EC_SUCCESS); - - /* Now copy sensible information into the nvmem cache. */ - memcpy(nvmem_cache_base(NVMEM_TPM), - legacy_nvmem_image + sizeof(struct nvmem_tag), - nvmem_user_sizes[NVMEM_TPM]); - - dump_nvmem_state("after first save", &test_result); - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - - TEST_ASSERT(test_result.deleted_obj_count == 24); - TEST_ASSERT(test_result.var_count == 0); - TEST_ASSERT(test_result.reserved_obj_count == 40); - TEST_ASSERT(test_result.evictable_obj_count == 9); - TEST_ASSERT(test_result.unexpected_count == 0); - TEST_ASSERT(test_result.valid_data_size == 5128); - TEST_ASSERT(test_result.erased_data_size == 698); - - return EC_SUCCESS; -} - -static int test_nvmem_save(void) -{ - const char *key = "var1"; - const char *value = "value of var 1"; - size_t total_var_size; - struct nvmem_test_result old_result; - - TEST_ASSERT(prepare_new_flash() == EC_SUCCESS); - - /* - * Verify that saving without changing the cache does not affect flash - * contents. - */ - old_result = test_result; - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - - /* - * Save of unmodified cache does not modify the flash contents and - * does not set the delimiter. - */ - - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result))); - - wipe_out_nvmem_cache(); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result))); - - /* - * Total size test variable storage takes in flash (container header - * size not included). - */ - total_var_size = strlen(key) + strlen(value) + sizeof(struct tuple); - - /* Verify that we can add a variable to nvmem. */ - TEST_ASSERT(setvar(key, strlen(key), value, strlen(value)) == - EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - - /* Remove changes caused by the new var addition. */ - test_result.delimiter_count -= 1; - test_result.valid_data_size -= total_var_size; - test_result.tuple_data_size -= total_var_size - - sizeof(struct tuple) * test_result.var_count; - test_result.var_count -= 1; - - TEST_ASSERT(memcmp(&test_result, &old_result, sizeof(test_result)) == - 0); - - /* Verify that we can delete a variable from nvmem. */ - TEST_ASSERT(setvar(key, strlen(key), NULL, 0) == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - test_result.deleted_obj_count -= 1; - test_result.erased_data_size -= total_var_size; - test_result.delimiter_count -= 1; - TEST_ASSERT(memcmp(&test_result, &old_result, sizeof(test_result)) == - 0); - - return EC_SUCCESS; -} - -static size_t get_free_nvmem_room(void) -{ - size_t free_room; - size_t free_pages; - /* Compaction kicks in when 3 pages or less are left. */ - const size_t max_pages = NEW_NVMEM_TOTAL_PAGES - 3; - - ccprintf("list index %d, data offset 0x%x\n", master_at.list_index, - master_at.mt.data_offset); - - if (master_at.list_index >= max_pages) - return 0; - - free_pages = max_pages - master_at.list_index; - free_room = (free_pages - 1) * (CONFIG_FLASH_BANK_SIZE - - sizeof(struct nn_page_header)) + - CONFIG_FLASH_BANK_SIZE - master_at.mt.data_offset; - ccprintf("free pages %zd, data offset 0x%x\n", free_pages, - master_at.mt.data_offset); - return free_room; -} - -static int test_nvmem_compaction(void) -{ - char value[100]; /* Definitely more than enough. */ - const char *key = "var 1"; - int i; - size_t key_len; - size_t val_len; - size_t free_room; - size_t real_var_size; - size_t var_space; - int max_vars; - int erased_data_size; - const size_t alignment_mask = CONFIG_FLASH_WRITE_SIZE - 1; - - key_len = strlen(key); - val_len = snprintf(value, sizeof(value), "variable value is %04d", 0); - - TEST_ASSERT(prepare_new_flash() == EC_SUCCESS); - - /* - * Remember how much room was erased before flooding nvmem with erased - * values. - */ - erased_data_size = test_result.erased_data_size; - - /* Let's see how much free room there is. */ - free_room = get_free_nvmem_room(); - TEST_ASSERT(free_room); - - /* How much room (key, value) pair takes in a container. */ - real_var_size = val_len + key_len + sizeof(struct tuple); - /* - * See how many vars including containers should be able to fit there. - * - * First calculate rounded up space a var will take. Apart from the - * var itself there will be a container header and a delimiter. - */ - var_space = (real_var_size + 2 * sizeof(struct nn_container) + - alignment_mask) & ~alignment_mask; - - max_vars = free_room / var_space; - - /* - * And now flood the NVMEM with erased values (each new setvar() - * invocation erases the previous instance. - */ - for (i = 0; i <= max_vars; i++) { - snprintf(value, sizeof(value), "variable value is %04d", i); - TEST_ASSERT(setvar(key, key_len, value, val_len) == EC_SUCCESS); - } - - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - /* Make sure there was no compaction yet. */ - TEST_ASSERT(test_result.erased_data_size > erased_data_size); - - /* This is how much the erased space grew as a result of flooding. */ - erased_data_size = test_result.erased_data_size - erased_data_size; - TEST_ASSERT(erased_data_size == max_vars * real_var_size); - - /* This will take it over the compaction limit. */ - val_len = snprintf(value, sizeof(value), "variable value is %03d", i); - TEST_ASSERT(setvar(key, key_len, value, val_len) == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(test_result.erased_data_size < var_space); - - return EC_SUCCESS; -} - -static int test_configured_nvmem(void) -{ - /* - * The purpose of this test is to check how nvmem_init() initializes - * from previously saved flash contents. - */ - TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS); - - /* - * This is initialization from legacy flash contents which replaces - * legacy flash image with the new format flash image - */ - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* And this is initialization from the new flash layout. */ - return nvmem_init(); -} - -static uint8_t find_lb(const void *data) -{ - return (const uint8_t *)memchr(data, '#', 256) - (const uint8_t *)data; -} - -/* - * Helper function, depending on the argument value either writes variables - * into nvmem and verifies their presence, or deletes them and verifies that - * they indeed disappear. - */ -static int var_read_write_delete_helper(int do_write) -{ - size_t i; - uint16_t saved_total_var_space; - uint32_t coverage_map; - - const struct { - uint8_t *key; - uint8_t *value; - } kv_pairs[] = { - /* Use # as the delimiter to allow \0 in keys/values. */ - {"\0key\00#", "value of key2#"}, {"key1#", "value of key1#"}, - {"key2#", "value of key2#"}, {"key3#", "value of\0 key3#"}, - {"ke\04#", "value\0 of\0 key4#"}, - }; - - coverage_map = 0; - saved_total_var_space = total_var_space; - - /* - * Read all vars, one at a time, verifying that they shows up in - * getvar results when appropriate but not before. - */ - for (i = 0; i <= ARRAY_SIZE(kv_pairs); i++) { - size_t j; - uint8_t key_len; - uint8_t val_len; - const void *value; - - for (j = 0; j < ARRAY_SIZE(kv_pairs); j++) { - const struct tuple *t; - - coverage_map |= 1; - - key_len = find_lb(kv_pairs[j].key); - t = getvar(kv_pairs[j].key, key_len); - - if ((j >= i) ^ !do_write) { - TEST_ASSERT(t == NULL); - continue; - } - - coverage_map |= 2; - - TEST_ASSERT(saved_total_var_space == total_var_space); - - /* Confirm that what we found is the right variable. */ - val_len = find_lb(kv_pairs[j].value); - - TEST_ASSERT(t->key_len == key_len); - TEST_ASSERT(t->val_len == val_len); - TEST_ASSERT( - !memcmp(kv_pairs[j].key, t->data_, key_len)); - TEST_ASSERT(!memcmp(kv_pairs[j].value, - t->data_ + key_len, val_len)); - freevar(t); - } - - if (i == ARRAY_SIZE(kv_pairs)) { - coverage_map |= 4; - /* All four variables have been processed. */ - break; - } - - val_len = find_lb(kv_pairs[i].value); - key_len = find_lb(kv_pairs[i].key); - value = kv_pairs[i].value; - if (!do_write) { - - coverage_map |= 8; - - saved_total_var_space -= val_len + key_len; - /* - * Make sure all val_len == 0 and val == NULL - * combinations are exercised. - */ - switch (i) { - case 0: - val_len = 0; - coverage_map |= 0x10; - break; - - case 1: - coverage_map |= 0x20; - value = NULL; - break; - default: - coverage_map |= 0x40; - val_len = 0; - value = NULL; - break; - } - } else { - coverage_map |= 0x80; - saved_total_var_space += val_len + key_len; - } - key_len = find_lb(kv_pairs[i].key); - TEST_ASSERT(setvar(kv_pairs[i].key, key_len, value, val_len) == - EC_SUCCESS); - - TEST_ASSERT(saved_total_var_space == total_var_space); - } - - if (do_write) - TEST_ASSERT(coverage_map == 0x87); - else - TEST_ASSERT(coverage_map == 0x7f); - - return EC_SUCCESS; -} - -static int test_var_read_write_delete(void) -{ - TEST_ASSERT(post_init_from_scratch(0xff) == EC_SUCCESS); - - ccprintf("\n%s: starting write cycle\n", __func__); - TEST_ASSERT(var_read_write_delete_helper(1) == EC_SUCCESS); - - ccprintf("%s: starting delete cycle\n", __func__); - TEST_ASSERT(var_read_write_delete_helper(0) == EC_SUCCESS); - - return EC_SUCCESS; -} - -static int test_nvmem_tuple_capacity(void) -{ - char key[5]; - char value[18]; - int rv; - - /* Does not matter, but for consistency let's init key and value. */ - memset(key, 0, sizeof(key)); - memset(value, 0, sizeof(value)); - - TEST_ASSERT(post_init_from_scratch(0xff) == EC_SUCCESS); - - /* Fill up var space until it is full. */ - while (1) { - rv = setvar(key, sizeof(key), value, sizeof(value) - 1); - if (rv != EC_SUCCESS) - break; - key[0]++; - } - TEST_ASSERT(rv == EC_ERROR_OVERFLOW); - iterate_over_flash(); - - /* - * Verify that total variable size is as expected. We know that the - * allotted space will not exactly fit a number of tuples, so the - * check is that the total tuple data size is smaller than the space. - * - * If some parameters change in the future such that this assumption - * becomes wrong, the test in the next line would fail. - */ - TEST_ASSERT(test_result.tuple_data_size < MAX_VAR_TOTAL_SPACE); - TEST_ASSERT((MAX_VAR_TOTAL_SPACE - test_result.tuple_data_size) < - (sizeof(key) + sizeof(value) - 1)); - - /* - * Verify that it is still possible to modify a variable when storage - * is almost full and the new value is larger than the old value. - */ - key[0]--; - value[0]++; - TEST_ASSERT(setvar(key, sizeof(key), - value, sizeof(value)) == EC_SUCCESS); - - return EC_SUCCESS; -} - -/* Verify that nvmem_erase_user_data only erases the given user's data. */ -static int test_nvmem_erase_tpm_data(void) -{ - TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - browse_flash_contents(1); - TEST_ASSERT(nvmem_erase_tpm_data() == EC_SUCCESS); - browse_flash_contents(1); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(test_result.deleted_obj_count == 0); - TEST_ASSERT(test_result.var_count == 3); - TEST_ASSERT(test_result.reserved_obj_count == 38); - TEST_ASSERT(test_result.evictable_obj_count == 0); - TEST_ASSERT(test_result.unexpected_count == 0); - TEST_ASSERT(test_result.valid_data_size == 1174); - TEST_ASSERT(test_result.erased_data_size == 0); - - return EC_SUCCESS; -} - -static size_t fill_obj_offsets(uint16_t *offsets, size_t max_objects) -{ - size_t i; - size_t obj_count; - - obj_count = init_object_offsets(offsets, max_objects); - - ccprintf("%zd objects\n", obj_count); - for (i = 0; i < obj_count; i++) { - uint32_t *op; - - op = evictable_offs_to_addr(offsets[i]); - ccprintf("offs %04x:%08x:%08x:%08x addr %pP size %zd\n", - offsets[i], op[-1], op[0], op[1], op, - (uintptr_t)nvmem_cache_base(NVMEM_TPM) + op[-1] - - (uintptr_t)op); - } - - return obj_count; -} - -static size_t fill_cache_offsets(const void *cache, uint16_t *offsets, - size_t max_objects) -{ - uint8_t buf[nvmem_user_sizes[NVMEM_TPM]]; - void *real_cache; - size_t num_offsets; - - real_cache = nvmem_cache_base(NVMEM_TPM); - memcpy(buf, real_cache, sizeof(buf)); - - memcpy(real_cache, cache, sizeof(buf)); - memset(offsets, 0, sizeof(*offsets) * max_objects); - num_offsets = fill_obj_offsets(offsets, max_objects); - - /* Restore the real cache. */ - memcpy(real_cache, buf, sizeof(buf)); - - return num_offsets; -} -#define MAX_OFFSETS 20 - -static uint32_t get_evict_size(const uint8_t *cache, uint16_t offset) -{ - uint32_t next_addr; - uint32_t cache_offset; - - cache_offset = s_evictNvStart + offset; - memcpy(&next_addr, cache + cache_offset - sizeof(next_addr), - sizeof(next_addr)); - - return next_addr - cache_offset; -} - -/* Returns zero if the two objects are identical. */ -static int compare_objects(const uint8_t *cache1, uint16_t offset1, - const uint8_t *cache2, uint16_t offset2) -{ - uint32_t size1; - uint32_t size2; - - size1 = get_evict_size(cache1, offset1); - size2 = get_evict_size(cache2, offset2); - - if (size1 == size2) - return memcmp(cache1 + s_evictNvStart + offset1, - cache2 + s_evictNvStart + offset2, size1); - - return 1; -} -/* - * Compare two instances of NVMEM caches. Reserved spaces should be exactly - * the same for the match, but evictable objects could be rearranged due to - * compaction, updating, etc. - * - * For the two cache instances to be considered the same the sets and contents - * of the evictable object spaces must also match object to object. - */ -static int caches_match(const uint8_t *cache1, const uint8_t *cache2) -{ - int failed_count; - size_t cache1_offs_count; - size_t cache2_offs_count; - size_t i; - uint16_t cache1_offsets[MAX_OFFSETS]; - uint16_t cache2_offsets[MAX_OFFSETS]; - - for (failed_count = i = 0; i < NV_PSEUDO_RESERVE_LAST; i++) { - NV_RESERVED_ITEM ri; - struct { - uint32_t offset; - uint32_t size; - } ranges[3]; - size_t j; - - NvGetReserved(i, &ri); - - ranges[0].offset = ri.offset; - - if (i != NV_STATE_CLEAR) { - ranges[0].size = ri.size; - ranges[1].size = 0; - } else { - ranges[0].size = offsetof(STATE_CLEAR_DATA, pcrSave); - ranges[1].offset = ranges[0].offset + ranges[0].size; - ranges[1].size = sizeof(PCR_SAVE); - ranges[2].offset = ranges[1].offset + ranges[1].size; - ranges[2].size = sizeof(PCR_AUTHVALUE); - } - - for (j = 0; j < ARRAY_SIZE(ranges); j++) { - - uint32_t offset; - uint32_t size; - uint32_t k; - - size = ranges[j].size; - if (!size) - break; - - offset = ranges[j].offset; - - if (!memcmp(cache1 + offset, cache2 + offset, size)) - continue; - - ccprintf("%s:%d failed comparing %zd:%zd:\n", __func__, - __LINE__, i, j); - for (k = offset; k < (offset + size); k++) - if (cache1[k] != cache2[k]) - ccprintf(" %3d:%02x", k - offset, - cache1[k]); - ccprintf("\n"); - for (k = offset; k < (offset + size); k++) - if (cache1[k] != cache2[k]) - ccprintf(" %3d:%02x", k - offset, - cache2[k]); - ccprintf("\n"); - - failed_count++; - } - } - - TEST_ASSERT(!failed_count); - - cache1_offs_count = fill_cache_offsets(cache1, cache1_offsets, - ARRAY_SIZE(cache1_offsets)); - cache2_offs_count = fill_cache_offsets(cache2, cache2_offsets, - ARRAY_SIZE(cache2_offsets)); - - TEST_ASSERT(cache1_offs_count == cache2_offs_count); - - for (i = 0; (i < ARRAY_SIZE(cache1_offsets)) && cache2_offs_count; - i++) { - size_t j; - - for (j = 0; j < cache2_offs_count; j++) { - if (compare_objects(cache1, cache1_offsets[i], cache2, - cache2_offsets[j])) - continue; - /* Remove object from the cache2 offsets. */ - cache2_offsets[j] = cache2_offsets[--cache2_offs_count]; - break; - } - } - - TEST_ASSERT(cache2_offs_count == 0); - return EC_SUCCESS; -} - -static int prepare_post_migration_nvmem(void) -{ - TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - return EC_SUCCESS; -} -/* - * This test creates various failure conditions related to interrupted nvmem - * save operations and verifies that transaction integrity is maintained - - * i.e. either all variables get updated, - */ -static int test_nvmem_incomplete_transaction(void) -{ - /* - * Will be more than enough, we can't store more than 15 objects or so - * anyways. - */ - uint16_t offsets[MAX_OFFSETS]; - size_t num_objects; - uint8_t buf[nvmem_user_sizes[NVMEM_TPM]]; - uint8_t *p; - size_t object_size; - union entry_u e; - - TEST_ASSERT(prepare_post_migration_nvmem() == EC_SUCCESS); - num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets)); - TEST_ASSERT(num_objects == 9); - - /* Save cache state before deleting objects. */ - memcpy(buf, nvmem_cache_base(NVMEM_TPM), sizeof(buf)); - - drop_evictable_obj(evictable_offs_to_addr(offsets[4])); - drop_evictable_obj(evictable_offs_to_addr(offsets[3])); - - failure_mode = TEST_FAIL_WHEN_SAVING; - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - wipe_out_nvmem_cache(); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - TEST_ASSERT(caches_match(buf, nvmem_cache_base(NVMEM_TPM)) == - EC_SUCCESS); - drop_evictable_obj(evictable_offs_to_addr(offsets[4])); - drop_evictable_obj(evictable_offs_to_addr(offsets[3])); - - /* Check if failure when invalidating is recovered after restart. */ - failure_mode = TEST_FAIL_WHEN_INVALIDATING; - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - ccprintf("%s:%d\n", __func__, __LINE__); - wipe_out_nvmem_cache(); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - ccprintf("%s:%d\n", __func__, __LINE__); - num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets)); - TEST_ASSERT(num_objects == 7); - - /* - * Now, let's modify an object and introduce corruption when saving - * it. - */ - p = evictable_offs_to_addr(offsets[4]); - p[10] ^= 0x55; - failure_mode = TEST_FAILED_HASH; - new_nvmem_save(); - failure_mode = TEST_NO_FAILURE; - - /* And verify that nvmem can still successfully initialize. */ - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* - * Now let's interrupt saving an object spanning two pages. - * - * First, fill up the current page to get close to the limit such that - * the next save will have to span two flash pages. - */ - object_size = offsets[4] - offsets[3]; - p = (uint8_t *)evictable_offs_to_addr(offsets[3]) + object_size - 10; - while ((master_at.mt.data_offset + object_size + - sizeof(struct nn_container)) <= CONFIG_FLASH_BANK_SIZE) { - (*p)++; - new_nvmem_save(); - } - - /* This will trigger spilling over the page boundary. */ - (*p)++; - failure_mode = TEST_SPANNING_PAGES; - new_nvmem_save(); - failure_mode = TEST_NO_FAILURE; - - /* Drain the event log. */ - e.r.timestamp = 0; - while (flash_log_dequeue_event(e.r.timestamp, e.entry, sizeof(e)) > 0) - ; - - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* Let's verify that a container mismatch event has been added. */ - TEST_ASSERT(flash_log_dequeue_event(e.r.timestamp, e.entry, sizeof(e)) - > 0); - TEST_ASSERT(e.r.type == FE_LOG_NVMEM); - TEST_ASSERT(e.r.payload[0] == NVMEMF_CONTAINER_HASH_MISMATCH); - return EC_SUCCESS; -} - -/* - * Verify that interrupted compaction results in a consistent state of the - * NVMEM cache. - */ -static int test_nvmem_interrupted_compaction(void) -{ - uint8_t buf[nvmem_user_sizes[NVMEM_TPM]]; - uint8_t target_list_index; - uint8_t filler = 1; - - TEST_ASSERT(prepare_post_migration_nvmem() == EC_SUCCESS); - - /* Let's fill up a couple of pages with erased objects. */ - target_list_index = master_at.list_index + 2; - - do { - /* - * A few randomly picked reserved objects to modify to create - * need for compaction. - */ - const uint8_t objs_to_modify[] = {1, 3, 19, 42}; - size_t i; - - for (i = 0; i < ARRAY_SIZE(objs_to_modify); i++) { - NV_RESERVED_ITEM ri; - - NvGetReserved(i, &ri); - - /* Direct access to the object. */ - memset((uint8_t *)nvmem_cache_base(NVMEM_TPM) + - ri.offset, - filler++, ri.size); - } - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - } while (master_at.list_index != target_list_index); - - /* Save the state of NVMEM cache. */ - memcpy(buf, nvmem_cache_base(NVMEM_TPM), sizeof(buf)); - failure_mode = TEST_FAIL_WHEN_COMPACTING; - compact_nvmem(); - wipe_out_nvmem_cache(); - ccprintf("%s:%d\n", __func__, __LINE__); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(caches_match(buf, nvmem_cache_base(NVMEM_TPM)) == - EC_SUCCESS); - return EC_SUCCESS; -} - -int nvmem_first_task(void *unused) -{ - return EC_SUCCESS; -} - -int nvmem_second_task(void *unused) -{ - return EC_SUCCESS; -} - -static void run_test_setup(void) -{ - /* Allow Flash erase/writes */ - flash_write_fail = 0; - test_reset(); -} - -void nvmem_wipe_cache(void) -{ -} - -int DCRYPTO_ladder_is_enabled(void) -{ - return 1; -} - -static int test_migration(void) -{ - /* - * This purpose of this test is to verify migration of the 'legacy' - * TPM NVMEM format to the new scheme where each element is stored in - * flash in its own container. - */ - TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(iterate_over_flash() == EC_SUCCESS); - TEST_ASSERT(test_result.var_count == 3); - TEST_ASSERT(test_result.reserved_obj_count == 40); - TEST_ASSERT(test_result.evictable_obj_count == 9); - TEST_ASSERT(test_result.delimiter_count == 1); - TEST_ASSERT(test_result.deleted_obj_count == 0); - TEST_ASSERT(test_result.unexpected_count == 0); - TEST_ASSERT(test_result.valid_data_size == 5214); - TEST_ASSERT(total_var_space == 77); - /* Container pointer not yet set. */ - TEST_ASSERT(!master_at.ct.data_offset && !master_at.ct.ph); - return EC_SUCCESS; -} - -/* - * The purpose of this test is to verify variable storage limits, both per - * object and total. - */ -static int test_var_boundaries(void) -{ - const size_t max_size = 255; /* Key and value must fit in a byte. */ - const uint8_t *key; - const uint8_t *val; - size_t key_len; - size_t val_len; - uint16_t saved_total_var_space; - uint32_t coverage_map; - uint8_t var_key[10]; - - TEST_ASSERT(prepare_new_flash() == EC_SUCCESS); - saved_total_var_space = total_var_space; - coverage_map = 0; - - /* - * Let's use the legacy NVMEM image as a source of fairly random but - * reproducible data. - */ - key = legacy_nvmem_image; - val = legacy_nvmem_image; - - /* - * Test limit of max variable body space, use keys and values of - * different sizes, below and above the limit. - */ - for (key_len = 1; key_len < max_size; key_len += 20) { - - coverage_map |= 1; - - val_len = MIN(max_size, MAX_VAR_BODY_SPACE - key_len); - TEST_ASSERT(setvar(key, key_len, val, val_len) == EC_SUCCESS); - TEST_ASSERT(total_var_space == - saved_total_var_space + key_len + val_len); - - /* Now drop the variable from the storage. */ - TEST_ASSERT(setvar(key, key_len, NULL, 0) == EC_SUCCESS); - TEST_ASSERT(total_var_space == saved_total_var_space); - - /* And if key length allows it, try to write too much. */ - if (val_len == max_size) - continue; - - coverage_map |= 2; - /* - * Yes, let's try writing one byte too many and see that the - * attempt is rejected. - */ - val_len++; - TEST_ASSERT(setvar(key, key_len, val, val_len) == - EC_ERROR_INVAL); - TEST_ASSERT(total_var_space == saved_total_var_space); - } - - /* - * Test limit of max total variable space, use keys and values of - * different sizes, below and above the limit. - */ - key_len = sizeof(var_key); - val_len = 20; /* Anything below 256 would work. */ - memset(var_key, 'x', key_len); - - while (1) { - int rv; - - /* - * Change the key so that a new variable is added to the - * storage. - */ - rv = setvar(var_key, key_len, val, val_len); - - if (rv == EC_ERROR_OVERFLOW) - break; - - coverage_map |= 4; - TEST_ASSERT(rv == EC_SUCCESS); - var_key[0]++; - saved_total_var_space += key_len + val_len; - } - - TEST_ASSERT(saved_total_var_space == total_var_space); - TEST_ASSERT(saved_total_var_space <= MAX_VAR_TOTAL_SPACE); - TEST_ASSERT((saved_total_var_space + key_len + val_len) > - MAX_VAR_TOTAL_SPACE); - - TEST_ASSERT(coverage_map == 7); - return EC_SUCCESS; -} - -static int verify_ram_index_space(size_t verify_size) -{ - NV_RESERVED_ITEM ri; - size_t i; - uint32_t casted_size; - uint8_t byte; - uint8_t fill_byte = 0x55; - - if (verify_size > RAM_INDEX_SPACE) - return EC_ERROR_INVAL; - - NvGetReserved(NV_RAM_INDEX_SPACE, &ri); - - /* - * Save the size of the index space, needed on machines where size_t - * is a 64 bit value. - */ - casted_size = verify_size; - - /* - * Now write index space in the cache, we write the complete space, - * but on read back only verify_size bytes are expected to be set. - */ - nvmem_write(ri.offset, sizeof(casted_size), &casted_size, NVMEM_TPM); - - for (i = 0; i < RAM_INDEX_SPACE; i++) - nvmem_write(ri.offset + sizeof(casted_size) + i, - sizeof(fill_byte), &fill_byte, NVMEM_TPM); - - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - wipe_out_nvmem_cache(); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* Make sure read back size matches. */ - nvmem_read(ri.offset, sizeof(casted_size), &casted_size, NVMEM_TPM); - TEST_ASSERT(casted_size == verify_size); - - /* - * Now check spaces which were supposed to be written (up to - * verify_size) and left intact. - */ - for (i = 0; i < RAM_INDEX_SPACE; i++) { - nvmem_read(ri.offset + sizeof(casted_size) + i, sizeof(byte), - &byte, NVMEM_TPM); - if (i < verify_size) - TEST_ASSERT(byte == fill_byte); - else - TEST_ASSERT(byte == 0); - } - - return EC_SUCCESS; -} - -static int test_tpm_nvmem_modify_reserved_objects(void) -{ - NV_RESERVED_ITEM ri; - /* Some random reserved objects' indices. */ - const uint8_t res_obj_ids[] = {1, 4, 9, 20}; - size_t i; - static uint8_t cache_copy[12 * 1024]; - struct nvmem_test_result old_result; - uint64_t new_values[ARRAY_SIZE(res_obj_ids)]; - size_t erased_size; - - TEST_ASSERT(sizeof(cache_copy) >= nvmem_user_sizes[NVMEM_TPM]); - TEST_ASSERT(prepare_new_flash() == EC_SUCCESS); - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - iterate_over_flash(); - old_result = test_result; - - /* Preserve NVMEM cache for future comparison. */ - memcpy(cache_copy, nvmem_cache_base(NVMEM_TPM), - nvmem_user_sizes[NVMEM_TPM]); - - erased_size = 0; - /* Modify several reserved objects in the cache. */ - for (i = 0; i < ARRAY_SIZE(res_obj_ids); i++) { - size_t copy_size; - uint8_t *addr_in_cache; - size_t k; - - NvGetReserved(res_obj_ids[i], &ri); - copy_size = MIN(sizeof(new_values[0]), ri.size); - addr_in_cache = - (uint8_t *)nvmem_cache_base(NVMEM_TPM) + ri.offset; - - /* Prepare a new value for the variable. */ - memcpy(new_values + i, addr_in_cache, copy_size); - for (k = 0; k < copy_size; k++) - ((uint8_t *)(new_values + i))[k] ^= 0x55; - - /* Update value in the cache. */ - memcpy(addr_in_cache, new_values + i, copy_size); - - /* And in the cache copy. */ - memcpy(cache_copy + ri.offset, new_values + i, copy_size); - - /* - * This much will be added to the erased space, object size - * plus index size. - */ - erased_size += ri.size + 1; - } - - /* Save it into flash. */ - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - - /* Wipe out the cache to be sure. */ - wipe_out_nvmem_cache(); - - /* Read NVMEM contents from flash. */ - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* Verify that the cache matches expectations. */ - TEST_ASSERT(!memcmp(cache_copy, nvmem_cache_base(NVMEM_TPM), - nvmem_user_sizes[NVMEM_TPM])); - - iterate_over_flash(); - - /* Update previous results with our expectations. */ - old_result.deleted_obj_count += ARRAY_SIZE(res_obj_ids); - old_result.erased_data_size += erased_size; - old_result.delimiter_count++; - - TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result))); - - /* Verify several index space cases. */ - for (i = 0; i <= RAM_INDEX_SPACE; i += (RAM_INDEX_SPACE / 2)) - TEST_ASSERT(verify_ram_index_space(i) == EC_SUCCESS); - - return EC_SUCCESS; -} - -static int compare_object(uint16_t obj_offset, size_t obj_size, const void *obj) -{ - uint32_t next_addr; - - memcpy(&next_addr, - evictable_offs_to_addr(obj_offset - sizeof(next_addr)), - sizeof(next_addr)); - - ccprintf("next_addr %x, sum %zx size %zd\n", next_addr, - (s_evictNvStart + obj_offset + obj_size), obj_size); - TEST_ASSERT(next_addr == (s_evictNvStart + obj_offset + obj_size)); - - if (!memcmp(evictable_offs_to_addr(obj_offset), obj, obj_size)) - return EC_SUCCESS; - - return EC_ERROR_INVAL; -} - -static int test_tpm_nvmem_modify_evictable_objects(void) -{ - size_t num_objects; - uint16_t offsets[MAX_OFFSETS]; - uint32_t handles[ARRAY_SIZE(offsets)]; - uint32_t new_evictable_object[30]; - size_t i; - const uint32_t new_obj_handle = 0x100; - static uint8_t modified_obj[CONFIG_FLASH_BANK_SIZE]; - size_t modified_obj_size; - uint32_t modified_obj_handle; - uint32_t deleted_obj_handle; - uint8_t *obj_cache_addr; - size_t num_handles; - int new_obj_index; - int modified_obj_index; - - TEST_ASSERT(prepare_new_flash() == EC_SUCCESS); - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - iterate_over_flash(); - - /* Verify that all evictable objects are there. */ - num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets)); - TEST_ASSERT(num_objects == 9); - num_handles = num_objects; - - /* Save handles of all objects there are. */ - for (i = 0; i < num_objects; i++) { - memcpy(handles + i, evictable_offs_to_addr(offsets[i]), - sizeof(handles[i])); - ccprintf("obj %zd handle %08x\n", i, handles[i]); - } - /* - * Let's modify the object which currently is stored second in the - * stack. - */ - modified_obj_size = offsets[3] - offsets[2] - sizeof(uint32_t); - - /* Modify the object and copy modified value into local buffer. */ - obj_cache_addr = evictable_offs_to_addr(offsets[2]); - memcpy(&modified_obj_handle, obj_cache_addr, - sizeof(modified_obj_handle)); - - for (i = 0; i < modified_obj_size; i++) { - uint8_t c; - - c = obj_cache_addr[i]; - - if (i >= sizeof(uint32_t)) { /* Preserve the 4 byte handle. */ - c ^= 0x55; - obj_cache_addr[i] = c; - } - modified_obj[i] = c; - } - - /* Save its handle and then drop the object at offset 5. */ - memcpy(&deleted_obj_handle, evictable_offs_to_addr(offsets[5]), - sizeof(deleted_obj_handle)); - drop_evictable_obj(evictable_offs_to_addr(offsets[5])); - - /* Prepare the new evictable object, first four bytes are the handle. */ - for (i = 0; i < ARRAY_SIZE(new_evictable_object); i++) - new_evictable_object[i] = new_obj_handle + i; - - /* Add it to the cache. */ - add_evictable_obj(new_evictable_object, sizeof(new_evictable_object)); - - /* Save the new cache state in the flash. */ - TEST_ASSERT(new_nvmem_save() == EC_SUCCESS); - - /* Wipe out NVMEM cache just in case. */ - wipe_out_nvmem_cache(); - - /* Read back from flash into cache. */ - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* One object removed, one added, the number should have not changed. */ - TEST_ASSERT(num_objects == - fill_obj_offsets(offsets, ARRAY_SIZE(offsets))); - - new_obj_index = 0; - modified_obj_index = 0; - for (i = 0; i < num_objects; i++) { - uint32_t handle; - size_t j; - - memcpy(&handle, evictable_offs_to_addr(offsets[i]), - sizeof(handles[i])); - ASSERT(handle != deleted_obj_handle); - - if (handle == new_obj_handle) - new_obj_index = i; - else if (handle == modified_obj_handle) - modified_obj_index = i; - /* - * Remove the found handle from the set of handles which were - * there originally. - */ - for (j = 0; j < num_handles; j++) - if (handles[j] == handle) { - num_handles--; - handles[j] = handles[num_handles]; - break; - } - } - - /* - * Removed object's handle is still in the array, and it should be the - * only remaining element. - */ - TEST_ASSERT(num_handles == 1); - TEST_ASSERT(handles[0] == deleted_obj_handle); - TEST_ASSERT(new_obj_index >= 0); /* New handle was seen in the cache. */ - TEST_ASSERT(modified_obj_index >= - 0); /* Modified object was seen in the cache. */ - - TEST_ASSERT(compare_object(offsets[new_obj_index], - sizeof(new_evictable_object), - new_evictable_object) == EC_SUCCESS); - TEST_ASSERT(compare_object(offsets[modified_obj_index], - modified_obj_size, - modified_obj) == EC_SUCCESS); - return EC_SUCCESS; -} - -static int test_nvmem_tuple_updates(void) -{ - size_t i; - const char *modified_var1 = "var one after"; - const struct tuple *t; - - const struct { - uint8_t *key; - uint8_t *value; - } kv_pairs[] = { - /* Use # as the delimiter to allow \0 in keys/values. */ - {"key0", "var zero before"}, - {"key1", "var one before"} - }; - - TEST_ASSERT(post_init_from_scratch(0xff) == EC_SUCCESS); - - /* Save vars in the nvmem. */ - for (i = 0; i < ARRAY_SIZE(kv_pairs); i++) - TEST_ASSERT(setvar(kv_pairs[i].key, strlen(kv_pairs[i].key), - kv_pairs[i].value, - strlen(kv_pairs[i].value)) == EC_SUCCESS); - - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - /* Verify the vars are still there. */ - for (i = 0; i < ARRAY_SIZE(kv_pairs); i++) { - const struct tuple *t; - - t = getvar(kv_pairs[i].key, strlen(kv_pairs[i].key)); - TEST_ASSERT(t); - TEST_ASSERT(t->val_len == strlen(kv_pairs[i].value)); - TEST_ASSERT(!memcmp(t->data_ + strlen(kv_pairs[i].key), - kv_pairs[i].value, t->val_len)); - freevar(t); - } - - /* - * Now, let's try updating variable 'key1' introducing various failure - * modes. - */ - failure_mode = TEST_FAIL_SAVING_VAR; - TEST_ASSERT(setvar(kv_pairs[1].key, strlen(kv_pairs[1].key), - modified_var1, strlen(modified_var1)) == EC_SUCCESS); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - /* No change should be seen. */ - for (i = 0; i < ARRAY_SIZE(kv_pairs); i++) { - t = getvar(kv_pairs[i].key, strlen(kv_pairs[i].key)); - TEST_ASSERT(t); - TEST_ASSERT(t->val_len == strlen(kv_pairs[i].value)); - TEST_ASSERT(!memcmp(t->data_ + strlen(kv_pairs[i].key), - kv_pairs[i].value, t->val_len)); - freevar(t); - } - failure_mode = TEST_FAIL_FINALIZING_VAR; - TEST_ASSERT(setvar(kv_pairs[1].key, strlen(kv_pairs[1].key), - modified_var1, strlen(modified_var1)) == EC_SUCCESS); - failure_mode = TEST_NO_FAILURE; - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* First variable should be still unchanged. */ - t = getvar(kv_pairs[0].key, strlen(kv_pairs[0].key)); - TEST_ASSERT(t); - TEST_ASSERT(t->val_len == strlen(kv_pairs[0].value)); - TEST_ASSERT(!memcmp(t->data_ + strlen(kv_pairs[0].key), - kv_pairs[0].value, t->val_len)); - freevar(t); - - /* Second variable should be updated. */ - t = getvar(kv_pairs[1].key, strlen(kv_pairs[1].key)); - TEST_ASSERT(t); - TEST_ASSERT(t->val_len == strlen(modified_var1)); - TEST_ASSERT(!memcmp(t->data_ + strlen(kv_pairs[1].key), modified_var1, - t->val_len)); - freevar(t); - - /* A corrupted attempt to update second variable. */ - failure_mode = TEST_FAIL_FINALIZING_VAR; - TEST_ASSERT(setvar(kv_pairs[1].key, strlen(kv_pairs[1].key), - kv_pairs[1].value, strlen(kv_pairs[1].value)) - == EC_SUCCESS); - failure_mode = TEST_NO_FAILURE; - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - - /* Is there an instance of the second variable still in the flash. */ - t = getvar(kv_pairs[1].key, strlen(kv_pairs[1].key)); - TEST_ASSERT(t); - freevar(t); - - /* Delete the remaining instance of the variable. */ - TEST_ASSERT(setvar(kv_pairs[1].key, strlen(kv_pairs[1].key), - NULL, 0) == EC_SUCCESS); - - /* Verify that it is indeed deleted before and after re-init. */ - TEST_ASSERT(!getvar(kv_pairs[1].key, strlen(kv_pairs[1].key))); - TEST_ASSERT(nvmem_init() == EC_SUCCESS); - TEST_ASSERT(!getvar(kv_pairs[1].key, strlen(kv_pairs[1].key))); - - return EC_SUCCESS; -} - -void run_test(void) -{ - run_test_setup(); - - RUN_TEST(test_migration); - RUN_TEST(test_corrupt_nvmem); - RUN_TEST(test_fully_erased_nvmem); - RUN_TEST(test_configured_nvmem); - RUN_TEST(test_nvmem_save); - RUN_TEST(test_var_read_write_delete); - RUN_TEST(test_nvmem_compaction); - RUN_TEST(test_var_boundaries); - RUN_TEST(test_nvmem_erase_tpm_data); - RUN_TEST(test_tpm_nvmem_modify_reserved_objects); - RUN_TEST(test_tpm_nvmem_modify_evictable_objects); - RUN_TEST(test_nvmem_incomplete_transaction); - RUN_TEST(test_nvmem_tuple_updates); - failure_mode = TEST_NO_FAILURE; /* In case the above test failed. */ - RUN_TEST(test_nvmem_tuple_capacity); - RUN_TEST(test_nvmem_interrupted_compaction); - failure_mode = TEST_NO_FAILURE; /* In case the above test failed. */ - - /* - * more tests to come - * RUN_TEST(test_lock); - * RUN_TEST(test_malloc_blocking); - */ - - test_print_result(); -} diff --git a/test/nvmem.tasklist b/test/nvmem.tasklist deleted file mode 100644 index 43afb03fb5..0000000000 --- a/test/nvmem.tasklist +++ /dev/null @@ -1,11 +0,0 @@ -/* Copyright 2016 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. - */ - -/** - * See CONFIG_TASK_LIST in config.h for details. - */ -#define CONFIG_TEST_TASK_LIST \ - TASK_TEST(NV_1, nvmem_first_task, NULL, 384) \ - TASK_TEST(NV_2, nvmem_second_task, NULL, 384) diff --git a/test/nvmem_tpm2_mock.c b/test/nvmem_tpm2_mock.c deleted file mode 100644 index a6d32bcb34..0000000000 --- a/test/nvmem_tpm2_mock.c +++ /dev/null @@ -1,377 +0,0 @@ -/* Copyright 2019 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. - */ -/* Stuff from tpm2 directory. */ - -#include "nvmem_test.h" - -#include "console.h" -#include "nvmem.h" -#include "util.h" - -#define NVMEM_CR50_SIZE 272 - -uint32_t s_evictNvStart; -uint32_t s_evictNvEnd; - -/* Calculate size of TPM NVMEM. */ -#define MOCK_NV_MEMORY_SIZE \ - (NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag) - NVMEM_CR50_SIZE) - -uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {MOCK_NV_MEMORY_SIZE, - NVMEM_CR50_SIZE}; - -/* - * Sizes of the reserved objects stored in the TPM NVMEM. Note that the second - * last object is in fact a variable size field starting with 4 bytes of size - * and then up to 512 bytes of actual index data. The array below assumes that - * the full 512 bytes of the index space are used. - */ -const uint16_t res_sizes[] = {4, 2, 2, 2, 66, 66, 66, 66, 66, 66, - 34, 34, 34, 66, 66, 66, 8, 4, 134, 28, - 3, 4, 4, 4, 4, 4, 2, 15, 2, 8, - 4, 4, 4, 96, 2844, 424, 516, 8}; - -static uint16_t res_addrs[ARRAY_SIZE(res_sizes)]; - -BOOL NvEarlyStageFindHandle(TPM_HANDLE handle) -{ - size_t i; - - res_addrs[0] = 0; - - for (i = 1; i < ARRAY_SIZE(res_addrs); i++) - res_addrs[i] = res_addrs[i - 1] + res_sizes[i - 1]; - - s_evictNvStart = res_addrs[i - 1] + res_sizes[i - 1]; - - s_evictNvEnd = MOCK_NV_MEMORY_SIZE; - return 0; -} - -void NvGetReserved(UINT32 index, NV_RESERVED_ITEM *ri) -{ - uint32_t index_size; - - if (index >= ARRAY_SIZE(res_sizes)) { - ri->size = 0; - return; - } - - ri->offset = res_addrs[index]; - if (index != NV_RAM_INDEX_SPACE) { - ri->size = res_sizes[index]; - return; - } - - memcpy(&index_size, nvmem_cache_base(NVMEM_TPM) + ri->offset, - sizeof(index_size)); - - if (index_size == ~0) - /* Must be starting with empty flash memeory. */ - index_size = 0; - - ri->size = index_size + sizeof(index_size); -} - -UINT16 UINT16_Marshal(UINT16 *source, BYTE **buffer, INT32 *size) -{ - uint16_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe16(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 UINT32_Marshal(UINT32 *source, BYTE **buffer, INT32 *size) -{ - uint32_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe32(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 UINT64_Marshal(UINT64 *source, BYTE **buffer, INT32 *size) -{ - uint64_t value; - - if (!size || (*size < sizeof(value))) - return 0; - - value = htobe64(*source); - - memcpy(*buffer, &value, sizeof(value)); - *buffer += sizeof(value); - *size -= sizeof(value); - - return sizeof(value); -} - -UINT16 TPM2B_DIGEST_Marshal(TPM2B_DIGEST *source, BYTE **buffer, INT32 *size) -{ - UINT16 total_size; - INT32 i; - uint8_t *p; - - total_size = UINT16_Marshal(&source->t.size, buffer, size); - p = *buffer; - - for (i = 0; (i < source->t.size) && *size; ++i) { - *p++ = source->t.buffer[i]; - *size -= 1; - } - - total_size += i; - *buffer = p; - - return total_size; -} - -uint16_t TPM2B_AUTH_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Marshal(source, buffer, size); -} - -uint16_t TPM2B_NONCE_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Marshal(source, buffer, size); -} - -TPM_RC UINT16_Unmarshal(UINT16 *target, BYTE **buffer, INT32 *size) -{ - uint16_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be16toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC UINT32_Unmarshal(UINT32 *target, BYTE **buffer, INT32 *size) -{ - uint32_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be32toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC UINT64_Unmarshal(UINT64 *target, BYTE **buffer, INT32 *size) -{ - uint64_t value; - - if (!size || *size < sizeof(value)) - return TPM_RC_INSUFFICIENT; - - memcpy(&value, *buffer, sizeof(value)); - *target = be64toh(value); - - *buffer += sizeof(value); - *size -= sizeof(value); - - return TPM_RC_SUCCESS; -} - -TPM_RC TPM2B_DIGEST_Unmarshal(TPM2B_DIGEST *target, BYTE **buffer, INT32 *size) -{ - TPM_RC result; - INT32 i; - uint8_t *p; - - result = UINT16_Unmarshal(&target->t.size, buffer, size); - - if (result != TPM_RC_SUCCESS) - return result; - - if (target->t.size == 0) - return TPM_RC_SUCCESS; - - if ((target->t.size > sizeof(TPMU_HA)) || (target->t.size > *size)) - return TPM_RC_SIZE; - - p = *buffer; - for (i = 0; i < target->t.size; ++i) - target->t.buffer[i] = *p++; - - *buffer = p; - *size -= i; - - return TPM_RC_SUCCESS; -} - -TPM_RC TPM2B_AUTH_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Unmarshal(target, buffer, size); -} - -TPM_RC TPM2B_NONCE_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) -{ - return TPM2B_DIGEST_Unmarshal(target, buffer, size); -} - -#define ITER_INIT (~0) - -static void *get_cache_addr(size_t offset) -{ - return (void *)(((uintptr_t)nvmem_cache_base(NVMEM_TPM)) + offset); -} - -static void read_from_cache(size_t offset, size_t size, void *dest) -{ - nvmem_read(offset, size, dest, NVMEM_TPM); -} - -static void write_to_cache(size_t offset, size_t size, void *src) -{ - nvmem_write(offset, size, src, NVMEM_TPM); -} - -/* Copies of the appropriate functions from NV.c in TPM2 library. */ -static uint32_t nv_next(uint32_t *iter) -{ - uint32_t currentIter; - - if (*iter == ITER_INIT) - *iter = s_evictNvStart; - - if ((*iter + sizeof(uint32_t) > s_evictNvEnd) || !*iter) - return 0; - - currentIter = *iter; - read_from_cache(*iter, sizeof(uint32_t), iter); - if (!*iter || (*iter == ITER_INIT)) - return 0; - - return currentIter + sizeof(uint32_t); -} - -static uint32_t nv_get_end(void) -{ - uint32_t iter = ITER_INIT; - uint32_t endAddr = s_evictNvStart; - uint32_t currentAddr; - - while ((currentAddr = nv_next(&iter)) != 0) - endAddr = currentAddr; - - if (endAddr != s_evictNvStart) { - /* Read offset. */ - endAddr -= sizeof(uint32_t); - read_from_cache(endAddr, sizeof(uint32_t), &endAddr); - } - return endAddr; -} - -size_t add_evictable_obj(void *obj, size_t obj_size) -{ - uint32_t end_addr; - uint32_t next_addr; - uint32_t list_end = 0; - - end_addr = nv_get_end(); - - next_addr = end_addr + sizeof(uint32_t) + obj_size; - - if (next_addr >= s_evictNvEnd) { - ccprintf("%s: could not fit %zd bytes!\n", __func__, obj_size); - return 0; - } - - /* Write next pointer */ - write_to_cache(end_addr, sizeof(uint32_t), &next_addr); - /* Write entity data. */ - write_to_cache(end_addr + sizeof(uint32_t), obj_size, obj); - - /* Write the end of list if it fits. */ - if (next_addr + sizeof(uint32_t) <= s_evictNvEnd) - write_to_cache(next_addr, sizeof(list_end), &list_end); - - return obj_size; -} - -/* - * It is the responsibility of the caller to pass the proper address of an - * object in the cache. - */ -void drop_evictable_obj(void *obj) -{ - uint32_t next_addr; - uint32_t list_end = 0; - uint32_t obj_addr; - - obj_addr = (uintptr_t)obj - (uintptr_t)nvmem_cache_base(NVMEM_TPM); - read_from_cache(obj_addr - sizeof(next_addr), sizeof(next_addr), - &next_addr); - ccprintf("%s:%d dropping obj at cache addr %x, offset %x, addr %pP " - "next addr %x aka %x (off s_evictNvStart)\n", - __func__, __LINE__, obj_addr - s_evictNvStart, obj_addr, obj, - next_addr, next_addr - s_evictNvStart); - - /* - * Now, to make it easier to add objects behind the current one, let's - * pretend there is no more objects. - */ - write_to_cache(obj_addr - sizeof(next_addr), sizeof(list_end), - &list_end); - - if (!next_addr || (next_addr == s_evictNvEnd)) - return; - - /* - * Iterate over objects starting with next_addr, copying them into - * obj_addr. - */ - obj_addr = next_addr; - while (1) { - uint32_t next_next_addr; - uint32_t next_obj_size; - - read_from_cache(next_addr, sizeof(next_next_addr), - &next_next_addr); - - if (!next_next_addr || (next_next_addr == s_evictNvEnd)) - return; - - next_obj_size = next_next_addr - obj_addr - sizeof(uint32_t); - add_evictable_obj( - (void *)((uintptr_t)nvmem_cache_base(NVMEM_TPM) + - next_addr + sizeof(uint32_t)), - next_obj_size); - next_addr = next_next_addr; - obj_addr += next_obj_size + sizeof(next_obj_size); - } -} - -void *evictable_offs_to_addr(uint16_t offset) -{ - return (void *)((uintptr_t)get_cache_addr(s_evictNvStart) + offset); -} |