Skip to content

Commit f73fe9a

Browse files
feat(fuzz): Add missing fuzzing targets.
1 parent cdce330 commit f73fe9a

30 files changed

+900
-91
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ jobs:
145145

146146
tests_nuget_ios:
147147
needs: [nugets, setup_config]
148-
runs-on: "macos-15"
149-
148+
runs-on: "macos-26"
150149
steps:
151150
- uses: actions/checkout@v4
152151
with:
@@ -230,6 +229,13 @@ jobs:
230229
- uses: actions/checkout@v4
231230
- uses: ./.github/workflows/formatting/rust
232231

232+
#### FUZZ TESTING ####
233+
quick_fuzz:
234+
runs-on: "ubuntu-22.04"
235+
steps:
236+
- uses: actions/checkout@v4
237+
- uses: ./.github/workflows/tests/fuzz/quick
238+
233239
csharp_code_format:
234240
needs: setup_config
235241
runs-on: "windows-2022"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Extended Fuzzing
2+
3+
on:
4+
schedule:
5+
# Run nightly at 2 AM UTC
6+
- cron: '0 2 * * *'
7+
workflow_dispatch:
8+
inputs:
9+
duration:
10+
description: 'Duration per target in seconds'
11+
required: false
12+
default: '300'
13+
type: string
14+
15+
jobs:
16+
extended_fuzz:
17+
runs-on: ubuntu-22.04
18+
timeout-minutes: 300 # 5 hours max
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Install Rust nightly
23+
run: |
24+
rustup toolchain install nightly
25+
rustup default nightly
26+
27+
- name: Install cargo-fuzz
28+
run: cargo install cargo-fuzz
29+
30+
- name: Restore corpus cache
31+
uses: actions/cache@v4
32+
with:
33+
path: fuzz/corpus
34+
key: fuzz-corpus-${{ github.sha }}
35+
restore-keys: |
36+
fuzz-corpus-
37+
38+
- name: Run extended fuzz tests
39+
working-directory: ./fuzz
40+
run: |
41+
DURATION=${{ inputs.duration || '300' }}
42+
./run_all_fuzz_tests.sh $DURATION
43+
44+
- name: Save corpus cache
45+
if: always()
46+
uses: actions/cache/save@v4
47+
with:
48+
path: fuzz/corpus
49+
key: fuzz-corpus-${{ github.sha }}
50+
51+
- name: Upload crash artifacts
52+
if: failure()
53+
uses: actions/upload-artifact@v4.3.6
54+
with:
55+
name: fuzz-crash-artifacts-extended-${{ github.run_id }}
56+
path: fuzz/artifacts/
57+
if-no-files-found: ignore
58+
59+
- name: Upload corpus on failure
60+
if: failure()
61+
uses: actions/upload-artifact@v4.3.6
62+
with:
63+
name: fuzz-corpus-${{ github.run_id }}
64+
path: fuzz/corpus/
65+
if-no-files-found: ignore

.github/workflows/tests/csharp/android/action.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,32 @@ runs:
4848
shell: bash
4949
run: echo "y" | sdkmanager --install "system-images;android-33;google_apis;x86_64"
5050

51+
- name: Clean up AVD cache
52+
shell: bash
53+
run: |
54+
rm -rf ~/.android/avd/
55+
rm -rf ~/.android/cache
56+
57+
- name: Free up disk space
58+
shell: bash
59+
run: |
60+
echo "Disk space before cleanup:"
61+
df -h
62+
63+
# Remove large unused packages (but keep dotnet - we need it!)
64+
sudo rm -rf /opt/ghc
65+
sudo rm -rf /usr/local/share/boost
66+
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
67+
68+
# Clean docker
69+
sudo docker image prune --all --force
70+
71+
echo "Disk space after cleanup:"
72+
df -h
73+
5174
- name: Creating Android device
5275
shell: bash
53-
run: echo "no" | avdmanager create avd -n test_emulator -k "system-images;android-33;google_apis;x86_64"
76+
run: echo "no" | avdmanager create avd -n test_emulator -k "system-images;android-33;google_apis;x86_64" -c 2048M
5477

5578
- name: Starting emulator
5679
shell: bash

.github/workflows/tests/csharp/ios/action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ runs:
2727
- name: Extract UDID
2828
shell: bash
2929
run: |
30-
# Find the UDID of the iPhone 16 simulator running iOS 18.0
31-
SIMULATOR_UDID=$(xcrun simctl list devices available 'iOS 18.0' | grep 'iPhone 16' | awk -F '[()]' '{print $2}' | head -n 1)
30+
# Find the UDID of the iPhone 16 simulator running iOS 18
31+
SIMULATOR_UDID=$(xcrun simctl list devices available 'iOS 18' | grep 'iPhone 16' | awk -F '[()]' '{print $2}' | head -n 1)
3232
3333
# Check if a UDID was found
3434
if [ -n "$SIMULATOR_UDID" ]; then
@@ -37,7 +37,7 @@ runs:
3737
echo "IPHONE_16_SIM_UDID=$SIMULATOR_UDID" >> $GITHUB_ENV
3838
echo "iPhone 16 UDID stored in environment variable IPHONE_16_SIM_UDID: $IPHONE_16_SIM_UDID"
3939
else
40-
echo "iPhone 16 simulator with iOS 18.0 not found."
40+
echo "iPhone 16 simulator with iOS 18 not found."
4141
exit 1
4242
fi
4343
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Quick Fuzz Test
2+
description: Run quick fuzz tests on all targets (10 seconds each)
3+
runs:
4+
using: composite
5+
steps:
6+
- name: Install Rust nightly
7+
shell: bash
8+
run: |
9+
rustup toolchain install nightly
10+
rustup default nightly
11+
12+
- name: Install cargo-fuzz
13+
shell: bash
14+
run: cargo install cargo-fuzz
15+
16+
- name: Run quick fuzz tests
17+
working-directory: ./fuzz
18+
shell: bash
19+
run: |
20+
set -e
21+
22+
echo "=== Running Quick Fuzz Tests (10s per target) ==="
23+
24+
# Get all fuzz targets
25+
TARGETS=$(cargo fuzz list)
26+
TOTAL=$(echo "$TARGETS" | wc -l)
27+
CURRENT=0
28+
FAILED_TARGETS=()
29+
30+
for target in $TARGETS; do
31+
CURRENT=$((CURRENT + 1))
32+
echo ""
33+
echo "[$CURRENT/$TOTAL] Testing: $target"
34+
35+
if timeout 15s cargo fuzz run "$target" -- -max_total_time=10 -rss_limit_mb=2048 2>&1; then
36+
echo "✓ $target passed"
37+
else
38+
EXIT_CODE=$?
39+
if [ $EXIT_CODE -eq 124 ]; then
40+
echo "⏱ $target timed out (expected)"
41+
else
42+
echo "✗ $target failed with exit code $EXIT_CODE"
43+
FAILED_TARGETS+=("$target")
44+
fi
45+
fi
46+
done
47+
48+
echo ""
49+
echo "=== Summary ==="
50+
echo "Total targets: $TOTAL"
51+
echo "Failed targets: ${#FAILED_TARGETS[@]}"
52+
53+
if [ ${#FAILED_TARGETS[@]} -gt 0 ]; then
54+
echo ""
55+
echo "Failed targets:"
56+
printf '%s\n' "${FAILED_TARGETS[@]}"
57+
exit 1
58+
fi
59+
60+
echo ""
61+
echo "All fuzz tests passed!"
62+
63+
- name: Upload crash artifacts
64+
if: failure()
65+
uses: actions/upload-artifact@v4.3.6
66+
with:
67+
name: fuzz-crash-artifacts
68+
path: fuzz/artifacts/
69+
if-no-files-found: ignore

fuzz/Cargo.toml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ path = "fuzz_targets/key/private_key_deserialization.rs"
6161
name = "argon2parameters_deserialization"
6262
path = "fuzz_targets/key/argon2parameters_deserialization.rs"
6363

64+
[[bin]]
65+
name = "mix_key_exchange"
66+
path = "fuzz_targets/key/mix_key_exchange.rs"
67+
6468
[[bin]]
6569
name = "share_deserialization"
6670
path = "fuzz_targets/secret_sharing/share_deserialization.rs"
@@ -100,3 +104,75 @@ path = "fuzz_targets/utils/base64_decode.rs"
100104
[[bin]]
101105
name = "base64_decode_url"
102106
path = "fuzz_targets/utils/base64_decode_url.rs"
107+
108+
[[bin]]
109+
name = "derive_key_argon2"
110+
path = "fuzz_targets/utils/derive_key_argon2.rs"
111+
112+
[[bin]]
113+
name = "scrypt_simple"
114+
path = "fuzz_targets/utils/scrypt_simple.rs"
115+
116+
[[bin]]
117+
name = "signature_deserialization"
118+
path = "fuzz_targets/signature/signature_deserialization.rs"
119+
120+
[[bin]]
121+
name = "sign"
122+
path = "fuzz_targets/signature/sign.rs"
123+
124+
[[bin]]
125+
name = "verify"
126+
path = "fuzz_targets/signature/verify.rs"
127+
128+
[[bin]]
129+
name = "signing_keypair_deserialization"
130+
path = "fuzz_targets/signing_key/signing_keypair_deserialization.rs"
131+
132+
[[bin]]
133+
name = "signing_public_key_deserialization"
134+
path = "fuzz_targets/signing_key/signing_public_key_deserialization.rs"
135+
136+
[[bin]]
137+
name = "online_ciphertext_header_deserialization"
138+
path = "fuzz_targets/online_ciphertext/header_deserialization.rs"
139+
140+
[[bin]]
141+
name = "online_encrypt_symmetric"
142+
path = "fuzz_targets/online_ciphertext/encrypt_symmetric.rs"
143+
144+
[[bin]]
145+
name = "online_encrypt_asymmetric"
146+
path = "fuzz_targets/online_ciphertext/encrypt_asymmetric.rs"
147+
148+
[[bin]]
149+
name = "online_decrypt_symmetric"
150+
path = "fuzz_targets/online_ciphertext/decrypt_symmetric.rs"
151+
152+
[[bin]]
153+
name = "online_decrypt_asymmetric"
154+
path = "fuzz_targets/online_ciphertext/decrypt_asymmetric.rs"
155+
156+
[[bin]]
157+
name = "encrypt_with_aad"
158+
path = "fuzz_targets/ciphertext/encrypt_with_aad.rs"
159+
160+
[[bin]]
161+
name = "decrypt_with_aad"
162+
path = "fuzz_targets/ciphertext/decrypt_with_aad.rs"
163+
164+
[[bin]]
165+
name = "encrypt_asymmetric_with_aad"
166+
path = "fuzz_targets/ciphertext/encrypt_asymmetric_with_aad.rs"
167+
168+
[[bin]]
169+
name = "decrypt_asymmetric_with_aad"
170+
path = "fuzz_targets/ciphertext/decrypt_asymmetric_with_aad.rs"
171+
172+
[[bin]]
173+
name = "generate_keypair"
174+
path = "fuzz_targets/key/generate_keypair.rs"
175+
176+
[[bin]]
177+
name = "constant_time_equals"
178+
path = "fuzz_targets/utils/constant_time_equals.rs"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![no_main]
2+
use arbitrary::Arbitrary;
3+
use libfuzzer_sys::fuzz_target;
4+
5+
use devolutions_crypto::ciphertext::Ciphertext;
6+
use devolutions_crypto::key::PrivateKey;
7+
8+
#[derive(Arbitrary, Clone, Debug)]
9+
struct Input {
10+
data: Ciphertext,
11+
key: PrivateKey,
12+
aad: Vec<u8>,
13+
}
14+
15+
fuzz_target!(|data: Input| {
16+
let _ = data.data.decrypt_asymmetric_with_aad(&data.key, &data.aad);
17+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![no_main]
2+
use arbitrary::Arbitrary;
3+
use libfuzzer_sys::fuzz_target;
4+
5+
use devolutions_crypto::ciphertext::Ciphertext;
6+
7+
#[derive(Arbitrary, Clone, Debug)]
8+
struct Input {
9+
data: Ciphertext,
10+
key: Vec<u8>,
11+
aad: Vec<u8>,
12+
}
13+
14+
fuzz_target!(|data: Input| {
15+
let _ = data.data.decrypt_with_aad(&data.key, &data.aad);
16+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![no_main]
2+
use arbitrary::Arbitrary;
3+
use libfuzzer_sys::fuzz_target;
4+
5+
use devolutions_crypto::ciphertext::{encrypt_asymmetric_with_aad, CiphertextVersion};
6+
use devolutions_crypto::key::PublicKey;
7+
8+
#[derive(Arbitrary, Clone, Debug)]
9+
struct Input {
10+
data: Vec<u8>,
11+
key: PublicKey,
12+
aad: Vec<u8>,
13+
version: CiphertextVersion,
14+
}
15+
16+
fuzz_target!(|data: Input| {
17+
let _ = encrypt_asymmetric_with_aad(&data.data, &data.key, &data.aad, data.version);
18+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![no_main]
2+
use arbitrary::Arbitrary;
3+
use libfuzzer_sys::fuzz_target;
4+
5+
use devolutions_crypto::ciphertext::{encrypt_with_aad, CiphertextVersion};
6+
7+
#[derive(Arbitrary, Clone, Debug)]
8+
struct Input {
9+
data: Vec<u8>,
10+
key: Vec<u8>,
11+
aad: Vec<u8>,
12+
version: CiphertextVersion,
13+
}
14+
15+
fuzz_target!(|data: Input| {
16+
let _ = encrypt_with_aad(&data.data, &data.key, &data.aad, data.version);
17+
});

0 commit comments

Comments
 (0)