/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.09k | FuzzedDataProvider fdp(data, size); |
28 | | |
29 | | // Choose which crypto operation to test |
30 | 1.09k | uint8_t path = fdp.ConsumeIntegralInRange<uint8_t>(0, 9); |
31 | | |
32 | | // Pick a hash algorithm |
33 | 1.09k | HashAlgorithm hash_alg = fdp.PickValueInArray({ |
34 | 1.09k | HashAlgorithm::MD5, |
35 | 1.09k | HashAlgorithm::SHA1, |
36 | 1.09k | HashAlgorithm::SHA224, |
37 | 1.09k | HashAlgorithm::SHA256, |
38 | 1.09k | HashAlgorithm::SHA384, |
39 | 1.09k | HashAlgorithm::SHA512 |
40 | 1.09k | }); |
41 | | |
42 | 1.09k | try { |
43 | 1.09k | switch (path) { |
44 | 35 | case 0: { |
45 | | // Test Hash creation and update |
46 | 35 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
47 | 35 | if (hash) { |
48 | 35 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
49 | 35 | if (!input_data.empty()) { |
50 | 35 | hash->update(input_data.data(), input_data.size()); |
51 | 35 | } |
52 | 35 | std::vector<uint8_t> digest = hash->final(hash->getOutputLength()); |
53 | 35 | delete hash; |
54 | 35 | } |
55 | 35 | break; |
56 | 0 | } |
57 | | |
58 | 113 | case 1: { |
59 | | // Test Hash with multiple updates |
60 | 113 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
61 | 113 | if (hash) { |
62 | 113 | size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10); |
63 | 511 | for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) { |
64 | 398 | size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); |
65 | 398 | std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size); |
66 | 398 | if (!chunk.empty()) { |
67 | 218 | hash->update(chunk.data(), chunk.size()); |
68 | 218 | } |
69 | 398 | } |
70 | 113 | std::vector<uint8_t> digest = hash->final(hash->getOutputLength()); |
71 | 113 | delete hash; |
72 | 113 | } |
73 | 113 | break; |
74 | 0 | } |
75 | | |
76 | 153 | case 2: { |
77 | | // Test Hash with OutputBuffer |
78 | 153 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
79 | 153 | if (hash) { |
80 | 153 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
81 | 153 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
82 | 153 | ); |
83 | 153 | if (!input_data.empty()) { |
84 | 78 | hash->update(input_data.data(), input_data.size()); |
85 | 78 | } |
86 | 153 | isc::util::OutputBuffer result(hash->getOutputLength()); |
87 | 153 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hash->getOutputLength() * 2); |
88 | 153 | hash->final(result, len); |
89 | 153 | delete hash; |
90 | 153 | } |
91 | 153 | break; |
92 | 0 | } |
93 | | |
94 | 124 | case 3: { |
95 | | // Test Hash with void* result |
96 | 124 | Hash* hash = CryptoLink::getCryptoLink().createHash(hash_alg); |
97 | 124 | if (hash) { |
98 | 124 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
99 | 124 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
100 | 124 | ); |
101 | 124 | if (!input_data.empty()) { |
102 | 67 | hash->update(input_data.data(), input_data.size()); |
103 | 67 | } |
104 | 124 | std::vector<uint8_t> result(hash->getOutputLength() * 2); |
105 | 124 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, result.size()); |
106 | 124 | hash->final(result.data(), len); |
107 | 124 | delete hash; |
108 | 124 | } |
109 | 124 | break; |
110 | 0 | } |
111 | | |
112 | 71 | case 4: { |
113 | | // Test HMAC creation and signing |
114 | 71 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
115 | 71 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
116 | 71 | if (secret.empty()) { |
117 | 7 | secret.push_back(0); // Ensure non-empty secret |
118 | 7 | } |
119 | | |
120 | 71 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
121 | 71 | secret.data(), secret.size(), hash_alg |
122 | 71 | ); |
123 | 71 | if (hmac) { |
124 | 71 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
125 | 71 | if (!input_data.empty()) { |
126 | 51 | hmac->update(input_data.data(), input_data.size()); |
127 | 51 | } |
128 | 71 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
129 | 71 | delete hmac; |
130 | 71 | } |
131 | 71 | break; |
132 | 0 | } |
133 | | |
134 | 148 | case 5: { |
135 | | // Test HMAC with multiple updates |
136 | 148 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
137 | 148 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
138 | 148 | if (secret.empty()) { |
139 | 9 | secret.push_back(0); |
140 | 9 | } |
141 | | |
142 | 148 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
143 | 148 | secret.data(), secret.size(), hash_alg |
144 | 148 | ); |
145 | 148 | if (hmac) { |
146 | 148 | size_t num_updates = fdp.ConsumeIntegralInRange<size_t>(1, 10); |
147 | 592 | for (size_t i = 0; i < num_updates && fdp.remaining_bytes() > 0; i++) { |
148 | 444 | size_t chunk_size = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); |
149 | 444 | std::vector<uint8_t> chunk = fdp.ConsumeBytes<uint8_t>(chunk_size); |
150 | 444 | if (!chunk.empty()) { |
151 | 310 | hmac->update(chunk.data(), chunk.size()); |
152 | 310 | } |
153 | 444 | } |
154 | 148 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
155 | 148 | delete hmac; |
156 | 148 | } |
157 | 148 | break; |
158 | 0 | } |
159 | | |
160 | 174 | case 6: { |
161 | | // Test HMAC with OutputBuffer |
162 | 174 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
163 | 174 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
164 | 174 | if (secret.empty()) { |
165 | 10 | secret.push_back(0); |
166 | 10 | } |
167 | | |
168 | 174 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
169 | 174 | secret.data(), secret.size(), hash_alg |
170 | 174 | ); |
171 | 174 | if (hmac) { |
172 | 174 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
173 | 174 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
174 | 174 | ); |
175 | 174 | if (!input_data.empty()) { |
176 | 83 | hmac->update(input_data.data(), input_data.size()); |
177 | 83 | } |
178 | 174 | isc::util::OutputBuffer result(hmac->getOutputLength()); |
179 | 174 | size_t len = fdp.ConsumeIntegralInRange<size_t>(0, hmac->getOutputLength() * 2); |
180 | 174 | hmac->sign(result, len); |
181 | 174 | delete hmac; |
182 | 174 | } |
183 | 174 | break; |
184 | 0 | } |
185 | | |
186 | 144 | case 7: { |
187 | | // Test HMAC verification |
188 | 144 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(1, 256); |
189 | 144 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
190 | 144 | if (secret.empty()) { |
191 | 11 | secret.push_back(0); |
192 | 11 | } |
193 | | |
194 | 144 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
195 | 144 | secret.data(), secret.size(), hash_alg |
196 | 144 | ); |
197 | 144 | if (hmac) { |
198 | 144 | std::vector<uint8_t> input_data = fdp.ConsumeBytes<uint8_t>( |
199 | 144 | fdp.ConsumeIntegralInRange<size_t>(0, size) |
200 | 144 | ); |
201 | 144 | if (!input_data.empty()) { |
202 | 84 | hmac->update(input_data.data(), input_data.size()); |
203 | 84 | } |
204 | | |
205 | | // Generate signature |
206 | 144 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
207 | | |
208 | | // Verify with same data (should succeed) |
209 | 144 | HMAC* verify_hmac = CryptoLink::getCryptoLink().createHMAC( |
210 | 144 | secret.data(), secret.size(), hash_alg |
211 | 144 | ); |
212 | 144 | if (verify_hmac) { |
213 | 144 | if (!input_data.empty()) { |
214 | 84 | verify_hmac->update(input_data.data(), input_data.size()); |
215 | 84 | } |
216 | 144 | verify_hmac->verify(signature.data(), signature.size()); |
217 | 144 | delete verify_hmac; |
218 | 144 | } |
219 | 144 | delete hmac; |
220 | 144 | } |
221 | 144 | break; |
222 | 0 | } |
223 | | |
224 | 103 | case 8: { |
225 | | // Test HMAC with long secret (should be hashed) |
226 | 103 | size_t secret_len = fdp.ConsumeIntegralInRange<size_t>(256, 1024); |
227 | 103 | std::vector<uint8_t> secret = fdp.ConsumeBytes<uint8_t>(secret_len); |
228 | 103 | if (secret.size() < 64) { |
229 | 38 | secret.resize(64, 0x42); // Pad to ensure long secret |
230 | 38 | } |
231 | | |
232 | 103 | HMAC* hmac = CryptoLink::getCryptoLink().createHMAC( |
233 | 103 | secret.data(), secret.size(), hash_alg |
234 | 103 | ); |
235 | 103 | if (hmac) { |
236 | 103 | std::vector<uint8_t> input_data = fdp.ConsumeRemainingBytes<uint8_t>(); |
237 | 103 | if (!input_data.empty()) { |
238 | 54 | hmac->update(input_data.data(), input_data.size()); |
239 | 54 | } |
240 | 103 | std::vector<uint8_t> signature = hmac->sign(hmac->getOutputLength()); |
241 | 103 | delete hmac; |
242 | 103 | } |
243 | 103 | 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.09k | } |
257 | 1.09k | } 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.09k | return 0; |
264 | 1.09k | } |