/src/kea-fuzzer/fuzz_cryptolink.cc
Line | Count | Source |
1 | | // Copyright (C) 2025 Ada Logics Ltd. |
2 | | // |
3 | | // This Source Code Form is subject to the terms of the Mozilla Public |
4 | | // License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
6 | | //////////////////////////////////////////////////////////////////////////////// |
7 | | #include <config.h> |
8 | | #include <fuzzer/FuzzedDataProvider.h> |
9 | | |
10 | | #include <exceptions/exceptions.h> |
11 | | #include <cryptolink/cryptolink.h> |
12 | | #include <cryptolink/crypto_hash.h> |
13 | | #include <cryptolink/crypto_hmac.h> |
14 | | #include <cryptolink/crypto_rng.h> |
15 | | |
16 | | #include <string> |
17 | | #include <vector> |
18 | | #include <cstddef> |
19 | | |
20 | | using namespace isc::cryptolink; |
21 | | |
22 | 1.10k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
23 | 1.10k | if (size < 3) { |
24 | 2 | return 0; |
25 | 2 | } |
26 | | |
27 | 1.10k | FuzzedDataProvider fdp(data, size); |
28 | | |
29 | | // Choose which crypto operation to test |
30 | 1.10k | uint8_t path = fdp.ConsumeIntegralInRange<uint8_t>(0, 9); |
31 | | |
32 | | // Pick a hash algorithm |
33 | 1.10k | HashAlgorithm hash_alg = fdp.PickValueInArray({ |
34 | 1.10k | HashAlgorithm::MD5, |
35 | 1.10k | HashAlgorithm::SHA1, |
36 | 1.10k | HashAlgorithm::SHA224, |
37 | 1.10k | HashAlgorithm::SHA256, |
38 | 1.10k | HashAlgorithm::SHA384, |
39 | 1.10k | HashAlgorithm::SHA512 |
40 | 1.10k | }); |
41 | | |
42 | 1.10k | try { |
43 | 1.10k | switch (path) { |
44 | 31 | case 0: { |
45 | | // Test Hash creation and update |
46 | 31 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
47 | 31 | if (hash) { |
48 | 31 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
49 | 31 | if (!input_data.empty()) { |
50 | 31 | hash->update(input_data.data(), input_data.size()); |
51 | 31 | } |
52 | 31 | std::vector<uint8_t> digest = hash->final(hash->getOutputLength()); |
53 | 31 | delete hash; |
54 | 31 | } |
55 | 31 | break; |
56 | 0 | } |
57 | | |
58 | 108 | case 1: { |
59 | | // Test Hash with multiple updates |
60 | 108 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
61 | 108 | if (hash) { |
62 | 108 | size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10); |
63 | 501 | for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) { |
64 | 393 | size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); |
65 | 393 | std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size); |
66 | 393 | if (!chunk.empty()) { |
67 | 248 | hash->update(chunk.data(), chunk.size()); |
68 | 248 | } |
69 | 393 | } |
70 | 108 | std::vector<uint8_t> digest = hash->final(hash->getOutputLength()); |
71 | 108 | delete hash; |
72 | 108 | } |
73 | 108 | break; |
74 | 0 | } |
75 | | |
76 | 157 | case 2: { |
77 | | // Test Hash with OutputBuffer |
78 | 157 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
79 | 157 | if (hash) { |
80 | 157 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
81 | 157 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
82 | 157 | ); |
83 | 157 | if (!input_data.empty()) { |
84 | 75 | hash->update(input_data.data(), input_data.size()); |
85 | 75 | } |
86 | 157 | isc::util::OutputBuffer result(hash->getOutputLength()); |
87 | 157 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hash->getOutputLength() * 2); |
88 | 157 | hash->final(result, len); |
89 | 157 | delete hash; |
90 | 157 | } |
91 | 157 | break; |
92 | 0 | } |
93 | | |
94 | 126 | case 3: { |
95 | | // Test Hash with void* result |
96 | 126 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
97 | 126 | if (hash) { |
98 | 126 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
99 | 126 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
100 | 126 | ); |
101 | 126 | if (!input_data.empty()) { |
102 | 57 | hash->update(input_data.data(), input_data.size()); |
103 | 57 | } |
104 | 126 | std::vector<uint8_t> result(hash->getOutputLength() * 2); |
105 | 126 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, result.size()); |
106 | 126 | hash->final(result.data(), len); |
107 | 126 | delete hash; |
108 | 126 | } |
109 | 126 | break; |
110 | 0 | } |
111 | | |
112 | 72 | case 4: { |
113 | | // Test HMAC creation and signing |
114 | 72 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
115 | 72 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
116 | 72 | if (secret.empty()) { |
117 | 8 | secret.push_back(0); // Ensure non-empty secret |
118 | 8 | } |
119 | | |
120 | 72 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
121 | 72 | secret.data(), secret.size(), hash_alg |
122 | 72 | ); |
123 | 72 | if (hmac) { |
124 | 72 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
125 | 72 | if (!input_data.empty()) { |
126 | 50 | hmac->update(input_data.data(), input_data.size()); |
127 | 50 | } |
128 | 72 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
129 | 72 | delete hmac; |
130 | 72 | } |
131 | 72 | break; |
132 | 0 | } |
133 | | |
134 | 146 | case 5: { |
135 | | // Test HMAC with multiple updates |
136 | 146 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
137 | 146 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
138 | 146 | if (secret.empty()) { |
139 | 9 | secret.push_back(0); |
140 | 9 | } |
141 | | |
142 | 146 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
143 | 146 | secret.data(), secret.size(), hash_alg |
144 | 146 | ); |
145 | 146 | if (hmac) { |
146 | 146 | size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10); |
147 | 617 | for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) { |
148 | 471 | size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); |
149 | 471 | std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size); |
150 | 471 | if (!chunk.empty()) { |
151 | 300 | hmac->update(chunk.data(), chunk.size()); |
152 | 300 | } |
153 | 471 | } |
154 | 146 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
155 | 146 | delete hmac; |
156 | 146 | } |
157 | 146 | break; |
158 | 0 | } |
159 | | |
160 | 186 | case 6: { |
161 | | // Test HMAC with OutputBuffer |
162 | 186 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
163 | 186 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
164 | 186 | if (secret.empty()) { |
165 | 10 | secret.push_back(0); |
166 | 10 | } |
167 | | |
168 | 186 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
169 | 186 | secret.data(), secret.size(), hash_alg |
170 | 186 | ); |
171 | 186 | if (hmac) { |
172 | 186 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
173 | 186 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
174 | 186 | ); |
175 | 186 | if (!input_data.empty()) { |
176 | 91 | hmac->update(input_data.data(), input_data.size()); |
177 | 91 | } |
178 | 186 | isc::util::OutputBuffer result(hmac->getOutputLength()); |
179 | 186 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hmac->getOutputLength() * 2); |
180 | 186 | hmac->sign(result, len); |
181 | 186 | delete hmac; |
182 | 186 | } |
183 | 186 | break; |
184 | 0 | } |
185 | | |
186 | 147 | case 7: { |
187 | | // Test HMAC verification |
188 | 147 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
189 | 147 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
190 | 147 | if (secret.empty()) { |
191 | 12 | secret.push_back(0); |
192 | 12 | } |
193 | | |
194 | 147 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
195 | 147 | secret.data(), secret.size(), hash_alg |
196 | 147 | ); |
197 | 147 | if (hmac) { |
198 | 147 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
199 | 147 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
200 | 147 | ); |
201 | 147 | if (!input_data.empty()) { |
202 | 87 | hmac->update(input_data.data(), input_data.size()); |
203 | 87 | } |
204 | | |
205 | | // Generate signature |
206 | 147 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
207 | | |
208 | | // Verify with same data (should succeed) |
209 | 147 | HMAC* verify_hmac = CryptoLink::getCryptoLink().createHMAC( |
210 | 147 | secret.data(), secret.size(), hash_alg |
211 | 147 | ); |
212 | 147 | if (verify_hmac) { |
213 | 147 | if (!input_data.empty()) { |
214 | 87 | verify_hmac->update(input_data.data(), input_data.size()); |
215 | 87 | } |
216 | 147 | verify_hmac->verify(signature.data(), signature.size()); |
217 | 147 | delete verify_hmac; |
218 | 147 | } |
219 | 147 | delete hmac; |
220 | 147 | } |
221 | 147 | break; |
222 | 0 | } |
223 | | |
224 | 100 | case 8: { |
225 | | // Test HMAC with long secret (should be hashed) |
226 | 100 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(256, 1024); |
227 | 100 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
228 | 100 | if (secret.size() < 64) { |
229 | 35 | secret.resize(64, 0x42); // Pad to ensure long secret |
230 | 35 | } |
231 | | |
232 | 100 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
233 | 100 | secret.data(), secret.size(), hash_alg |
234 | 100 | ); |
235 | 100 | if (hmac) { |
236 | 100 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
237 | 100 | if (!input_data.empty()) { |
238 | 53 | hmac->update(input_data.data(), input_data.size()); |
239 | 53 | } |
240 | 100 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
241 | 100 | delete hmac; |
242 | 100 | } |
243 | 100 | break; |
244 | 0 | } |
245 | | |
246 | 34 | case 9: { |
247 | | // Test RNG generation |
248 | 34 | size_t rng_len = fdp.ConsumeIntegralInRange<size_t>(0, 1024); |
249 | 34 | std::vector<uint8_t> random_data = isc::cryptolink::random(rng_len); |
250 | | |
251 | | // Test Qid generation |
252 | 34 | uint16_t qid = isc::cryptolink::generateQid(); |
253 | 34 | (void)qid; // Use the variable |
254 | 34 | break; |
255 | 0 | } |
256 | 1.10k | } |
257 | 1.10k | } catch (const isc::Exception&) { |
258 | | // Expected for invalid algorithms, key lengths, etc. |
259 | 0 | } catch (const std::exception&) { |
260 | | // Catch any standard library exceptions |
261 | 0 | } |
262 | | |
263 | 1.10k | return 0; |
264 | 1.10k | } |