mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
crypto: improve fuzz testing code, harnesses, documentation and scripts
This commit is contained in:
parent
4f7c6b3586
commit
78f879aaf1
@ -1,4 +1,4 @@
|
|||||||
# trezor-crypto fuzzing
|
# Fuzz Testing trezor-crypto
|
||||||
Selected functions can be fuzzed via specific libFuzzer harnesses for increased test coverage and issue detection.
|
Selected functions can be fuzzed via specific libFuzzer harnesses for increased test coverage and issue detection.
|
||||||
|
|
||||||
Note: the following commands are relative to the trezor-crypto main directory.
|
Note: the following commands are relative to the trezor-crypto main directory.
|
||||||
@ -19,15 +19,17 @@ Recommended: ASAN / UBSAN / MSAN flags for error detection can be specified via
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
* `SANFLAGS="-fsanitize=address,undefined"`
|
* `SANFLAGS="-fsanitize=address,undefined"`
|
||||||
* `SANFLAGS="-fsanitize=memory"`
|
* `SANFLAGS="-fsanitize=memory -fsanitize-memory-track-origins"`
|
||||||
|
|
||||||
|
|
||||||
### Optimizations
|
### Optimizations
|
||||||
|
|
||||||
Override `OPTFLAGS` to test the library at different optimization levels or simplify the debugging of detected issues.
|
Override `OPTFLAGS` to test the library at different optimization levels or simplify the debugging of detected issues.
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
|
|
||||||
* `OPTFLAGS="-O0 -ggdb3"`
|
* `OPTFLAGS="-O0 -ggdb3"`
|
||||||
|
* `OPTFLAGS="-O3 -march=native"`
|
||||||
|
|
||||||
## Operation
|
## Operation
|
||||||
|
|
||||||
@ -46,18 +48,22 @@ mkdir fuzzer/fuzzer_corpus
|
|||||||
./fuzzer/fuzzer -max_len=2048 -use_value_profile=1 -jobs=16 -timeout=1 -reload=5 -print_pcs=1 -print_funcs=42 fuzzer/fuzzer_corpus
|
./fuzzer/fuzzer -max_len=2048 -use_value_profile=1 -jobs=16 -timeout=1 -reload=5 -print_pcs=1 -print_funcs=42 fuzzer/fuzzer_corpus
|
||||||
```
|
```
|
||||||
|
|
||||||
Hint: for more permanent setups, consider invoking the fuzzer from outside of the source directory to avoid cluttering it with logfiles and crash inputs.
|
Hint: for more permanent setups, consider invoking the fuzzer from outside of the source directory to avoid cluttering it with logfiles and crash inputs. Similarly, it is recommended to store the fuzzer corpus in another location.
|
||||||
|
|
||||||
## Automated fuzzer dictionary generation
|
## Automated Fuzzer Dictionary Generation
|
||||||
|
|
||||||
[Dictionaries](https://llvm.org/docs/LibFuzzer.html#dictionaries) are a useful mechanism to augment the capabilities of the fuzzer. Specify them via the `-dict=` flag.
|
[Dictionaries](https://llvm.org/docs/LibFuzzer.html#dictionaries) are a useful mechanism to augment the capabilities of the fuzzer. Specify them via the `-dict=` flag.
|
||||||
|
|
||||||
### Collect interesting strings from the unit tests
|
### Collect Interesting Strings From Unit Tests
|
||||||
``` bash
|
|
||||||
grep -r -P -o -h "\"\w+\"" tests | sort | uniq > fuzzer_crypto_tests_strings_dictionary1.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
## Evaluate source coverage
|
```bash
|
||||||
|
cd fuzzer
|
||||||
|
./extract_fuzzer_dictionary.sh fuzzer_crypto_tests_strings_dictionary1.txt
|
||||||
|
```
|
||||||
|
The resulting file can be used as a fuzzer dictionary.
|
||||||
|
|
||||||
|
|
||||||
|
## Evaluate Source Coverage
|
||||||
|
|
||||||
1. build the fuzzer binary with `CFLAGS="-fprofile-instr-generate -fcoverage-mapping"`
|
1. build the fuzzer binary with `CFLAGS="-fprofile-instr-generate -fcoverage-mapping"`
|
||||||
1. run with suitable `-runs=` or `-max_total_time=` limits
|
1. run with suitable `-runs=` or `-max_total_time=` limits
|
||||||
|
31
crypto/fuzzer/extract_fuzzer_dictionary.sh
Executable file
31
crypto/fuzzer/extract_fuzzer_dictionary.sh
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# usage: script.sh target-dictionary-filename
|
||||||
|
|
||||||
|
# this script searches for interesting strings in the source code and converts
|
||||||
|
# them into a standard fuzzer dictionary file.
|
||||||
|
|
||||||
|
TARGETFILE=${1:-fuzzer_crypto_tests_strings_dictionary1.txt}
|
||||||
|
|
||||||
|
# collect strings with normal words from the tests
|
||||||
|
grep -r -P -o -h "\"[\w ]+\"" ../tests | sort | uniq > $TARGETFILE
|
||||||
|
|
||||||
|
# collect BIP39 and SLIP39 words
|
||||||
|
grep -r -P -o -h "\"\w+\"" ../slip39_wordlist.h ../bip39_english.h | sort | uniq >> $TARGETFILE
|
||||||
|
|
||||||
|
# hex string to quoted escaped hex conversion
|
||||||
|
# TODO add an inverted output variant with swapped endian order?
|
||||||
|
grep -r -P -o -h "([0-9a-fA-F][0-9a-fA-F])+" ../tests | sort | uniq | \
|
||||||
|
while read -r line ; do
|
||||||
|
# double escape since it is going to be used in bash
|
||||||
|
escaped_hex=`echo $line | sed -e 's/../\\\\x&/g'`
|
||||||
|
echo "\"$escaped_hex\"" >> $TARGETFILE
|
||||||
|
done
|
||||||
|
|
||||||
|
# search and reassemble BIP39 seed mnemonics that span multiple lines from the tests
|
||||||
|
# valid words are 3 to 10 characters long and there are 12, 18 or 24 words in a valid mnemonic
|
||||||
|
grep -r -P -o -h "\"(\w{3,10} ?)+\",?" ../tests | grep -vP "[0-9A-Z]" | tr '"\n' ' ' | \
|
||||||
|
sed 's/ / /g' | sed 's/ / /g'| grep -Po "(\w{3,10} ){11,23}(\w{3,10})" | sort | uniq | \
|
||||||
|
while read -r line ; do
|
||||||
|
echo "\"$line\"" >> $TARGETFILE
|
||||||
|
done
|
@ -20,16 +20,25 @@
|
|||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// necessary for the target functions
|
// necessary for the target functions
|
||||||
|
#include "aes/aes.h"
|
||||||
#include "bignum.h"
|
#include "bignum.h"
|
||||||
#include "ecdsa.h"
|
#include "ecdsa.h"
|
||||||
#include "hasher.h"
|
#include "hasher.h"
|
||||||
|
#include "nist256p1.h"
|
||||||
#include "rand.h"
|
#include "rand.h"
|
||||||
|
#include "secp256k1.h"
|
||||||
|
|
||||||
|
#include "ed25519-donna/ed25519-donna.h"
|
||||||
|
#include "ed25519-donna/ed25519.h"
|
||||||
|
#include "nem.h"
|
||||||
|
#include "shamir.h"
|
||||||
|
|
||||||
/* fuzzer input data handling */
|
/* fuzzer input data handling */
|
||||||
const uint8_t *fuzzer_ptr;
|
const uint8_t *fuzzer_ptr;
|
||||||
@ -58,13 +67,14 @@ void fuzzer_reset_state(void) {
|
|||||||
extern size_t bn_format(const bignum256 *amnt, const char *prefix,
|
extern size_t bn_format(const bignum256 *amnt, const char *prefix,
|
||||||
const char *suffix, unsigned int decimals, int exponent,
|
const char *suffix, unsigned int decimals, int exponent,
|
||||||
bool trailing, char *out, size_t outlen);
|
bool trailing, char *out, size_t outlen);
|
||||||
int fuzz_bn_format() {
|
int fuzz_bn_format(void) {
|
||||||
bignum256 target_bignum;
|
bignum256 target_bignum;
|
||||||
|
// we need some amount of data, bail if the input is too short
|
||||||
if (fuzzer_length < sizeof(target_bignum)) {
|
if (fuzzer_length < sizeof(target_bignum)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[512];
|
char buf[512] = {0};
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
// mutate the struct contents
|
// mutate the struct contents
|
||||||
@ -77,6 +87,8 @@ int fuzz_bn_format() {
|
|||||||
}
|
}
|
||||||
memcpy(&prefixlen, fuzzer_input(1), 1);
|
memcpy(&prefixlen, fuzzer_input(1), 1);
|
||||||
char prefix[prefixlen];
|
char prefix[prefixlen];
|
||||||
|
memset(&prefix, 0, prefixlen);
|
||||||
|
|
||||||
if (prefixlen > 0 && prefixlen <= 128 && prefixlen <= fuzzer_length) {
|
if (prefixlen > 0 && prefixlen <= 128 && prefixlen <= fuzzer_length) {
|
||||||
memcpy(&prefix, fuzzer_input(prefixlen), prefixlen);
|
memcpy(&prefix, fuzzer_input(prefixlen), prefixlen);
|
||||||
// force null termination
|
// force null termination
|
||||||
@ -92,6 +104,8 @@ int fuzz_bn_format() {
|
|||||||
}
|
}
|
||||||
memcpy(&suffixlen, fuzzer_input(1), 1);
|
memcpy(&suffixlen, fuzzer_input(1), 1);
|
||||||
char suffix[suffixlen];
|
char suffix[suffixlen];
|
||||||
|
memset(&suffix, 0, suffixlen);
|
||||||
|
|
||||||
if (suffixlen > 0 && suffixlen <= 128 && suffixlen <= fuzzer_length) {
|
if (suffixlen > 0 && suffixlen <= 128 && suffixlen <= fuzzer_length) {
|
||||||
memcpy(&suffix, fuzzer_input(suffixlen), suffixlen);
|
memcpy(&suffix, fuzzer_input(suffixlen), suffixlen);
|
||||||
// force null termination
|
// force null termination
|
||||||
@ -125,7 +139,7 @@ extern uint8_t *base32_decode(const char *in, size_t inlen, uint8_t *out,
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define BASE32_DECODE_MAX_INPUT_LEN 512
|
#define BASE32_DECODE_MAX_INPUT_LEN 512
|
||||||
|
|
||||||
int fuzz_base32_decode() {
|
int fuzz_base32_decode(void) {
|
||||||
if (fuzzer_length < 2 || fuzzer_length > BASE32_DECODE_MAX_INPUT_LEN) {
|
if (fuzzer_length < 2 || fuzzer_length > BASE32_DECODE_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -153,7 +167,7 @@ extern char *base32_encode(const uint8_t *in, size_t inlen, char *out,
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define BASE32_ENCODE_MAX_INPUT_LEN 512
|
#define BASE32_ENCODE_MAX_INPUT_LEN 512
|
||||||
|
|
||||||
int fuzz_base32_encode() {
|
int fuzz_base32_encode(void) {
|
||||||
if (fuzzer_length > BASE32_ENCODE_MAX_INPUT_LEN) {
|
if (fuzzer_length > BASE32_ENCODE_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -178,7 +192,7 @@ extern int base58_encode_check(const uint8_t *data, int datalen,
|
|||||||
// internal limit is 128, try some extra bytes
|
// internal limit is 128, try some extra bytes
|
||||||
#define BASE58_ENCODE_MAX_INPUT_LEN 140
|
#define BASE58_ENCODE_MAX_INPUT_LEN 140
|
||||||
|
|
||||||
int fuzz_base58_encode_check() {
|
int fuzz_base58_encode_check(void) {
|
||||||
if (fuzzer_length > BASE58_ENCODE_MAX_INPUT_LEN) {
|
if (fuzzer_length > BASE58_ENCODE_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -207,7 +221,7 @@ extern int base58_decode_check(const char *str, HasherType hasher_type,
|
|||||||
// internal limit is 128, try some extra bytes
|
// internal limit is 128, try some extra bytes
|
||||||
#define BASE58_DECODE_MAX_INPUT_LEN 140
|
#define BASE58_DECODE_MAX_INPUT_LEN 140
|
||||||
|
|
||||||
int fuzz_base58_decode_check() {
|
int fuzz_base58_decode_check(void) {
|
||||||
if (fuzzer_length > BASE58_DECODE_MAX_INPUT_LEN) {
|
if (fuzzer_length > BASE58_DECODE_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -240,7 +254,7 @@ extern int xmr_base58_addr_decode_check(const char *addr, size_t sz,
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define XMR_BASE58_ADDR_DECODE_MAX_INPUT_LEN 512
|
#define XMR_BASE58_ADDR_DECODE_MAX_INPUT_LEN 512
|
||||||
|
|
||||||
int fuzz_xmr_base58_addr_decode_check() {
|
int fuzz_xmr_base58_addr_decode_check(void) {
|
||||||
if (fuzzer_length > XMR_BASE58_ADDR_DECODE_MAX_INPUT_LEN) {
|
if (fuzzer_length > XMR_BASE58_ADDR_DECODE_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -265,7 +279,7 @@ extern int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data,
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define XMR_BASE58_ADDR_ENCODE_MAX_INPUT_LEN 512
|
#define XMR_BASE58_ADDR_ENCODE_MAX_INPUT_LEN 512
|
||||||
|
|
||||||
int fuzz_xmr_base58_addr_encode_check() {
|
int fuzz_xmr_base58_addr_encode_check(void) {
|
||||||
uint64_t tag_in;
|
uint64_t tag_in;
|
||||||
size_t tag_size = sizeof(tag_in);
|
size_t tag_size = sizeof(tag_in);
|
||||||
if (fuzzer_length < tag_size ||
|
if (fuzzer_length < tag_size ||
|
||||||
@ -298,7 +312,7 @@ extern int xmr_read_varint(uint8_t *buff, size_t buff_size, uint64_t *val);
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define XMR_SERIALIZE_VARINT_MAX_INPUT_LEN 128
|
#define XMR_SERIALIZE_VARINT_MAX_INPUT_LEN 128
|
||||||
|
|
||||||
int fuzz_xmr_serialize_varint() {
|
int fuzz_xmr_serialize_varint(void) {
|
||||||
uint64_t varint_in;
|
uint64_t varint_in;
|
||||||
size_t varint_in_size = sizeof(varint_in);
|
size_t varint_in_size = sizeof(varint_in);
|
||||||
if (fuzzer_length < varint_in_size ||
|
if (fuzzer_length < varint_in_size ||
|
||||||
@ -333,7 +347,7 @@ extern bool nem_validate_address(const char *address, uint8_t network);
|
|||||||
// arbitrarily chosen maximum size
|
// arbitrarily chosen maximum size
|
||||||
#define NEM_VALIDATE_ADDRESS_MAX_INPUT_LEN 128
|
#define NEM_VALIDATE_ADDRESS_MAX_INPUT_LEN 128
|
||||||
|
|
||||||
int fuzz_nem_validate_address() {
|
int fuzz_nem_validate_address(void) {
|
||||||
if (fuzzer_length < (1 + 1) ||
|
if (fuzzer_length < (1 + 1) ||
|
||||||
fuzzer_length > NEM_VALIDATE_ADDRESS_MAX_INPUT_LEN) {
|
fuzzer_length > NEM_VALIDATE_ADDRESS_MAX_INPUT_LEN) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -358,6 +372,339 @@ int fuzz_nem_validate_address() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern bool nem_get_address(const ed25519_public_key public_key,
|
||||||
|
uint8_t version, char *address);
|
||||||
|
|
||||||
|
int fuzz_nem_get_address(void) {
|
||||||
|
unsigned char ed25519_public_key[32] = {0};
|
||||||
|
uint32_t network = 0;
|
||||||
|
|
||||||
|
if (fuzzer_length != (sizeof(ed25519_public_key) + sizeof(network))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char address[NEM_ADDRESS_SIZE + 1] = {0};
|
||||||
|
|
||||||
|
memcpy(ed25519_public_key, fuzzer_input(32), 32);
|
||||||
|
memcpy(&network, fuzzer_input(4), 4);
|
||||||
|
|
||||||
|
nem_get_address(ed25519_public_key, network, address);
|
||||||
|
|
||||||
|
// TODO check return address for memory info leakage?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void xmr_get_subaddress_secret_key(bignum256modm r, uint32_t major,
|
||||||
|
uint32_t minor,
|
||||||
|
const bignum256modm m);
|
||||||
|
|
||||||
|
int fuzz_xmr_get_subaddress_secret_key(void) {
|
||||||
|
bignum256modm m = {0};
|
||||||
|
uint32_t major = 0;
|
||||||
|
uint32_t minor = 0;
|
||||||
|
if (fuzzer_length != (sizeof(bignum256modm) + 2 * sizeof(uint32_t))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bignum256modm output = {0};
|
||||||
|
|
||||||
|
memcpy(m, fuzzer_input(sizeof(bignum256modm)), sizeof(bignum256modm));
|
||||||
|
memcpy(&major, fuzzer_input(sizeof(uint32_t)), sizeof(uint32_t));
|
||||||
|
memcpy(&minor, fuzzer_input(sizeof(uint32_t)), sizeof(uint32_t));
|
||||||
|
|
||||||
|
xmr_get_subaddress_secret_key(output, major, minor, m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void xmr_derive_private_key(bignum256modm s, const ge25519 *deriv,
|
||||||
|
uint32_t idx, const bignum256modm base);
|
||||||
|
|
||||||
|
int fuzz_xmr_derive_private_key(void) {
|
||||||
|
bignum256modm base = {0};
|
||||||
|
ge25519 deriv = {0};
|
||||||
|
uint32_t idx = 0;
|
||||||
|
|
||||||
|
if (fuzzer_length !=
|
||||||
|
(sizeof(bignum256modm) + sizeof(ge25519) + sizeof(uint32_t))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(base, fuzzer_input(sizeof(bignum256modm)), sizeof(bignum256modm));
|
||||||
|
memcpy(&deriv, fuzzer_input(sizeof(ge25519)), sizeof(ge25519));
|
||||||
|
memcpy(&idx, fuzzer_input(sizeof(uint32_t)), sizeof(uint32_t));
|
||||||
|
|
||||||
|
bignum256modm output = {0};
|
||||||
|
|
||||||
|
xmr_derive_private_key(output, &deriv, idx, base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void xmr_derive_public_key(ge25519 *r, const ge25519 *deriv,
|
||||||
|
uint32_t idx, const ge25519 *base);
|
||||||
|
|
||||||
|
int fuzz_xmr_derive_public_key(void) {
|
||||||
|
ge25519 base = {0};
|
||||||
|
ge25519 deriv = {0};
|
||||||
|
uint32_t idx = 0;
|
||||||
|
|
||||||
|
if (fuzzer_length != (2 * sizeof(ge25519) + sizeof(uint32_t))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&base, fuzzer_input(sizeof(ge25519)), sizeof(ge25519));
|
||||||
|
memcpy(&deriv, fuzzer_input(sizeof(ge25519)), sizeof(ge25519));
|
||||||
|
memcpy(&idx, fuzzer_input(sizeof(uint32_t)), sizeof(uint32_t));
|
||||||
|
|
||||||
|
ge25519 output = {0};
|
||||||
|
|
||||||
|
xmr_derive_public_key(&output, &deriv, idx, &base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern bool shamir_interpolate(uint8_t *result, uint8_t result_index,
|
||||||
|
const uint8_t *share_indices,
|
||||||
|
const uint8_t **share_values,
|
||||||
|
uint8_t share_count, size_t len);
|
||||||
|
|
||||||
|
#define SHAMIR_MAX_SHARE_COUNT 16
|
||||||
|
#define SHAMIR_MAX_DATA_LEN (SHAMIR_MAX_SHARE_COUNT * SHAMIR_MAX_LEN)
|
||||||
|
int fuzz_shamir_interpolate(void) {
|
||||||
|
if (fuzzer_length != (2 * sizeof(uint8_t) + SHAMIR_MAX_SHARE_COUNT +
|
||||||
|
SHAMIR_MAX_DATA_LEN + 2)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t result[SHAMIR_MAX_LEN] = {0};
|
||||||
|
uint8_t result_index = 0;
|
||||||
|
uint8_t share_indices[SHAMIR_MAX_SHARE_COUNT] = {0};
|
||||||
|
uint8_t share_values_content[SHAMIR_MAX_SHARE_COUNT][SHAMIR_MAX_LEN] = {0};
|
||||||
|
const uint8_t *share_values[SHAMIR_MAX_SHARE_COUNT] = {0};
|
||||||
|
uint8_t share_count = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < SHAMIR_MAX_SHARE_COUNT; i++) {
|
||||||
|
share_values[i] = share_values_content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&result_index, fuzzer_input(sizeof(uint8_t)), sizeof(uint8_t));
|
||||||
|
memcpy(&share_indices, fuzzer_input(SHAMIR_MAX_SHARE_COUNT),
|
||||||
|
SHAMIR_MAX_SHARE_COUNT);
|
||||||
|
memcpy(&share_values_content, fuzzer_input(SHAMIR_MAX_DATA_LEN),
|
||||||
|
SHAMIR_MAX_DATA_LEN);
|
||||||
|
memcpy(&share_count, fuzzer_input(sizeof(uint8_t)), sizeof(uint8_t));
|
||||||
|
|
||||||
|
// the target function checks for (len > SHAMIR_MAX_LEN),
|
||||||
|
// so we don't have to test the whole size_t length value
|
||||||
|
len = (fuzzer_ptr[0] << 8) + fuzzer_ptr[1];
|
||||||
|
fuzzer_input(2);
|
||||||
|
|
||||||
|
// mirror the checks in mod_trezorcrypto_shamir_interpolate()
|
||||||
|
if (share_count < 1 || share_count > SHAMIR_MAX_SHARE_COUNT) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
shamir_interpolate(result, result_index, share_indices, share_values,
|
||||||
|
share_count, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key,
|
||||||
|
const uint8_t *sig, const uint8_t *digest);
|
||||||
|
|
||||||
|
int fuzz_ecdsa_verify_digest(void) {
|
||||||
|
uint8_t curve_decider = 0;
|
||||||
|
uint8_t hash[32] = {0};
|
||||||
|
uint8_t sig[64] = {0};
|
||||||
|
uint8_t pub_key[65] = {0};
|
||||||
|
|
||||||
|
if (fuzzer_length < 1 + sizeof(hash) + sizeof(sig) + sizeof(pub_key)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&curve_decider, fuzzer_input(1), 1);
|
||||||
|
memcpy(&hash, fuzzer_input(sizeof(hash)), sizeof(hash));
|
||||||
|
memcpy(&sig, fuzzer_input(sizeof(sig)), sizeof(sig));
|
||||||
|
memcpy(&pub_key, fuzzer_input(sizeof(pub_key)), sizeof(pub_key));
|
||||||
|
|
||||||
|
const ecdsa_curve *curve;
|
||||||
|
// pick one of the standard curves
|
||||||
|
if ((curve_decider & 0x1) == 1) {
|
||||||
|
curve = &secp256k1;
|
||||||
|
} else {
|
||||||
|
curve = &nist256p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = ecdsa_verify_digest(curve, (const uint8_t *)&pub_key,
|
||||||
|
(const uint8_t *)&sig, (const uint8_t *)&hash);
|
||||||
|
|
||||||
|
// TODO check if the fuzzer ever manages to get the return value to 0
|
||||||
|
(void)res;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern bool word_index(uint16_t *index, const char *word, uint8_t word_length);
|
||||||
|
|
||||||
|
int fuzz_word_index(void) {
|
||||||
|
#define MAX_WORD_LENGTH 12
|
||||||
|
|
||||||
|
// TODO exact match?
|
||||||
|
if (fuzzer_length < MAX_WORD_LENGTH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char word[MAX_WORD_LENGTH + 1] = {0};
|
||||||
|
memcpy(&word, fuzzer_ptr, MAX_WORD_LENGTH);
|
||||||
|
size_t word_length = strlen(word);
|
||||||
|
|
||||||
|
uint16_t index = 0;
|
||||||
|
|
||||||
|
word_index(&index, (const char *)&word, word_length);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint16_t slip39_word_completion_mask(uint16_t sequence);
|
||||||
|
|
||||||
|
int fuzz_slip39_word_completion_mask(void) {
|
||||||
|
if (fuzzer_length != 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint16_t sequence = (fuzzer_ptr[0] << 8) + fuzzer_ptr[1];
|
||||||
|
fuzzer_input(2);
|
||||||
|
|
||||||
|
// TODO perform additional checks on the output?
|
||||||
|
slip39_word_completion_mask(sequence);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int mnemonic_to_bits(const char *mnemonic, uint8_t *mnemonic_bits);
|
||||||
|
|
||||||
|
int fuzz_mnemonic_to_bits(void) {
|
||||||
|
// slightly longer than MAX_MNEMONIC_LEN from config.h
|
||||||
|
#define MAX_MNEMONIC_LENGTH 256
|
||||||
|
|
||||||
|
if (fuzzer_length < MAX_MNEMONIC_LENGTH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char mnemonic[MAX_MNEMONIC_LENGTH + 1] = {0};
|
||||||
|
memcpy(&mnemonic, fuzzer_ptr, MAX_MNEMONIC_LENGTH);
|
||||||
|
uint8_t mnemonic_bits[32 + 1] = {0};
|
||||||
|
|
||||||
|
int number_of_bits = mnemonic_to_bits((const char *)&mnemonic, mnemonic_bits);
|
||||||
|
assert(0 <= number_of_bits && number_of_bits <= 264);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fuzz_aes(void) {
|
||||||
|
if (fuzzer_length < 1 + 16 + 16 + 32) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
aes_encrypt_ctx ctxe;
|
||||||
|
aes_decrypt_ctx ctxd;
|
||||||
|
uint8_t ibuf[16] = {0};
|
||||||
|
uint8_t obuf[16] = {0};
|
||||||
|
uint8_t iv[16] = {0};
|
||||||
|
uint8_t cbuf[16] = {0};
|
||||||
|
|
||||||
|
const uint8_t *keylength_decider = fuzzer_input(1);
|
||||||
|
|
||||||
|
// note: the unit test uses the fixed 32 byte key
|
||||||
|
// 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
|
||||||
|
uint8_t keybuf[32] = {0};
|
||||||
|
memcpy(&keybuf, fuzzer_input(32), 32);
|
||||||
|
|
||||||
|
#ifdef AES_VAR
|
||||||
|
// try 128, 192, 256 bit key lengths
|
||||||
|
|
||||||
|
size_t keylength = 32;
|
||||||
|
switch (keylength_decider[0] & 0x3) {
|
||||||
|
case 0:
|
||||||
|
// invalid length
|
||||||
|
keylength = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
keylength = 16;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
keylength = 24;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
keylength = 32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aes_encrypt_key((const unsigned char *)&keybuf, keylength, &ctxe) ||
|
||||||
|
aes_decrypt_key((const unsigned char *)&keybuf, keylength, &ctxd)) {
|
||||||
|
// initialization problems, stop processing
|
||||||
|
// we expect this to happen with the invalid key length
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// use a 256 bit key length
|
||||||
|
(void)keylength_decider;
|
||||||
|
aes_encrypt_key256((const unsigned char *)&keybuf, &ctxe);
|
||||||
|
aes_decrypt_key256((const unsigned char *)&keybuf, &ctxd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(ibuf, fuzzer_input(16), 16);
|
||||||
|
memcpy(iv, fuzzer_input(16), 16);
|
||||||
|
|
||||||
|
aes_ecb_encrypt(ibuf, obuf, 16, &ctxe);
|
||||||
|
aes_ecb_decrypt(ibuf, obuf, 16, &ctxd);
|
||||||
|
|
||||||
|
aes_cbc_encrypt(ibuf, obuf, 16, iv, &ctxe);
|
||||||
|
aes_cbc_decrypt(ibuf, obuf, 16, iv, &ctxd);
|
||||||
|
|
||||||
|
aes_cfb_encrypt(ibuf, obuf, 16, iv, &ctxe);
|
||||||
|
aes_cfb_decrypt(ibuf, obuf, 16, iv, &ctxe);
|
||||||
|
|
||||||
|
aes_ofb_encrypt(ibuf, obuf, 16, iv, &ctxe);
|
||||||
|
aes_ofb_decrypt(ibuf, obuf, 16, iv, &ctxe);
|
||||||
|
|
||||||
|
aes_ctr_encrypt(ibuf, obuf, 16, cbuf, aes_ctr_cbuf_inc, &ctxe);
|
||||||
|
aes_ctr_decrypt(ibuf, obuf, 16, cbuf, aes_ctr_cbuf_inc, &ctxe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int base58gph_encode_check(const uint8_t *data, int datalen, char *str,
|
||||||
|
int strsize);
|
||||||
|
|
||||||
|
extern int base58gph_decode_check(const char *str, uint8_t *data, int datalen);
|
||||||
|
|
||||||
|
int fuzz_b58gph_encode_decode(void) {
|
||||||
|
// note: encode and decode have an internal limit of 128
|
||||||
|
#define BASE58_GPH_MAX_INPUT_LEN 140
|
||||||
|
|
||||||
|
if (fuzzer_length > BASE58_GPH_MAX_INPUT_LEN) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t encode_in_buffer[BASE58_GPH_MAX_INPUT_LEN] = {0};
|
||||||
|
// with null termination
|
||||||
|
char decode_in_buffer[BASE58_GPH_MAX_INPUT_LEN + 1] = {0};
|
||||||
|
char out_buffer[BASE58_GPH_MAX_INPUT_LEN] = {0};
|
||||||
|
size_t outlen = sizeof(out_buffer);
|
||||||
|
|
||||||
|
size_t raw_inlen = fuzzer_length;
|
||||||
|
memcpy(&encode_in_buffer, fuzzer_input(raw_inlen), raw_inlen);
|
||||||
|
memcpy(&decode_in_buffer, &encode_in_buffer, raw_inlen);
|
||||||
|
|
||||||
|
base58gph_encode_check(encode_in_buffer, raw_inlen, out_buffer, outlen);
|
||||||
|
base58gph_decode_check(decode_in_buffer, (uint8_t *)&out_buffer, outlen);
|
||||||
|
|
||||||
|
// TODO do logical encode<>decode comparison checks?
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define META_HEADER_SIZE 3
|
#define META_HEADER_SIZE 3
|
||||||
|
|
||||||
// main fuzzer entry
|
// main fuzzer entry
|
||||||
@ -369,9 +716,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
|
|
||||||
fuzzer_reset_state();
|
fuzzer_reset_state();
|
||||||
|
|
||||||
uint8_t decision = data[0];
|
uint8_t target_decision = data[0];
|
||||||
|
|
||||||
// TODO use when necessary
|
// TODO use once necessary
|
||||||
// uint8_t subdecision = data[1];
|
// uint8_t subdecision = data[1];
|
||||||
|
|
||||||
// note: data[2] is reserved for future use
|
// note: data[2] is reserved for future use
|
||||||
@ -380,7 +727,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
fuzzer_ptr = data + META_HEADER_SIZE;
|
fuzzer_ptr = data + META_HEADER_SIZE;
|
||||||
fuzzer_length = size - META_HEADER_SIZE;
|
fuzzer_length = size - META_HEADER_SIZE;
|
||||||
|
|
||||||
switch (decision) {
|
// if active: reject all other inputs that are not the selected target
|
||||||
|
// this is helpful for directing the fuzzing focus on a specific case
|
||||||
|
#ifdef FUZZER_EXCLUSIVE_TARGET
|
||||||
|
if (target_decision != FUZZER_EXCLUSIVE_TARGET) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO reorder and regroup target functions
|
||||||
|
switch (target_decision) {
|
||||||
case 0:
|
case 0:
|
||||||
fuzz_bn_format();
|
fuzz_bn_format();
|
||||||
break;
|
break;
|
||||||
@ -408,6 +764,45 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
case 8:
|
case 8:
|
||||||
fuzz_nem_validate_address();
|
fuzz_nem_validate_address();
|
||||||
break;
|
break;
|
||||||
|
case 9:
|
||||||
|
fuzz_nem_get_address();
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
fuzz_xmr_get_subaddress_secret_key();
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
fuzz_xmr_derive_private_key();
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
fuzz_xmr_derive_public_key();
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
fuzz_shamir_interpolate();
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
#ifdef FUZZ_ALLOW_SLOW
|
||||||
|
// slow through expensive bignum operations
|
||||||
|
fuzz_ecdsa_verify_digest();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
fuzz_word_index();
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
fuzz_slip39_word_completion_mask();
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
fuzz_mnemonic_to_bits();
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
#ifdef FUZZ_ALLOW_SLOW
|
||||||
|
fuzz_aes();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
fuzz_b58gph_encode_decode();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user