Skip to content

Commit 766dd7e

Browse files
committed
Iterate on the fuzzers to give them more "bite"
1 parent 0702ef0 commit 766dd7e

File tree

10 files changed

+449
-55
lines changed

10 files changed

+449
-55
lines changed

fuzz/Cargo.lock

Lines changed: 129 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fuzz/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,19 @@ path = "fuzz_targets/cross_algorithm_fuzz.rs"
3636
test = false
3737
doc = false
3838
bench = false
39+
40+
# New target for key parsing
41+
[[bin]]
42+
name = "key_parsing"
43+
path = "fuzz_targets/key_parsing_fuzz.rs"
44+
test = false
45+
doc = false
46+
bench = false
47+
48+
# New target for signature parsing
49+
[[bin]]
50+
name = "signature_parsing"
51+
path = "fuzz_targets/signature_parsing_fuzz.rs"
52+
test = false
53+
doc = false
54+
bench = false

fuzz/README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,50 @@ cargo install cargo-fuzz
1212

1313
## Available Fuzz Targets
1414

15-
1. **keypair_generation** - Tests key pair generation with different algorithms
16-
2. **sign_verify** - Tests signature creation and verification
17-
3. **cross_algorithm** - Tests verification with mismatched keys and signatures from different algorithms
15+
1. **`keypair_generation`** - Tests key pair generation with different algorithms using fuzzed randomness.
16+
2. **`sign_verify`** - Tests signature creation and verification using generated keys and fuzzed messages.
17+
3. **`cross_algorithm`** - Tests verification with mismatched keys and signatures from different algorithms.
18+
4. **`key_parsing`** - Tests parsing of arbitrary byte sequences into `PublicKey` and `SecretKey` structs across algorithms.
19+
5. **`signature_parsing`** - Tests parsing of arbitrary byte sequences into `Signature` structs across algorithms.
1820

1921
## Running the Fuzz Tests
2022

2123
To run a specific fuzz target:
2224

23-
```
25+
```bash
2426
cargo fuzz run keypair_generation
2527
cargo fuzz run sign_verify
2628
cargo fuzz run cross_algorithm
29+
cargo fuzz run key_parsing
30+
cargo fuzz run signature_parsing
2731
```
2832

2933
To run a fuzz target for a specific amount of time:
3034

31-
```
35+
```bash
3236
cargo fuzz run keypair_generation -- -max_total_time=60
3337
```
3438

3539
To run a fuzz target with a specific number of iterations:
3640

37-
```
41+
```bash
3842
cargo fuzz run keypair_generation -- -runs=1000000
3943
```
4044

45+
To run **all** fuzz targets sequentially, use the provided script (make sure it's executable: `chmod +x fuzz/run_all_fuzzers.sh`):
46+
47+
```bash
48+
./fuzz/run_all_fuzzers.sh
49+
```
50+
51+
This script will iterate through all defined targets **in parallel** using **GNU Parallel**.
52+
If you don't have GNU Parallel installed, the script will output an error. You can install it using your system's package manager:
53+
54+
- **Debian/Ubuntu:** `sudo apt update && sudo apt install parallel`
55+
- **Fedora:** `sudo dnf install parallel`
56+
- **Arch Linux:** `sudo pacman -S parallel`
57+
- **macOS (Homebrew):** `brew install parallel`
58+
4159
## Corpus Management
4260

4361
Cargo-fuzz automatically manages a corpus of interesting inputs. You can find them in the `fuzz/corpus` directory once you've run the fuzz tests.
@@ -51,6 +69,7 @@ cargo fuzz run target_name fuzz/artifacts/target_name/crash-*
5169
```
5270

5371
When reporting an issue found by fuzzing, please include:
72+
5473
1. The exact command used to run the fuzzer
5574
2. The crash input file
5675
3. The full output of the crash
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![no_main]
2+
3+
use bitcoinpqc::{algorithm_from_index, PublicKey, SecretKey};
4+
use libfuzzer_sys::fuzz_target;
5+
6+
const NUM_ALGORITHMS: u8 = 4; // SECP256K1_SCHNORR, FN_DSA_512, ML_DSA_44, SLH_DSA_128S
7+
8+
fuzz_target!(|data: &[u8]| {
9+
if data.is_empty() {
10+
return; // Need at least one byte for algorithm selection
11+
}
12+
13+
// Use first byte to select an algorithm
14+
let alg_byte = data[0];
15+
let algorithm = algorithm_from_index(alg_byte);
16+
17+
// Use remaining bytes as potential key data
18+
let key_data = &data[1..];
19+
20+
// Attempt to parse as PublicKey
21+
let _ = PublicKey::try_from_slice(algorithm, key_data);
22+
23+
// Attempt to parse as SecretKey
24+
let secret_key_result = SecretKey::try_from_slice(algorithm, key_data);
25+
26+
assert!(
27+
secret_key_result.is_ok(),
28+
"Secret key parsing failed! Algorithm: {algorithm:?}",
29+
);
30+
});
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![no_main]
22

3-
use bitcoinpqc::{generate_keypair, Algorithm};
3+
use bitcoinpqc::{algorithm_from_index, generate_keypair};
44
use libfuzzer_sys::fuzz_target;
55

66
fuzz_target!(|data: &[u8]| {
@@ -11,16 +11,16 @@ fuzz_target!(|data: &[u8]| {
1111

1212
// Use first byte to select an algorithm
1313
let alg_byte = data[0];
14-
let algorithm = match alg_byte % 4 {
15-
0 => Algorithm::SECP256K1_SCHNORR,
16-
1 => Algorithm::FN_DSA_512,
17-
2 => Algorithm::ML_DSA_44,
18-
_ => Algorithm::SLH_DSA_128S,
19-
};
14+
let algorithm = algorithm_from_index(alg_byte);
2015

2116
// Use remaining bytes as random data
2217
let random_data = &data[1..];
2318

2419
// Try to generate a keypair with this data
25-
let _ = generate_keypair(algorithm, random_data);
20+
let keypair = generate_keypair(algorithm, random_data);
21+
22+
assert!(
23+
keypair.is_ok(),
24+
"Keypair generation failed! Algorithm: {algorithm:?}",
25+
);
2626
});

fuzz/fuzz_targets/sign_verify_fuzz.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![no_main]
22

3-
use bitcoinpqc::{generate_keypair, sign, verify, Algorithm};
3+
use bitcoinpqc::{algorithm_from_index, generate_keypair, sign, verify};
44
use libfuzzer_sys::fuzz_target;
55

66
fuzz_target!(|data: &[u8]| {
@@ -11,12 +11,7 @@ fuzz_target!(|data: &[u8]| {
1111

1212
// Use first byte to select an algorithm
1313
let alg_byte = data[0];
14-
let algorithm = match alg_byte % 4 {
15-
0 => Algorithm::SECP256K1_SCHNORR,
16-
1 => Algorithm::FN_DSA_512,
17-
2 => Algorithm::ML_DSA_44,
18-
_ => Algorithm::SLH_DSA_128S,
19-
};
14+
let algorithm = algorithm_from_index(alg_byte);
2015

2116
// Use 128 bytes for key generation
2217
let key_data = &data[1..129];
@@ -37,7 +32,12 @@ fuzz_target!(|data: &[u8]| {
3732
};
3833

3934
// Try to verify the signature with the correct public key
40-
let _verify_result = verify(&keypair.public_key, message, &signature);
35+
let verify_result = verify(&keypair.public_key, message, &signature);
36+
37+
assert!(
38+
verify_result.is_ok(),
39+
"Verification failed for a signature generated with the corresponding private key! Algorithm: {algorithm:?}",
40+
);
4141

4242
// Also try some invalid cases (if we have a valid signature)
4343
if message.len() > 1 {

0 commit comments

Comments
 (0)