tangled
alpha
login
or
join now
anil.recoil.org
/
ocaml-crypto
forked from
gazagnaire.org/ocaml-crypto
0
fork
atom
upstream: https://github.com/mirage/mirage-crypto
0
fork
atom
overview
issues
pulls
pipelines
Add untracked directories
Thomas Gazagnaire
1 month ago
a4649310
ff83230d
+155
2 changed files
expand all
collapse all
unified
split
fuzz
dune
fuzz_crypto.ml
+15
fuzz/dune
···
1
1
+
; Crowbar fuzz testing for crypto
2
2
+
;
3
3
+
; To run: dune exec ocaml-crypto/fuzz/fuzz_crypto.exe
4
4
+
; With AFL: afl-fuzz -i fuzz/corpus -o fuzz/findings -- ./_build/default/ocaml-crypto/fuzz/fuzz_crypto.exe @@
5
5
+
6
6
+
(executable
7
7
+
(name fuzz_crypto)
8
8
+
(modules fuzz_crypto)
9
9
+
(libraries crypto crowbar))
10
10
+
11
11
+
(rule
12
12
+
(alias fuzz)
13
13
+
(deps fuzz_crypto.exe)
14
14
+
(action
15
15
+
(run %{exe:fuzz_crypto.exe})))
+140
fuzz/fuzz_crypto.ml
···
1
1
+
(*---------------------------------------------------------------------------
2
2
+
Copyright (c) 2025 Thomas Gazagnaire. All rights reserved.
3
3
+
SPDX-License-Identifier: MIT
4
4
+
---------------------------------------------------------------------------*)
5
5
+
6
6
+
(* Crowbar-based fuzz testing for cryptographic primitives *)
7
7
+
8
8
+
open Crowbar
9
9
+
10
10
+
(* AES key sizes: 16, 24, or 32 bytes *)
11
11
+
let valid_aes_key_sizes = [ 16; 24; 32 ]
12
12
+
13
13
+
(* AES block size: 16 bytes *)
14
14
+
let aes_block_size = 16
15
15
+
16
16
+
(* Generate a padded key of valid size *)
17
17
+
let make_key size input =
18
18
+
if String.length input >= size then String.sub input 0 size
19
19
+
else input ^ String.make (size - String.length input) '\x00'
20
20
+
21
21
+
(* Test AES ECB roundtrip for valid key sizes *)
22
22
+
let test_aes_ecb_roundtrip key_input plaintext =
23
23
+
List.iter
24
24
+
(fun key_size ->
25
25
+
let key_str = make_key key_size key_input in
26
26
+
let key = Crypto.AES.ECB.of_secret key_str in
27
27
+
(* Pad plaintext to block size *)
28
28
+
let pt_len = String.length plaintext in
29
29
+
let padded_len =
30
30
+
if pt_len = 0 then aes_block_size
31
31
+
else ((pt_len + aes_block_size - 1) / aes_block_size) * aes_block_size
32
32
+
in
33
33
+
let padded_pt =
34
34
+
if pt_len >= padded_len then String.sub plaintext 0 padded_len
35
35
+
else plaintext ^ String.make (padded_len - pt_len) '\x00'
36
36
+
in
37
37
+
let ciphertext = Crypto.AES.ECB.encrypt ~key padded_pt in
38
38
+
let decrypted = Crypto.AES.ECB.decrypt ~key ciphertext in
39
39
+
check_eq ~pp:Format.pp_print_string padded_pt decrypted)
40
40
+
valid_aes_key_sizes
41
41
+
42
42
+
(* Test AES CBC roundtrip *)
43
43
+
let test_aes_cbc_roundtrip key_input iv_input plaintext =
44
44
+
List.iter
45
45
+
(fun key_size ->
46
46
+
let key_str = make_key key_size key_input in
47
47
+
let key = Crypto.AES.CBC.of_secret key_str in
48
48
+
let iv = make_key aes_block_size iv_input in
49
49
+
(* Pad plaintext to block size *)
50
50
+
let pt_len = String.length plaintext in
51
51
+
let padded_len =
52
52
+
if pt_len = 0 then aes_block_size
53
53
+
else ((pt_len + aes_block_size - 1) / aes_block_size) * aes_block_size
54
54
+
in
55
55
+
let padded_pt =
56
56
+
if pt_len >= padded_len then String.sub plaintext 0 padded_len
57
57
+
else plaintext ^ String.make (padded_len - pt_len) '\x00'
58
58
+
in
59
59
+
let ciphertext = Crypto.AES.CBC.encrypt ~key ~iv padded_pt in
60
60
+
let decrypted = Crypto.AES.CBC.decrypt ~key ~iv ciphertext in
61
61
+
check_eq ~pp:Format.pp_print_string padded_pt decrypted)
62
62
+
valid_aes_key_sizes
63
63
+
64
64
+
(* Test AES CTR roundtrip *)
65
65
+
let test_aes_ctr_roundtrip key_input ctr_input plaintext =
66
66
+
if String.length plaintext = 0 then ()
67
67
+
else
68
68
+
List.iter
69
69
+
(fun key_size ->
70
70
+
let key_str = make_key key_size key_input in
71
71
+
let key = Crypto.AES.CTR.of_secret key_str in
72
72
+
let ctr_str = make_key aes_block_size ctr_input in
73
73
+
let ctr = Crypto.AES.CTR.ctr_of_octets ctr_str in
74
74
+
let ciphertext = Crypto.AES.CTR.encrypt ~key ~ctr plaintext in
75
75
+
let decrypted = Crypto.AES.CTR.decrypt ~key ~ctr ciphertext in
76
76
+
check_eq ~pp:Format.pp_print_string plaintext decrypted)
77
77
+
valid_aes_key_sizes
78
78
+
79
79
+
(* Test AES GCM roundtrip *)
80
80
+
let test_aes_gcm_roundtrip key_input nonce_input plaintext =
81
81
+
List.iter
82
82
+
(fun key_size ->
83
83
+
let key_str = make_key key_size key_input in
84
84
+
let key = Crypto.AES.GCM.of_secret key_str in
85
85
+
(* GCM nonce should be 12 bytes for optimal performance *)
86
86
+
let nonce = make_key 12 nonce_input in
87
87
+
let adata = "" in
88
88
+
let result = Crypto.AES.GCM.authenticate_encrypt ~key ~nonce ~adata plaintext in
89
89
+
match Crypto.AES.GCM.authenticate_decrypt ~key ~nonce ~adata result with
90
90
+
| Some decrypted -> check_eq ~pp:Format.pp_print_string plaintext decrypted
91
91
+
| None -> fail "GCM decryption failed")
92
92
+
valid_aes_key_sizes
93
93
+
94
94
+
(* Test AES CCM roundtrip *)
95
95
+
let test_aes_ccm_roundtrip key_input nonce_input plaintext =
96
96
+
if String.length plaintext = 0 then ()
97
97
+
else
98
98
+
List.iter
99
99
+
(fun key_size ->
100
100
+
let key_str = make_key key_size key_input in
101
101
+
let key = Crypto.AES.CCM16.of_secret key_str in
102
102
+
(* CCM nonce should be 7-13 bytes; use 12 *)
103
103
+
let nonce = make_key 12 nonce_input in
104
104
+
let adata = "" in
105
105
+
let result =
106
106
+
Crypto.AES.CCM16.authenticate_encrypt ~key ~nonce ~adata plaintext
107
107
+
in
108
108
+
match Crypto.AES.CCM16.authenticate_decrypt ~key ~nonce ~adata result with
109
109
+
| Some decrypted ->
110
110
+
check_eq ~pp:Format.pp_print_string plaintext decrypted
111
111
+
| None -> fail "CCM decryption failed")
112
112
+
valid_aes_key_sizes
113
113
+
114
114
+
(* Test ChaCha20-Poly1305 roundtrip *)
115
115
+
let test_chacha20_poly1305_roundtrip key_input nonce_input plaintext =
116
116
+
(* ChaCha20 key is 32 bytes, nonce is 12 bytes *)
117
117
+
let key_str = make_key 32 key_input in
118
118
+
let key = Crypto.Chacha20.of_secret key_str in
119
119
+
let nonce = make_key 12 nonce_input in
120
120
+
let adata = "" in
121
121
+
let result =
122
122
+
Crypto.Chacha20.authenticate_encrypt ~key ~nonce ~adata plaintext
123
123
+
in
124
124
+
match Crypto.Chacha20.authenticate_decrypt ~key ~nonce ~adata result with
125
125
+
| Some decrypted -> check_eq ~pp:Format.pp_print_string plaintext decrypted
126
126
+
| None -> fail "ChaCha20-Poly1305 decryption failed"
127
127
+
128
128
+
let () =
129
129
+
add_test ~name:"crypto: aes ecb roundtrip" [ bytes; bytes ]
130
130
+
test_aes_ecb_roundtrip;
131
131
+
add_test ~name:"crypto: aes cbc roundtrip" [ bytes; bytes; bytes ]
132
132
+
test_aes_cbc_roundtrip;
133
133
+
add_test ~name:"crypto: aes ctr roundtrip" [ bytes; bytes; bytes ]
134
134
+
test_aes_ctr_roundtrip;
135
135
+
add_test ~name:"crypto: aes gcm roundtrip" [ bytes; bytes; bytes ]
136
136
+
test_aes_gcm_roundtrip;
137
137
+
add_test ~name:"crypto: aes ccm roundtrip" [ bytes; bytes; bytes ]
138
138
+
test_aes_ccm_roundtrip;
139
139
+
add_test ~name:"crypto: chacha20-poly1305 roundtrip" [ bytes; bytes; bytes ]
140
140
+
test_chacha20_poly1305_roundtrip