From e805689eacb082f252a25b7a9b07a44d8809442f Mon Sep 17 00:00:00 2001 From: Howard Yang Date: Thu, 16 Sep 2021 16:06:45 +0800 Subject: Reland "cr50_fuzz: Add fuzzer for u2f commands" This is a reland of 3cac98670745fc5ca82a058fab512567f8444759 The structure of u2f command related types are updated before the original CL lands. Update the fuzzer to correctly fuzz the new code, and ignore the profdata generated by fuzzers in .gitignore. Original change's description: > cr50_fuzz: Add fuzzer for u2f commands > > Currently there's only one fuzzer for Pinweaver and one for host > commands in cr50. Add a fuzzer for the u2f commands (generate, sign, > attest) used in the WebAuthn flow to ensure its security. Most regions > of the concerning functions are covered except for pure error code > returns and unreachable regions (currently auth secret is not used in > sign and attest command yet). > > Rename old cr50_fuzz namings to pinweaver_fuzz, since they only cover > Pinweaver commands. > > BUG=b:172367435 > TEST=make buildall -j > TEST=make host-u2f_fuzz && \ > ./build/host/u2f_fuzz/u2f_fuzz.exe -timeout=10 \ > -ignore_ooms=false -ignore_timeouts=false -fork=71; \ > llvm-profdata merge -sparse default.profraw -o default.profdata; \ > llvm-cov show ./build/host/u2f_fuzz/u2f_fuzz.exe \ > -object ./build/host/u2f_fuzz/RO/board/cr50/dcrypto/u2f.o \ > --instr-profile default.profdata \ > board/cr50/dcrypto/u2f.c common/u2f.c > report > > Cq-Depend: chromium:3162473 > Change-Id: I02b820cf03f7b46ccad7c3bc7b82e73ff45217c6 > Signed-off-by: Howard Yang > Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3162469 > Reviewed-by: Andrey Pronin > Reviewed-by: Vadim Sukhomlinov > Reviewed-by: Leo Lai Bug: b:172367435 Change-Id: I279e20b21a11e0ec957b6a5c3e95bc9a3b9df196 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3217474 Reviewed-by: Vadim Sukhomlinov Tested-by: Howard Yang Commit-Queue: Howard Yang --- fuzz/build.mk | 20 +-- fuzz/cr50_fuzz.cc | 148 ---------------------- fuzz/cr50_fuzz.owners | 5 - fuzz/cr50_fuzz.proto | 31 ----- fuzz/cr50_fuzz.tasklist | 9 -- fuzz/fuzz_config.h | 8 +- fuzz/pinweaver_fuzz.cc | 148 ++++++++++++++++++++++ fuzz/pinweaver_fuzz.owners | 5 + fuzz/pinweaver_fuzz.proto | 31 +++++ fuzz/pinweaver_fuzz.tasklist | 9 ++ fuzz/pinweaver_model.h | 2 +- fuzz/u2f_fuzz.cc | 283 +++++++++++++++++++++++++++++++++++++++++++ fuzz/u2f_fuzz.tasklist | 9 ++ 13 files changed, 502 insertions(+), 206 deletions(-) delete mode 100644 fuzz/cr50_fuzz.cc delete mode 100644 fuzz/cr50_fuzz.owners delete mode 100644 fuzz/cr50_fuzz.proto delete mode 100644 fuzz/cr50_fuzz.tasklist create mode 100644 fuzz/pinweaver_fuzz.cc create mode 100644 fuzz/pinweaver_fuzz.owners create mode 100644 fuzz/pinweaver_fuzz.proto create mode 100644 fuzz/pinweaver_fuzz.tasklist create mode 100644 fuzz/u2f_fuzz.cc create mode 100644 fuzz/u2f_fuzz.tasklist (limited to 'fuzz') diff --git a/fuzz/build.mk b/fuzz/build.mk index 5cc2a40859..e769f5282e 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 +fuzz-test-list-host += pinweaver_fuzz host_command_fuzz u2f_fuzz endif # For fuzzing targets libec.a is built from the ro objects and hides functions @@ -24,20 +24,24 @@ endif # Does your object file need to link against cstdlib? # Yes -> use -rw # Otherwise use -y -cr50_fuzz-rw = cr50_fuzz.o pinweaver_model.o mem_hash_tree.o nvmem_tpm2_mock.o +pinweaver_fuzz-rw = pinweaver_fuzz.o pinweaver_model.o \ + mem_hash_tree.o nvmem_tpm2_mock.o host_command_fuzz-y = host_command_fuzz.o +u2f_fuzz-y = u2f_fuzz.o +u2f_fuzz-y += ../board/cr50/dcrypto/u2f.o -CR50_PROTO_HEADERS := $(out)/gen/fuzz/cr50_fuzz.pb.h \ +CR50_PROTO_HEADERS := $(out)/gen/fuzz/pinweaver_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} +$(out)/RW/fuzz/pinweaver_fuzz.o: ${CR50_PROTO_HEADERS} +$(out)/RW/fuzz/pinweaver_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)/RO/common/u2f.o: CFLAGS += -DU2F_TEST -$(out)/cr50_fuzz.exe: $(out)/cryptoc/libcryptoc.a \ - $(out)/gen/fuzz/cr50_fuzz.pb.o \ +$(out)/pinweaver_fuzz.exe: $(out)/cryptoc/libcryptoc.a \ + $(out)/gen/fuzz/pinweaver_fuzz.pb.o \ $(out)/gen/fuzz/pinweaver/pinweaver.pb.o \ -$(out)/cr50_fuzz.exe: LDFLAGS_EXTRA+=-lcrypto ${LIBPROTOBUF_MUTATOR_LDLIBS} +$(out)/pinweaver_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 - -#include -#include -#include -#include -#include - -#include -#include - -#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(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 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 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/fuzz_config.h b/fuzz/fuzz_config.h index 781921870a..bb10294c3c 100644 --- a/fuzz/fuzz_config.h +++ b/fuzz/fuzz_config.h @@ -12,7 +12,7 @@ /* Disable hibernate: We never want to exit while fuzzing. */ #undef CONFIG_HIBERNATE -#ifdef TEST_CR50_FUZZ +#ifdef TEST_PINWEAVER_FUZZ #define CONFIG_DCRYPTO #define CONFIG_PINWEAVER #define CONFIG_UPTO_SHA512 @@ -80,7 +80,7 @@ enum nvmem_users { /******************************************************************************/ #define CONFIG_SW_CRC -#endif /* TEST_CR50_FUZZ */ +#endif /* TEST_PINWEAVER_FUZZ */ #ifdef TEST_HOST_COMMAND_FUZZ #undef CONFIG_HOSTCMD_DEBUG_MODE @@ -102,11 +102,11 @@ enum nvmem_users { #endif /* TEST_HOST_COMMAND_FUZZ */ -#ifdef TEST_CR50_U2F_FUZZ +#ifdef TEST_U2F_FUZZ #define CONFIG_DCRYPTO #define CONFIG_U2F #define CC_EXTENSION CC_COMMAND -#endif /* TEST_CR50_U2F_FUZZ */ +#endif /* TEST_U2F_FUZZ */ #endif /* TEST_FUZZ */ #endif /* __FUZZ_FUZZ_CONFIG_H */ diff --git a/fuzz/pinweaver_fuzz.cc b/fuzz/pinweaver_fuzz.cc new file mode 100644 index 0000000000..853c4341fc --- /dev/null +++ b/fuzz/pinweaver_fuzz.cc @@ -0,0 +1,148 @@ +// 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 Cr50 commands related to Pinweaver. + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define HIDE_EC_STDLIB +#include "chip/host/persistence.h" +#include "fuzz/pinweaver_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(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 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 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/pinweaver_fuzz.owners b/fuzz/pinweaver_fuzz.owners new file mode 100644 index 0000000000..80e5dc1dee --- /dev/null +++ b/fuzz/pinweaver_fuzz.owners @@ -0,0 +1,5 @@ +# 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/pinweaver_fuzz.proto b/fuzz/pinweaver_fuzz.proto new file mode 100644 index 0000000000..0291eacd88 --- /dev/null +++ b/fuzz/pinweaver_fuzz.proto @@ -0,0 +1,31 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto3"; + +package fuzz; + +import public "fuzz/pinweaver/pinweaver.proto"; + +message RandomBytes { + bytes value = 1; +} + +message Cr50SubAction { + // Allows a logical representation of an action (PinWeaver) or a literal + // representation (RandomBytes). The logical representation fills out the + // expected values of particular fields when they are empty or not part of the + // proto so that the fuzzer can reach parts of the code without having to + // brute force an HMAC. The literal representation allows for the fuzzer to + // represent inputs that cannot be represented with the logical + // representation. + oneof sub_action { + RandomBytes random_bytes = 1; + pinweaver.Request pinweaver = 2; + } +} + +message Cr50FuzzerInput { + repeated Cr50SubAction sub_actions = 1; +} diff --git a/fuzz/pinweaver_fuzz.tasklist b/fuzz/pinweaver_fuzz.tasklist new file mode 100644 index 0000000000..24870f2abb --- /dev/null +++ b/fuzz/pinweaver_fuzz.tasklist @@ -0,0 +1,9 @@ +/* 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/pinweaver_model.h b/fuzz/pinweaver_model.h index 84508786f3..a16e54690f 100644 --- a/fuzz/pinweaver_model.h +++ b/fuzz/pinweaver_model.h @@ -12,7 +12,7 @@ #include #define HIDE_EC_STDLIB -#include "fuzz/cr50_fuzz.pb.h" +#include "fuzz/pinweaver_fuzz.pb.h" #include "fuzz/mem_hash_tree.h" #include "fuzz/span.h" #include "include/pinweaver.h" diff --git a/fuzz/u2f_fuzz.cc b/fuzz/u2f_fuzz.cc new file mode 100644 index 0000000000..dfb46c966a --- /dev/null +++ b/fuzz/u2f_fuzz.cc @@ -0,0 +1,283 @@ +/* Copyright 2021 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 + +#include +#include +#include +#include + +#define HIDE_EC_STDLIB +extern "C" { +#include "physical_presence.h" +#include "u2f_cmds.h" +#include "u2f_impl.h" +#include "internal.h" +} + +extern "C" { +/******************************************************************************/ +/* Mock implementations of cr50 board. + */ +int system_get_chip_unique_id(uint8_t **id) +{ + return P256_NBYTES; +} + +/******************************************************************************/ +/* Mock implementations of Dcrypto functionality. + */ +int DCRYPTO_x509_gen_u2f_cert_name(const p256_int *d, const p256_int *pk_x, + const p256_int *pk_y, const p256_int *serial, + const char *name, uint8_t *cert, const int n) +{ + memset(cert, 1, n); + return n; +} + +enum dcrypto_result DCRYPTO_p256_key_from_bytes( + p256_int *x, p256_int *y, p256_int *d, + const uint8_t key_bytes[P256_NBYTES]) +{ + p256_int key; + + p256_from_bin(key_bytes, &key); + + // The actual condition for this function to fail happens rarely, + // and not able to to control. So we assume it fails for some inputs + // for fuzz purpose. + if (P256_DIGIT(&key, 0) % 10 == 0) { + return DCRYPTO_RETRY; + } + + if (p256_lt_blinded(&key, &SECP256r1_nMin2) >= 0) + return DCRYPTO_RETRY; + p256_add_d(&key, 1, d); + if (x == NULL || y == NULL) + return DCRYPTO_OK; + memset(x, 0, P256_NBYTES); + memset(y, 0, P256_NBYTES); + return DCRYPTO_OK; +} + +enum dcrypto_result dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, + const p256_int *key, + const p256_int *message, + p256_int *r, p256_int *s) +{ + memset(r, 0, sizeof(p256_int)); + memset(s, 0, sizeof(p256_int)); + return DCRYPTO_OK; +} + +/******************************************************************************/ +/* Mock implementations of U2F functionality. + */ +static struct u2f_state ustate; +struct u2f_state *u2f_get_state(void) +{ + return &ustate; +} + +static enum touch_state tstate; +enum touch_state pop_check_presence(int consume) +{ + return tstate; +} + +uint8_t buffer[512]; + +int test_fuzz_one_input(const uint8_t *data, unsigned int size) +{ + FuzzedDataProvider data_provider(data, size); + + if (data_provider.ConsumeBool()) { + ustate.drbg_entropy_size = 64; + } else { + ustate.drbg_entropy_size = 32; + } + + if (data_provider.ConsumeBool()) { + tstate = POP_TOUCH_YES; + } else { + tstate = POP_TOUCH_NO; + } + + while (data_provider.remaining_bytes() > 0) { + std::vector bytes; + int command_num = data_provider.ConsumeIntegralInRange(0, 2); + + size_t request_size, response_size; + struct u2f_generate_req *generate_req; + struct u2f_generate_resp *generate_resp_v0; + struct u2f_generate_versioned_resp *generate_resp_v1; + struct u2f_generate_versioned_resp_v2 *generate_resp_v2; + struct u2f_sign_req *sign_req_v0; + struct u2f_sign_versioned_req *sign_req_v1; + struct u2f_sign_versioned_req_v2 *sign_req_v2; + struct u2f_attest_req *attest_req; + struct g2f_register_msg_v0 *g2f_msg_v0; + uint8_t appId[U2F_APPID_SIZE]; + uint8_t userSecret[U2F_USER_SECRET_SIZE]; + uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE]; + uint8_t flags; + struct u2f_key_handle kh_0; + struct u2f_versioned_key_handle kh_1; + struct u2f_key_handle_v2 kh_2; + struct u2f_ec_point public_key; + int kh_version; + vendor_cmd_rc rc; + + switch (command_num) { + case 0: + bytes = data_provider.ConsumeBytes( + sizeof(struct u2f_generate_req)); + memcpy(buffer, bytes.data(), bytes.size()); + generate_req = (u2f_generate_req *)buffer; + memcpy(appId, generate_req->appId, U2F_APPID_SIZE); + memcpy(userSecret, generate_req->userSecret, + U2F_USER_SECRET_SIZE); + memcpy(authTimeSecretHash, + generate_req->authTimeSecretHash, + U2F_AUTH_TIME_SECRET_SIZE); + flags = generate_req->flags; + response_size = 512; + rc = u2f_generate_cmd(VENDOR_CC_U2F_GENERATE, buffer, + sizeof(struct u2f_generate_req), + &response_size); + if (rc != VENDOR_RC_SUCCESS) { + break; + } + if (response_size == sizeof(struct u2f_generate_resp)) { + kh_version = 0; + } else if (response_size == + sizeof(struct u2f_generate_versioned_resp)) { + kh_version = 1; + } else if (response_size == + sizeof(struct u2f_generate_versioned_resp_v2)) { + kh_version = 2; + } else { + exit(1); + } + if (kh_version == 0) { + generate_resp_v0 = (u2f_generate_resp *)buffer; + kh_0 = generate_resp_v0->keyHandle; + public_key = generate_resp_v0->pubKey; + sign_req_v0 = (u2f_sign_req *)buffer; + memcpy(sign_req_v0->appId, appId, + U2F_APPID_SIZE); + memcpy(sign_req_v0->userSecret, userSecret, + U2F_USER_SECRET_SIZE); + sign_req_v0->flags = flags; + sign_req_v0->keyHandle = kh_0; + bytes = data_provider.ConsumeBytes( + U2F_P256_SIZE); + memcpy(sign_req_v0->hash, bytes.data(), + bytes.size()); + request_size = sizeof(struct u2f_sign_req); + } else if (kh_version == 1) { + generate_resp_v1 = + (u2f_generate_versioned_resp *)buffer; + kh_1 = generate_resp_v1->keyHandle; + sign_req_v1 = (u2f_sign_versioned_req *)buffer; + memcpy(sign_req_v1->appId, appId, + U2F_APPID_SIZE); + memcpy(sign_req_v1->userSecret, userSecret, + U2F_USER_SECRET_SIZE); + sign_req_v1->flags = flags; + sign_req_v1->keyHandle = kh_1; + bytes = data_provider.ConsumeBytes( + U2F_P256_SIZE); + memcpy(sign_req_v1->hash, bytes.data(), + bytes.size()); + request_size = + sizeof(struct u2f_sign_versioned_req); + } else { + generate_resp_v2 = + (u2f_generate_versioned_resp_v2 *)buffer; + kh_2 = generate_resp_v2->keyHandle; + sign_req_v2 = + (u2f_sign_versioned_req_v2 *)buffer; + memcpy(sign_req_v2->appId, appId, + U2F_APPID_SIZE); + memcpy(sign_req_v2->userSecret, userSecret, + U2F_USER_SECRET_SIZE); + memcpy(sign_req_v2->authTimeSecret, + authTimeSecretHash, + U2F_AUTH_TIME_SECRET_SIZE); + sign_req_v2->flags = flags; + sign_req_v2->keyHandle = kh_2; + bytes = data_provider.ConsumeBytes( + U2F_P256_SIZE); + memcpy(sign_req_v2->hash, bytes.data(), + bytes.size()); + request_size = sizeof( + struct u2f_sign_versioned_req_v2); + } + response_size = 512; + u2f_sign_cmd(VENDOR_CC_U2F_SIGN, buffer, request_size, + &response_size); + if (kh_version == 0) { + attest_req = (u2f_attest_req *)buffer; + attest_req->format = U2F_ATTEST_FORMAT_REG_RESP; + attest_req->dataLen = + sizeof(struct g2f_register_msg_v0); + memcpy(attest_req->userSecret, userSecret, + U2F_USER_SECRET_SIZE); + g2f_msg_v0 = + (g2f_register_msg_v0 *)attest_req->data; + g2f_msg_v0->reserved = 0; + memcpy(g2f_msg_v0->app_id, appId, + U2F_APPID_SIZE); + memcpy(g2f_msg_v0->key_handle.hmac, kh_0.hmac, + sizeof(kh_0.hmac)); + memcpy(g2f_msg_v0->key_handle.origin_seed, + kh_0.origin_seed, + sizeof(kh_0.origin_seed)); + g2f_msg_v0->public_key = public_key; + bytes = data_provider.ConsumeBytes( + U2F_CHAL_SIZE); + memcpy(g2f_msg_v0->challenge, bytes.data(), + bytes.size()); + response_size = 512; + u2f_attest_cmd(VENDOR_CC_U2F_ATTEST, buffer, + sizeof(struct u2f_attest_req), + &response_size); + } + break; + case 1: { + int version = + data_provider.ConsumeIntegralInRange(0, 2); + request_size = + (version == 0) ? + sizeof(struct u2f_sign_req) : + (version == 1) ? + sizeof(struct u2f_sign_versioned_req) : + sizeof(struct u2f_sign_versioned_req_v2); + bytes = data_provider.ConsumeBytes( + request_size); + memcpy(buffer, bytes.data(), bytes.size()); + response_size = 512; + u2f_sign_cmd(VENDOR_CC_U2F_SIGN, buffer, request_size, + &response_size); + break; + } + case 2: + auto str = data_provider.ConsumeRandomLengthString(256); + memcpy(buffer, str.data(), str.size()); + attest_req = (u2f_attest_req *)buffer; + attest_req->dataLen = + sizeof(struct g2f_register_msg_v0); + response_size = 512; + u2f_attest_cmd(VENDOR_CC_U2F_ATTEST, buffer, str.size(), + &response_size); + break; + } + break; + } + return 0; +} +} \ No newline at end of file diff --git a/fuzz/u2f_fuzz.tasklist b/fuzz/u2f_fuzz.tasklist new file mode 100644 index 0000000000..24870f2abb --- /dev/null +++ b/fuzz/u2f_fuzz.tasklist @@ -0,0 +1,9 @@ +/* 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 -- cgit v1.2.1