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