summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHoward Yang <hcyang@google.com>2021-09-16 16:06:45 +0800
committerCommit Bot <commit-bot@chromium.org>2021-10-07 07:38:13 +0000
commit3cac98670745fc5ca82a058fab512567f8444759 (patch)
treeabd10abc470a197e3c81c9971b9277965fc9b140
parentaf9fd4fba4285b0bd73b96ff6c58e7fcc950c441 (diff)
downloadchrome-ec-stabilize-14268.51.B-cr50_stab.tar.gz
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 <hcyang@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3162469 Reviewed-by: Andrey Pronin <apronin@chromium.org> Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Leo Lai <cylai@google.com>
-rw-r--r--chip/host/config_chip.h2
-rw-r--r--common/u2f.c5
-rw-r--r--fuzz/build.mk20
-rw-r--r--fuzz/fuzz_config.h8
-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.h2
-rw-r--r--fuzz/u2f_fuzz.cc240
-rw-r--r--fuzz/u2f_fuzz.tasklist9
-rw-r--r--include/u2f_cmds.h5
12 files changed, 276 insertions, 19 deletions
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..356301e52a
--- /dev/null
+++ b/fuzz/u2f_fuzz.cc
@@ -0,0 +1,240 @@
+/* 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 *resp;
+ struct u2f_generate_versioned_resp *versioned_resp;
+ struct u2f_sign_req *sign_req;
+ struct u2f_sign_versioned_req *sign_versioned_req;
+ struct u2f_attest_req *attest_req;
+ struct g2f_register_msg *g2f_msg;
+ uint8_t appId[U2F_APPID_SIZE];
+ uint8_t userSecret[U2F_USER_SECRET_SIZE];
+ uint8_t flags;
+ struct u2f_key_handle kh;
+ struct u2f_versioned_key_handle versioned_kh;
+ 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);
+ 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;
+ }
+ kh_version = (response_size ==
+ sizeof(struct u2f_generate_resp)) ?
+ 0 :
+ 1;
+ if (!kh_version) {
+ resp = (u2f_generate_resp *)buffer;
+ kh = resp->keyHandle;
+ public_key = resp->pubKey;
+ sign_req = (u2f_sign_req *)buffer;
+ memcpy(sign_req->appId, appId, U2F_APPID_SIZE);
+ memcpy(sign_req->userSecret, userSecret,
+ U2F_USER_SECRET_SIZE);
+ sign_req->flags = flags;
+ sign_req->keyHandle = kh;
+ bytes = data_provider.ConsumeBytes<uint8_t>(
+ U2F_P256_SIZE);
+ memcpy(sign_req->hash, bytes.data(),
+ bytes.size());
+ request_size = sizeof(struct u2f_sign_req);
+ } else {
+ versioned_resp =
+ (u2f_generate_versioned_resp *)buffer;
+ versioned_kh = versioned_resp->keyHandle;
+ sign_versioned_req =
+ (u2f_sign_versioned_req *)buffer;
+ memcpy(sign_versioned_req->appId, appId,
+ U2F_APPID_SIZE);
+ memcpy(sign_versioned_req->userSecret,
+ userSecret, U2F_USER_SECRET_SIZE);
+ sign_versioned_req->flags = flags;
+ sign_versioned_req->keyHandle = versioned_kh;
+ bytes = data_provider.ConsumeBytes<uint8_t>(
+ U2F_P256_SIZE);
+ memcpy(sign_versioned_req->hash, bytes.data(),
+ bytes.size());
+ request_size =
+ sizeof(struct u2f_sign_versioned_req);
+ }
+ response_size = 512;
+ u2f_sign_cmd(VENDOR_CC_U2F_SIGN, buffer, request_size,
+ &response_size);
+ if (!kh_version) {
+ attest_req = (u2f_attest_req *)buffer;
+ attest_req->format = U2F_ATTEST_FORMAT_REG_RESP;
+ attest_req->dataLen =
+ sizeof(struct g2f_register_msg);
+ memcpy(attest_req->userSecret, userSecret,
+ U2F_USER_SECRET_SIZE);
+ g2f_msg = (g2f_register_msg *)attest_req->data;
+ g2f_msg->reserved = 0;
+ memcpy(g2f_msg->app_id, appId, U2F_APPID_SIZE);
+ memcpy(g2f_msg->key_handle.hmac, kh.hmac,
+ sizeof(kh.hmac));
+ memcpy(g2f_msg->key_handle.origin_seed,
+ kh.origin_seed, sizeof(kh.origin_seed));
+ g2f_msg->public_key = public_key;
+ bytes = data_provider.ConsumeBytes<uint8_t>(
+ U2F_CHAL_SIZE);
+ memcpy(g2f_msg->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: {
+ bool is_versioned = data_provider.ConsumeBool();
+ request_size =
+ is_versioned ?
+ sizeof(struct u2f_sign_versioned_req) :
+ sizeof(struct u2f_sign_req);
+ 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);
+ 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/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