diff --git a/.gitignore b/.gitignore index 12bde55..7c0c9eb 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,9 @@ target .idea/ /specs/cells .vscode/ +webauthn_coverage +webauthn_fuzzer +tests/pw_webauthn_lib/corpus +tests/pw_webauthn_lib/crash-* +tests/pw_webauthn_lib/fuzz-* +tests/pw_webauthn_lib/coverage/* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..552e576 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.12) +project(pw-lock C) +set(CMAKE_C_STANDARD 11) +# uncomment it for sanitize +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined") +add_compile_options(-Wno-macro-redefined) + +include_directories(deps/libecc/src) +# deps/ckb-c-std-lib +include_directories(c) +include_directories(tests/pw_webauthn_lib) +add_definitions(-DWORDSIZE=64) +add_definitions(-DWITH_STDLIB) +add_definitions(-D__unix__) +add_definitions(-DCKB_SIMULATOR) +add_definitions(-DNN_CONSISTENCY_CHECK) + +add_executable(pw_webauthn_lib tests/pw_webauthn_lib/main_test.c) + diff --git a/tests/pw_webauthn_lib/Makefile b/tests/pw_webauthn_lib/Makefile new file mode 100644 index 0000000..724e953 --- /dev/null +++ b/tests/pw_webauthn_lib/Makefile @@ -0,0 +1,63 @@ +OS = Unknown +ifneq ($(shell uname -a | grep -i Darwin),) + OS = MacOS +endif +ifneq ($(shell uname -a | grep -i Linux),) + OS = Linux +endif +ifeq ($(OS),Unknown) + echo "error: unsupported OS"; exit 1 +endif + +NPROC?=4 +CC=clang +LLVM_PROFDATA=llvm-profdata +LLVM_COV=llvm-cov + +CORPUS_DIR=corpus +FLAGS=-I../../deps/libecc/src -I../../c -I. -DWORDSIZE=64 -DWITH_STDLIB -D__unix__ -DCKB_SIMULATOR -DNN_CONSISTENCY_CHECK -Wno-macro-redefined + +FUZZER_FLAGS=-g -O1 -fsanitize=fuzzer,address,undefined $(FLAGS) + +COVERAGE_DIR=coverage +COVERAGE_FLAGS=-fprofile-instr-generate -fcoverage-mapping $(FLAGS) + +ifeq ($(OS),MacOS) + COVERAGE_FLAGS+=-Wl,-U,_LLVMFuzzerCustomMutator -Wl,-U,_LLVMFuzzerInitialize +endif + +EXTERNAL_HEADERS=../../c/webauthn/pw_webauthn.h + +all: fuzzer coverage + +show: $(COVERAGE_DIR)/fuzzer.profdata + $(LLVM_COV) show --instr-profile=$(COVERAGE_DIR)/fuzzer.profdata webauthn_coverage + +report: $(COVERAGE_DIR)/fuzzer.profdata coverage $(EXTERNAL_HEADERS) + $(LLVM_COV) report --show-functions --instr-profile=$(COVERAGE_DIR)/fuzzer.profdata webauthn_coverage $(EXTERNAL_HEADERS) + +fuzzer: + clang $(FUZZER_FLAGS) fuzzer.c -o webauthn_fuzzer + +coverage: $(EXTERNAL_HEADERS) + clang $(COVERAGE_FLAGS) coverage.c fuzzer.c -o webauthn_coverage + +start-fuzzer: fuzzer + ./webauthn_fuzzer -workers=$(NPROC) -jobs=$(NPROC) -max_len=700 corpus + +clean: + rm -rf webauthn_fuzzer webauthn_coverage webauthn_fuzzer.dSYM + +#%.h:export +# ln -s $(CURDIR)/../$@ $(CURDIR)/$@ + +%.profraw: coverage + echo "start webauthn_coverage" + LLVM_PROFILE_FILE=$@ ./webauthn_coverage $(CORPUS_DIR)/* + +%.profdata: %.profraw + $(LLVM_PROFDATA) merge --sparse $< -o $@ + +.PHONY: all fuzzer coverage report + +.PRECIOUS: $(COVERAGE_DIR)/fuzzer.profraw $(COVERAGE_DIR)/fuzzer.profdata diff --git a/tests/pw_webauthn_lib/README.md b/tests/pw_webauthn_lib/README.md new file mode 100644 index 0000000..e2221c5 --- /dev/null +++ b/tests/pw_webauthn_lib/README.md @@ -0,0 +1,11 @@ +### Fuzzing test +```bash +make +make start-fuzzer +``` + +Get code coverage by: +```bash +make report +make show +``` diff --git a/tests/pw_webauthn_lib/ckb_syscalls.h b/tests/pw_webauthn_lib/ckb_syscalls.h new file mode 100644 index 0000000..3a85fad --- /dev/null +++ b/tests/pw_webauthn_lib/ckb_syscalls.h @@ -0,0 +1,194 @@ +#ifndef CKB_C_STDLIB_CKB_SYSCALLS_H_ +#define CKB_C_STDLIB_CKB_SYSCALLS_H_ + +#include +#include +#include + + +#define SYS_exit 93 +#define SYS_ckb_load_transaction 2051 +#define SYS_ckb_load_script 2052 +#define SYS_ckb_load_tx_hash 2061 +#define SYS_ckb_load_script_hash 2062 +#define SYS_ckb_load_cell 2071 +#define SYS_ckb_load_header 2072 +#define SYS_ckb_load_input 2073 +#define SYS_ckb_load_witness 2074 +#define SYS_ckb_load_cell_by_field 2081 +#define SYS_ckb_load_header_by_field 2082 +#define SYS_ckb_load_input_by_field 2083 +#define SYS_ckb_load_cell_data_as_code 2091 +#define SYS_ckb_load_cell_data 2092 +#define SYS_ckb_debug 2177 + +#define CKB_SUCCESS 0 +#define CKB_INDEX_OUT_OF_BOUND 1 +#define CKB_ITEM_MISSING 2 +#define CKB_LENGTH_NOT_ENOUGH 3 + +#define CKB_SOURCE_INPUT 1 +#define CKB_SOURCE_OUTPUT 2 +#define CKB_SOURCE_CELL_DEP 3 +#define CKB_SOURCE_HEADER_DEP 4 +#define CKB_SOURCE_GROUP_INPUT 0x0100000000000001 +#define CKB_SOURCE_GROUP_OUTPUT 0x0100000000000002 + +#define CKB_CELL_FIELD_CAPACITY 0 +#define CKB_CELL_FIELD_DATA_HASH 1 +#define CKB_CELL_FIELD_LOCK 2 +#define CKB_CELL_FIELD_LOCK_HASH 3 +#define CKB_CELL_FIELD_TYPE 4 +#define CKB_CELL_FIELD_TYPE_HASH 5 +#define CKB_CELL_FIELD_OCCUPIED_CAPACITY 6 + +#define CKB_HEADER_FIELD_EPOCH_NUMBER 0 +#define CKB_HEADER_FIELD_EPOCH_START_BLOCK_NUMBER 1 +#define CKB_HEADER_FIELD_EPOCH_LENGTH 2 + +#define CKB_INPUT_FIELD_OUT_POINT 0 +#define CKB_INPUT_FIELD_SINCE 1 + +int ckb_exit(int8_t code) { return 0;} + +int ckb_load_tx_hash(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_checked_load_tx_hash(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_load_script_hash(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_checked_load_script_hash(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_load_cell(void* addr, uint64_t* len, size_t offset, size_t index, + size_t source) { + return 0; +} + +int ckb_checked_load_cell(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source) { + return 0; +} + +int ckb_load_input(void* addr, uint64_t* len, size_t offset, size_t index, + size_t source) { + return 0; +} + +int ckb_checked_load_input(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source) { + return 0; +} + +int ckb_load_header(void* addr, uint64_t* len, size_t offset, size_t index, + size_t source) { + return 0; +} + +int ckb_checked_load_header(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source) { + return 0; +} + +int ckb_load_witness(void* addr, uint64_t* len, size_t offset, size_t index, + size_t source) { + return 0; +} + +int ckb_checked_load_witness(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source) { + return 0; +} + +int ckb_load_script(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_checked_load_script(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_load_transaction(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_checked_load_transaction(void* addr, uint64_t* len, size_t offset) { + return 0; +} + +int ckb_load_cell_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, size_t field) { + return 0; +} + +int ckb_checked_load_cell_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, size_t field) { + return 0; +} + +int ckb_load_header_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, size_t field) { + return 0; +} + +int ckb_checked_load_header_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, + size_t field) { + return 0; +} + +int ckb_load_input_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, size_t field) { + return 0; +} + +int ckb_checked_load_input_by_field(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source, size_t field) { + return 0; +} + +int ckb_load_cell_code(void* addr, size_t memory_size, size_t content_offset, + size_t content_size, size_t index, size_t source) { + return 0; +} + +int ckb_load_cell_data(void* addr, uint64_t* len, size_t offset, size_t index, + size_t source) { + return 0; +} + +int ckb_checked_load_cell_data(void* addr, uint64_t* len, size_t offset, + size_t index, size_t source) { + return 0; +} + +int ckb_debug(const char* s) { + return 0; +} + +int ckb_load_actual_type_witness(uint8_t* buf, uint64_t* len, size_t index, + size_t* type_source) { + return 0; +} + +/* calculate inputs length */ +int ckb_calculate_inputs_len() { + return 0; +} + +/* + * Look for dep cell with specific data hash, data_hash should a buffer with + * 32 bytes. + */ +int ckb_look_for_dep_with_hash(const uint8_t* data_hash, size_t* index) { + return 0; +} + +#endif /* CKB_C_STDLIB_CKB_SYSCALLS_H_ */ diff --git a/tests/pw_webauthn_lib/coverage.c b/tests/pw_webauthn_lib/coverage.c new file mode 100644 index 0000000..1b44764 --- /dev/null +++ b/tests/pw_webauthn_lib/coverage.c @@ -0,0 +1,41 @@ +/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This main() function can be linked to a fuzz target (i.e. a library +// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize()) +// instead of libFuzzer. This main() function will not perform any fuzzing +// but will simply feed all input files one by one to the fuzz target. +// +// Use this file to provide reproducers for bugs when linking against libFuzzer +// or other fuzzing engine is undesirable. +//===----------------------------------------------------------------------===*/ +#include +#include +#include + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv); + +int main(int argc, char **argv) { + fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); + if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); + for (int i = 1; i < argc; i++) { + fprintf(stderr, "Running: %s\n", argv[i]); + FILE *f = fopen(argv[i], "r"); + assert(f); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char *)malloc(len); + size_t n_read = fread(buf, 1, len, f); + fclose(f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read); + } +} diff --git a/tests/pw_webauthn_lib/fuzzer.c b/tests/pw_webauthn_lib/fuzzer.c new file mode 100644 index 0000000..344c354 --- /dev/null +++ b/tests/pw_webauthn_lib/fuzzer.c @@ -0,0 +1,23 @@ +#include "ckb_syscalls.h" +#include "webauthn/pw_webauthn_lib.c" +#include "stubs.h" + + +/** + * @param message: transaction message digest, size is 32 bytes + * @param lock_args: last 20 bytes sha256 hash of pubkey, used to shield + * the real pubkey. size is 20 bytes + * @param lock_bytes: transaction signature in witness.lock, size is 500 bytes + * + */ +int validate_webauthn(unsigned char* message, unsigned char* lock_args, + unsigned char* lock_bytes, uint64_t lock_bytes_size); + + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + if (size < (564+20+32)) { + return 0; + } + validate_webauthn(data, data+32, data+32+20, 564); + return 0; +} diff --git a/tests/pw_webauthn_lib/main_test.c b/tests/pw_webauthn_lib/main_test.c new file mode 100644 index 0000000..ba16023 --- /dev/null +++ b/tests/pw_webauthn_lib/main_test.c @@ -0,0 +1,13 @@ + +#include "ckb_syscalls.h" +#include "webauthn/pw_webauthn_lib.c" +#include "stubs.h" + + +int verify_challenge_in_client_data(const u8* digest_message, + const u8* client_data, + int client_data_len); +int main() { + +} + diff --git a/tests/pw_webauthn_lib/stubs.h b/tests/pw_webauthn_lib/stubs.h new file mode 100644 index 0000000..8e1a6ef --- /dev/null +++ b/tests/pw_webauthn_lib/stubs.h @@ -0,0 +1,55 @@ +#ifndef PW_LOCK_TESTS_SECP256R1_SHA256_SIGHASH_STUBS_H_ +#define PW_LOCK_TESTS_SECP256R1_SHA256_SIGHASH_STUBS_H_ + +int aff_pt_import_from_buf(aff_pt_t pt, + const u8 *pt_buf, + u16 pt_buf_len, ec_shortw_crv_src_t crv) { + return 0; +} + + +const ec_str_params *ec_get_curve_params_by_name(const u8 *ec_name, + u8 ec_name_len) { + return NULL; +} + +void ec_shortw_aff_to_prj(prj_pt_t out, aff_pt_src_t in) { + +} + +int ec_verify_finalize(struct ec_verify_context *ctx) { + return 0; +} + +int ec_verify_init(struct ec_verify_context *ctx, const ec_pub_key *pub_key, + const u8 *sig, u8 siglen, + ec_sig_alg_type sig_type, hash_alg_type hash_type) +{ + return 0; +} +int ec_verify_update(struct ec_verify_context *ctx, + const u8 *chunk, u32 chunklen) +{ + return 0; + +} + +void import_params(ec_params *out_params, const ec_str_params *in_str_params) +{ + +} + +void sha256(const u8 *input, u32 ilen, u8 output[SHA256_DIGEST_SIZE]) { +} +void sha256_final(sha256_context *ctx, u8 output[SHA256_DIGEST_SIZE]) { +} +void sha256_init(sha256_context *ctx) +{ + +} + +void sha256_update(sha256_context *ctx, const u8 *input, u32 ilen) +{ + +} +#endif //PW_LOCK_TESTS_SECP256R1_SHA256_SIGHASH_STUBS_H_