/src/crypto_aead_fuzzer.cc
Line | Count | Source |
1 | | // Copyright 2026 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <assert.h> |
16 | | #include <stdlib.h> |
17 | | #include <stdint.h> |
18 | | #include <string.h> |
19 | | #include <sodium.h> |
20 | | |
21 | | #include "fake_random.h" |
22 | | |
23 | | typedef int (*aead_encrypt_fn)(unsigned char *cipher, |
24 | | unsigned long long *cipher_len, |
25 | | const unsigned char *message, |
26 | | unsigned long long message_len, |
27 | | const unsigned char *ad, |
28 | | unsigned long long ad_len, |
29 | | const unsigned char *nsec, |
30 | | const unsigned char *npub, |
31 | | const unsigned char *k); |
32 | | |
33 | | typedef int (*aead_decrypt_fn)(unsigned char *message, |
34 | | unsigned long long *message_len, |
35 | | unsigned char *nsec, |
36 | | const unsigned char *cipher, |
37 | | unsigned long long cipher_len, |
38 | | const unsigned char *ad, |
39 | | unsigned long long ad_len, |
40 | | const unsigned char *npub, |
41 | | const unsigned char *k); |
42 | | |
43 | | struct AEAD_Algorithm { |
44 | | aead_encrypt_fn encrypt; |
45 | | aead_decrypt_fn decrypt; |
46 | | size_t key_bytes; |
47 | | size_t npub_bytes; |
48 | | size_t a_bytes; |
49 | | int (*is_available)(void); |
50 | | }; |
51 | | |
52 | 381 | static int always_available(void) { return 1; } |
53 | | |
54 | 605 | extern "C" int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) { |
55 | 605 | if (sodium_init() == -1) { |
56 | 0 | return 0; |
57 | 0 | } |
58 | | |
59 | 605 | if (size < 2) { |
60 | 2 | return 0; |
61 | 2 | } |
62 | | |
63 | 603 | static AEAD_Algorithm algs[] = { |
64 | 603 | { |
65 | 603 | crypto_aead_chacha20poly1305_ietf_encrypt, |
66 | 603 | crypto_aead_chacha20poly1305_ietf_decrypt, |
67 | 603 | crypto_aead_chacha20poly1305_ietf_KEYBYTES, |
68 | 603 | crypto_aead_chacha20poly1305_ietf_NPUBBYTES, |
69 | 603 | crypto_aead_chacha20poly1305_ietf_ABYTES, |
70 | 603 | always_available |
71 | 603 | }, |
72 | 603 | { |
73 | 603 | crypto_aead_xchacha20poly1305_ietf_encrypt, |
74 | 603 | crypto_aead_xchacha20poly1305_ietf_decrypt, |
75 | 603 | crypto_aead_xchacha20poly1305_ietf_KEYBYTES, |
76 | 603 | crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, |
77 | 603 | crypto_aead_xchacha20poly1305_ietf_ABYTES, |
78 | 603 | always_available |
79 | 603 | }, |
80 | 603 | { |
81 | 603 | crypto_aead_chacha20poly1305_encrypt, |
82 | 603 | crypto_aead_chacha20poly1305_decrypt, |
83 | 603 | crypto_aead_chacha20poly1305_KEYBYTES, |
84 | 603 | crypto_aead_chacha20poly1305_NPUBBYTES, |
85 | 603 | crypto_aead_chacha20poly1305_ABYTES, |
86 | 603 | always_available |
87 | 603 | }, |
88 | 603 | #ifdef crypto_aead_aegis128l_KEYBYTES |
89 | 603 | { |
90 | 603 | crypto_aead_aegis128l_encrypt, |
91 | 603 | crypto_aead_aegis128l_decrypt, |
92 | 603 | crypto_aead_aegis128l_KEYBYTES, |
93 | 603 | crypto_aead_aegis128l_NPUBBYTES, |
94 | 603 | crypto_aead_aegis128l_ABYTES, |
95 | 603 | always_available |
96 | 603 | }, |
97 | 603 | #endif |
98 | 603 | #ifdef crypto_aead_aegis256_KEYBYTES |
99 | 603 | { |
100 | 603 | crypto_aead_aegis256_encrypt, |
101 | 603 | crypto_aead_aegis256_decrypt, |
102 | 603 | crypto_aead_aegis256_KEYBYTES, |
103 | 603 | crypto_aead_aegis256_NPUBBYTES, |
104 | 603 | crypto_aead_aegis256_ABYTES, |
105 | 603 | always_available |
106 | 603 | }, |
107 | 603 | #endif |
108 | 603 | { |
109 | 603 | crypto_aead_aes256gcm_encrypt, |
110 | 603 | crypto_aead_aes256gcm_decrypt, |
111 | 603 | crypto_aead_aes256gcm_KEYBYTES, |
112 | 603 | crypto_aead_aes256gcm_NPUBBYTES, |
113 | 603 | crypto_aead_aes256gcm_ABYTES, |
114 | 603 | crypto_aead_aes256gcm_is_available |
115 | 603 | } |
116 | 603 | }; |
117 | 603 | size_t num_algs = sizeof(algs) / sizeof(algs[0]); |
118 | | |
119 | 603 | uint8_t choice = data[0] % num_algs; |
120 | 603 | const AEAD_Algorithm &alg = algs[choice]; |
121 | | |
122 | 603 | if (alg.is_available && !alg.is_available()) { |
123 | 0 | return 0; |
124 | 0 | } |
125 | | |
126 | 603 | if (size < 1 + alg.key_bytes + alg.npub_bytes) { |
127 | 11 | return 0; |
128 | 11 | } |
129 | | |
130 | 592 | const unsigned char *k = data + 1; |
131 | 592 | const unsigned char *npub = data + 1 + alg.key_bytes; |
132 | 592 | const unsigned char *msg = data + 1 + alg.key_bytes + alg.npub_bytes; |
133 | 592 | size_t total_msg_len = size - (1 + alg.key_bytes + alg.npub_bytes); |
134 | | |
135 | | // Split remaining data into message and associated data |
136 | 592 | size_t ad_len = total_msg_len / 4; |
137 | 592 | size_t msg_len = total_msg_len - ad_len; |
138 | 592 | const unsigned char *ad = msg; |
139 | 592 | msg += ad_len; |
140 | | |
141 | | // Limit lengths to avoid timeouts |
142 | 592 | if (msg_len > 4096) msg_len = 4096; |
143 | 592 | if (ad_len > 4096) ad_len = 4096; |
144 | | |
145 | 592 | unsigned char *ciphertext = (unsigned char *) malloc(msg_len + alg.a_bytes); |
146 | 592 | unsigned long long ciphertext_len; |
147 | | |
148 | 592 | alg.encrypt(ciphertext, &ciphertext_len, |
149 | 592 | msg, msg_len, |
150 | 592 | ad, ad_len, |
151 | 592 | NULL, npub, k); |
152 | | |
153 | 592 | unsigned char *decrypted = (unsigned char *) malloc(msg_len + alg.a_bytes); |
154 | 592 | unsigned long long decrypted_len; |
155 | 592 | int err = alg.decrypt(decrypted, &decrypted_len, |
156 | 592 | NULL, |
157 | 592 | ciphertext, ciphertext_len, |
158 | 592 | ad, ad_len, |
159 | 592 | npub, k); |
160 | | |
161 | 592 | if (err == 0) { |
162 | 592 | assert(decrypted_len == msg_len); |
163 | 592 | assert(memcmp(decrypted, msg, msg_len) == 0); |
164 | 592 | } |
165 | | |
166 | 592 | free(ciphertext); |
167 | 592 | free(decrypted); |
168 | | |
169 | 592 | return 0; |
170 | 592 | } |