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