diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | chip/host/config_chip.h | 2 | ||||
-rw-r--r-- | common/u2f.c | 5 | ||||
-rw-r--r-- | fuzz/build.mk | 20 | ||||
-rw-r--r-- | fuzz/fuzz_config.h | 8 | ||||
-rw-r--r-- | fuzz/pinweaver_fuzz.cc (renamed from fuzz/cr50_fuzz.cc) | 4 | ||||
-rw-r--r-- | fuzz/pinweaver_fuzz.owners (renamed from fuzz/cr50_fuzz.owners) | 0 | ||||
-rw-r--r-- | fuzz/pinweaver_fuzz.proto (renamed from fuzz/cr50_fuzz.proto) | 0 | ||||
-rw-r--r-- | fuzz/pinweaver_fuzz.tasklist (renamed from fuzz/cr50_fuzz.tasklist) | 0 | ||||
-rw-r--r-- | fuzz/pinweaver_model.h | 2 | ||||
-rw-r--r-- | fuzz/u2f_fuzz.cc | 283 | ||||
-rw-r--r-- | fuzz/u2f_fuzz.tasklist | 9 | ||||
-rw-r--r-- | include/common.h | 2 | ||||
-rw-r--r-- | include/u2f_cmds.h | 5 |
14 files changed, 323 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore index aeadec0735..ffa7ec071c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ cscope.* .tests-passed .failedboards/ .sizes.txt -.vscode/
\ No newline at end of file +.vscode/ +# Fuzz-test-generated coverage data +default.profraw
\ No newline at end of file diff --git a/chip/host/config_chip.h b/chip/host/config_chip.h index 195744c556..8fea752bae 100644 --- a/chip/host/config_chip.h +++ b/chip/host/config_chip.h @@ -9,7 +9,7 @@ #define __CROS_EC_CONFIG_CHIP_H /* Memory mapping */ -#if !defined(TEST_NVMEM) && !defined(TEST_CR50_FUZZ) +#if !defined(TEST_NVMEM) && !defined(TEST_PINWEAVER_FUZZ) #define CONFIG_FLASH_SIZE 0x00020000 #define CONFIG_FLASH_BANK_SIZE 0x1000 #else diff --git a/common/u2f.c b/common/u2f.c index 89565950dd..4738bf0572 100644 --- a/common/u2f.c +++ b/common/u2f.c @@ -262,9 +262,8 @@ static inline size_t u2f_attest_format_size(uint8_t format) } /* U2F ATTEST command */ -static enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf, - size_t input_size, - size_t *response_size) +enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf, + size_t input_size, size_t *response_size) { const struct u2f_attest_req *req = buf; struct u2f_attest_resp *resp; 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 <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 +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/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/cr50_fuzz.cc b/fuzz/pinweaver_fuzz.cc index 186700f415..853c4341fc 100644 --- a/fuzz/cr50_fuzz.cc +++ b/fuzz/pinweaver_fuzz.cc @@ -2,7 +2,7 @@ // 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. +// Fuzzer for the Cr50 commands related to Pinweaver. #include <unistd.h> @@ -17,7 +17,7 @@ #define HIDE_EC_STDLIB #include "chip/host/persistence.h" -#include "fuzz/cr50_fuzz.pb.h" +#include "fuzz/pinweaver_fuzz.pb.h" #include "fuzz/fuzz_config.h" #include "fuzz/pinweaver_model.h" #include "fuzz/span.h" diff --git a/fuzz/cr50_fuzz.owners b/fuzz/pinweaver_fuzz.owners index 80e5dc1dee..80e5dc1dee 100644 --- a/fuzz/cr50_fuzz.owners +++ b/fuzz/pinweaver_fuzz.owners diff --git a/fuzz/cr50_fuzz.proto b/fuzz/pinweaver_fuzz.proto index 0291eacd88..0291eacd88 100644 --- a/fuzz/cr50_fuzz.proto +++ b/fuzz/pinweaver_fuzz.proto diff --git a/fuzz/cr50_fuzz.tasklist b/fuzz/pinweaver_fuzz.tasklist index 24870f2abb..24870f2abb 100644 --- a/fuzz/cr50_fuzz.tasklist +++ b/fuzz/pinweaver_fuzz.tasklist 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 <unordered_map> #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 <fuzzer/FuzzedDataProvider.h> + +#include <cstdint> +#include <memory> +#include <string> +#include <vector> + +#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<uint8_t> 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<uint8_t>( + 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<uint8_t>( + 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<uint8_t>( + 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<uint8_t>( + 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<uint8_t>( + 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<uint8_t>( + 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 diff --git a/include/common.h b/include/common.h index 2bbc24d684..def017e214 100644 --- a/include/common.h +++ b/include/common.h @@ -134,7 +134,7 @@ #define __warn_unused_result __attribute__((warn_unused_result)) #endif -#ifdef TEST_CR50_FUZZ +#ifdef TEST_PINWEAVER_FUZZ /** * Workaround: Clang incorrectly handles profiling information * used for fuzzing with __attribute__((always_inline)). diff --git a/include/u2f_cmds.h b/include/u2f_cmds.h index 00a12af808..0f441e5855 100644 --- a/include/u2f_cmds.h +++ b/include/u2f_cmds.h @@ -39,6 +39,11 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf, enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size); +/** + * U2F_ATTEST command handler. + */ +enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf, + size_t input_size, size_t *response_size); /* Maximum size in bytes of G2F attestation certificate. */ #define G2F_ATTESTATION_CERT_MAX_LEN 315 |