/src/openssl/fuzz/ml-kem.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * https://www.openssl.org/source/license.html |
8 | | * or in the file LICENSE in the source distribution. |
9 | | */ |
10 | | |
11 | | /* |
12 | | * Test ml-kem operation. |
13 | | */ |
14 | | #include <string.h> |
15 | | #include <openssl/evp.h> |
16 | | #include <openssl/err.h> |
17 | | #include <openssl/rand.h> |
18 | | #include <openssl/byteorder.h> |
19 | | #include <openssl/ml_kem.h> |
20 | | #include "internal/nelem.h" |
21 | | #include "fuzzer.h" |
22 | | |
23 | | /** |
24 | | * @brief Consumes an 8-bit unsigned integer from a buffer. |
25 | | * |
26 | | * This function extracts an 8-bit unsigned integer from the provided buffer, |
27 | | * updates the buffer pointer, and adjusts the remaining length. |
28 | | * |
29 | | * @param buf Pointer to the input buffer. |
30 | | * @param len Pointer to the size of the remaining buffer; updated after consumption. |
31 | | * @param val Pointer to store the extracted 8-bit value. |
32 | | * |
33 | | * @return Pointer to the updated buffer position after reading the value, |
34 | | * or NULL if the buffer does not contain enough data. |
35 | | */ |
36 | | static uint8_t *consume_uint8t(const uint8_t *buf, size_t *len, uint8_t *val) |
37 | 164 | { |
38 | 164 | if (*len < sizeof(uint8_t)) |
39 | 0 | return NULL; |
40 | 164 | *val = *buf; |
41 | 164 | *len -= sizeof(uint8_t); |
42 | 164 | return (uint8_t *)buf + 1; |
43 | 164 | } |
44 | | |
45 | | /** |
46 | | * @brief Selects a key type and size from a buffer. |
47 | | * |
48 | | * This function reads a key size value from the buffer, determines the |
49 | | * corresponding key type and length, and updates the buffer pointer |
50 | | * accordingly. If `only_valid` is set, it restricts selection to valid |
51 | | * key sizes; otherwise, it includes some invalid sizes for testing. |
52 | | * |
53 | | * @param buf Pointer to the buffer pointer; updated after reading. |
54 | | * @param len Pointer to the remaining buffer size; updated accordingly. |
55 | | * @param keytype Pointer to store the selected key type string. |
56 | | * @param keylen Pointer to store the selected key length. |
57 | | * @param only_valid Flag to restrict selection to valid key sizes. |
58 | | * |
59 | | * @return 1 if a key type is successfully selected, 0 on failure. |
60 | | */ |
61 | | static int select_keytype_and_size(uint8_t **buf, size_t *len, |
62 | | char **keytype, size_t *keylen, |
63 | | int only_valid) |
64 | 247 | { |
65 | 247 | uint16_t keysize; |
66 | 247 | uint16_t modulus = 6; |
67 | | |
68 | | /* |
69 | | * Note: We don't really care about endianess here, we just |
70 | | * want a random 16 bit value |
71 | | */ |
72 | 247 | *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf); |
73 | 247 | *len -= sizeof(uint16_t); |
74 | | |
75 | 247 | if (*buf == NULL) |
76 | 0 | return 0; |
77 | | |
78 | | /* |
79 | | * select from sizes |
80 | | * ML-KEM-512, ML-KEM-768, and ML-KEM-1024 |
81 | | * also select some invalid sizes to trigger |
82 | | * error paths |
83 | | */ |
84 | 247 | if (only_valid) |
85 | 166 | modulus = 3; |
86 | | |
87 | | /* |
88 | | * Note, keylens for valid values (cases 0-2) |
89 | | * are taken based on input values from our unit tests |
90 | | */ |
91 | 247 | switch (keysize % modulus) { |
92 | 101 | case 0: |
93 | 101 | *keytype = "ML-KEM-512"; |
94 | 101 | *keylen = OSSL_ML_KEM_512_PUBLIC_KEY_BYTES; |
95 | 101 | break; |
96 | 49 | case 1: |
97 | 49 | *keytype = "ML-KEM-768"; |
98 | 49 | *keylen = OSSL_ML_KEM_768_PUBLIC_KEY_BYTES; |
99 | 49 | break; |
100 | 56 | case 2: |
101 | 56 | *keytype = "ML-KEM-1024"; |
102 | 56 | *keylen = OSSL_ML_KEM_1024_PUBLIC_KEY_BYTES; |
103 | 56 | break; |
104 | 2 | case 3: |
105 | | /* select invalid alg */ |
106 | 2 | *keytype = "ML-KEM-13"; |
107 | 2 | *keylen = 13; |
108 | 2 | break; |
109 | 38 | case 4: |
110 | | /* Select valid alg, but bogus size */ |
111 | 38 | *keytype = "ML-KEM-1024"; |
112 | 38 | *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf); |
113 | 38 | *len -= sizeof(uint16_t); |
114 | 38 | *keylen = (size_t)keysize; |
115 | 38 | *keylen %= 1024; /* size to our key buffer */ |
116 | 38 | break; |
117 | 1 | default: |
118 | 1 | *keytype = NULL; |
119 | 1 | *keylen = 0; |
120 | 1 | break; |
121 | 247 | } |
122 | 247 | return 1; |
123 | 247 | } |
124 | | |
125 | | /** |
126 | | * @brief Creates an ML-KEM raw key from a buffer. |
127 | | * |
128 | | * This function selects a key type and size from the buffer, generates |
129 | | * a random key of the appropriate length, and creates either a public |
130 | | * or private ML-KEM key using OpenSSL's EVP_PKEY interface. |
131 | | * |
132 | | * @param buf Pointer to the buffer pointer; updated after reading. |
133 | | * @param len Pointer to the remaining buffer size; updated accordingly. |
134 | | * @param key1 Pointer to store the generated EVP_PKEY key (public or private). |
135 | | * @param key2 Unused parameter (reserved for future use). |
136 | | * |
137 | | * @note The generated key is allocated using OpenSSL's EVP_PKEY functions |
138 | | * and should be freed appropriately using `EVP_PKEY_free()`. |
139 | | */ |
140 | | static void create_mlkem_raw_key(uint8_t **buf, size_t *len, |
141 | | void **key1, void **key2) |
142 | 81 | { |
143 | 81 | EVP_PKEY *pubkey; |
144 | 81 | char *keytype = NULL; |
145 | 81 | size_t keylen = 0; |
146 | 81 | uint8_t key[4096]; |
147 | 81 | int pub = 0; |
148 | | |
149 | 81 | if (!select_keytype_and_size(buf, len, &keytype, &keylen, 0)) |
150 | 0 | return; |
151 | | |
152 | | /* |
153 | | * Select public or private key creation based on the low order |
154 | | * bit of the next buffer value |
155 | | * Note that keylen as returned from select_keytype_and_size is |
156 | | * a public key length, private keys for ML-KEM are always double |
157 | | * the size plus 32, so make that adjustment here |
158 | | */ |
159 | 81 | if ((*buf)[0] & 0x1) |
160 | 46 | pub = 1; |
161 | 35 | else |
162 | 35 | keylen = (keylen * 2) + 32; |
163 | | |
164 | | /* |
165 | | * libfuzzer provides by default up to 4096 bit input |
166 | | * buffers, but its typically much less (between 1 and 100 bytes) |
167 | | * so use RAND_bytes here instead |
168 | | */ |
169 | 81 | if (!RAND_bytes(key, keylen)) |
170 | 0 | return; |
171 | | |
172 | | /* |
173 | | * Try to generate either a raw public or private key using random data |
174 | | * Because the input is completely random, its effectively certain this |
175 | | * operation will fail, but it will still exercise the code paths below, |
176 | | * which is what we want the fuzzer to do |
177 | | */ |
178 | 81 | if (pub == 1) |
179 | 46 | pubkey = EVP_PKEY_new_raw_public_key_ex(NULL, keytype, NULL, key, keylen); |
180 | 35 | else |
181 | 35 | pubkey = EVP_PKEY_new_raw_private_key_ex(NULL, keytype, NULL, key, keylen); |
182 | | |
183 | 81 | *key1 = pubkey; |
184 | 81 | return; |
185 | 81 | } |
186 | | |
187 | | /** |
188 | | * @brief Generates a valid ML-KEM key using OpenSSL. |
189 | | * |
190 | | * This function selects a valid ML-KEM key type and size from the buffer, |
191 | | * initializes an OpenSSL EVP_PKEY context, and generates a cryptographic |
192 | | * key accordingly. |
193 | | * |
194 | | * @param buf Pointer to the buffer pointer; updated after reading. |
195 | | * @param len Pointer to the remaining buffer size; updated accordingly. |
196 | | * @param key1 Pointer to store the generated EVP_PKEY key. |
197 | | * @param unused Unused parameter (reserved for future use). |
198 | | * |
199 | | * @note The generated key is allocated using OpenSSL's EVP_PKEY functions |
200 | | * and should be freed using `EVP_PKEY_free()`. |
201 | | */ |
202 | | static void keygen_mlkem_real_key(uint8_t **buf, size_t *len, |
203 | | void **key1, void **key2) |
204 | 83 | { |
205 | 83 | char *keytype = NULL; |
206 | 83 | size_t keylen = 0; |
207 | 83 | EVP_PKEY_CTX *ctx = NULL; |
208 | 83 | EVP_PKEY **key; |
209 | | |
210 | 83 | *key1 = *key2 = NULL; |
211 | | |
212 | 83 | key = (EVP_PKEY **)key1; |
213 | | |
214 | 166 | again: |
215 | | /* |
216 | | * Only generate valid key types and lengths |
217 | | * Note, no adjustment is made to keylen here, as |
218 | | * the provider is responsible for selecting the keys and sizes |
219 | | * for us during the EVP_PKEY_keygen call |
220 | | */ |
221 | 166 | if (!select_keytype_and_size(buf, len, &keytype, &keylen, 1)) |
222 | 0 | return; |
223 | | |
224 | 166 | ctx = EVP_PKEY_CTX_new_from_name(NULL, keytype, NULL); |
225 | 166 | if (!ctx) { |
226 | 0 | fprintf(stderr, "Failed to generate ctx\n"); |
227 | 0 | return; |
228 | 0 | } |
229 | | |
230 | 166 | if (!EVP_PKEY_keygen_init(ctx)) { |
231 | 0 | fprintf(stderr, "Failed to init keygen ctx\n"); |
232 | 0 | goto err; |
233 | 0 | } |
234 | | |
235 | 166 | *key = EVP_PKEY_new(); |
236 | 166 | if (*key == NULL) |
237 | 0 | goto err; |
238 | | |
239 | 166 | if (!EVP_PKEY_generate(ctx, key)) { |
240 | 0 | fprintf(stderr, "Failed to generate new real key\n"); |
241 | 0 | goto err; |
242 | 0 | } |
243 | | |
244 | 166 | if (key == (EVP_PKEY **)key1) { |
245 | 83 | EVP_PKEY_CTX_free(ctx); |
246 | 83 | key = (EVP_PKEY **)key2; |
247 | 83 | goto again; |
248 | 83 | } |
249 | | |
250 | 83 | err: |
251 | 83 | EVP_PKEY_CTX_free(ctx); |
252 | 83 | return; |
253 | 166 | } |
254 | | |
255 | | /** |
256 | | * @brief Performs key encapsulation and decapsulation using an EVP_PKEY. |
257 | | * |
258 | | * This function generates a random key, encapsulates it using the provided |
259 | | * public key, then decapsulates it to retrieve the original key. It makes |
260 | | * use of OpenSSL's EVP_PKEY API for encryption and decryption. |
261 | | * |
262 | | * @param[out] buf Unused output buffer (reserved for future use). |
263 | | * @param[out] len Unused length parameter (reserved for future use). |
264 | | * @param[in] key1 Pointer to an EVP_PKEY structure used for key operations. |
265 | | * @param[in] in2 Unused input parameter (reserved for future use). |
266 | | * @param[out] out1 Unused output parameter (reserved for future use). |
267 | | * @param[out] out2 Unused output parameter (reserved for future use). |
268 | | */ |
269 | | static void mlkem_encap_decap(uint8_t **buf, size_t *len, void *key1, void *in2, |
270 | | void **out1, void **out2) |
271 | 46 | { |
272 | 46 | EVP_PKEY *key = (EVP_PKEY *)key1; |
273 | 46 | EVP_PKEY_CTX *ctx; |
274 | 46 | unsigned char genkey[32]; |
275 | 46 | size_t genkey_len = 32; |
276 | 46 | unsigned char unwrappedkey[32]; |
277 | 46 | size_t unwrappedkey_len = 32; |
278 | 46 | unsigned char wrapkey[1568]; |
279 | 46 | size_t wrapkey_len = 1568; |
280 | | |
281 | 46 | ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); |
282 | 46 | if (ctx == NULL) { |
283 | 0 | fprintf(stderr, "Failed to allocate ctx\n"); |
284 | 0 | goto err; |
285 | 0 | } |
286 | | |
287 | 46 | if (!EVP_PKEY_encapsulate_init(ctx, NULL)) { |
288 | 0 | fprintf(stderr, "Failed to init encap context\n"); |
289 | 0 | goto err; |
290 | 0 | } |
291 | | |
292 | 46 | if (!RAND_bytes(genkey, genkey_len)) |
293 | 0 | goto err; |
294 | | |
295 | 46 | if (EVP_PKEY_encapsulate(ctx, wrapkey, &wrapkey_len, genkey, &genkey_len) <= 0) { |
296 | 0 | fprintf(stderr, "Failed to encapsulate key\n"); |
297 | 0 | goto err; |
298 | 0 | } |
299 | | |
300 | 46 | EVP_PKEY_CTX_free(ctx); |
301 | 46 | ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); |
302 | 46 | if (ctx == NULL) { |
303 | 0 | fprintf(stderr, "Failed to create context\n"); |
304 | 0 | goto err; |
305 | 0 | } |
306 | | |
307 | 46 | if (!EVP_PKEY_decapsulate_init(ctx, NULL)) { |
308 | 0 | fprintf(stderr, "Failed to init decap\n"); |
309 | 0 | goto err; |
310 | 0 | } |
311 | | |
312 | 46 | if (EVP_PKEY_decapsulate(ctx, unwrappedkey, &unwrappedkey_len, |
313 | 46 | wrapkey, wrapkey_len) <= 0) { |
314 | 0 | fprintf(stderr, "Failed to decap key\n"); |
315 | 0 | goto err; |
316 | 0 | } |
317 | | |
318 | 46 | if (memcmp(unwrappedkey, genkey, genkey_len)) |
319 | 0 | fprintf(stderr, "mismatch on secret comparison\n"); |
320 | 46 | err: |
321 | 46 | EVP_PKEY_CTX_free(ctx); |
322 | 46 | return; |
323 | 46 | } |
324 | | |
325 | | /** |
326 | | * @brief Derives a shared secret using the provided key and peer key. |
327 | | * |
328 | | * This function performs a key derivation operation using the given |
329 | | * private key and peer public key. The resulting shared secret is |
330 | | * allocated dynamically and must be freed by the caller. |
331 | | * |
332 | | * @param[in] key The private key used for derivation. |
333 | | * @param[in] peer The peer's public key. |
334 | | * @param[out] shared Pointer to the derived shared secret (allocated). |
335 | | * @param[out] shared_len Length of the derived shared secret. |
336 | | * |
337 | | * @note The caller is responsible for freeing the memory allocated |
338 | | * for `shared` using `OPENSSL_free()`. |
339 | | */ |
340 | | static void do_derive(EVP_PKEY *key, EVP_PKEY *peer, uint8_t **shared, size_t *shared_len) |
341 | 20 | { |
342 | 20 | EVP_PKEY_CTX *ctx = NULL; |
343 | | |
344 | 20 | *shared = NULL; |
345 | 20 | *shared_len = 0; |
346 | | |
347 | 20 | ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); |
348 | 20 | if (ctx == NULL) { |
349 | 0 | fprintf(stderr, "failed to create keygen context\n"); |
350 | 0 | goto err; |
351 | 0 | } |
352 | | |
353 | 20 | if (!EVP_PKEY_derive_init(ctx)) { |
354 | 0 | fprintf(stderr, "failed to init derive context\n"); |
355 | 0 | goto err; |
356 | 0 | } |
357 | | |
358 | 20 | if (!EVP_PKEY_derive_set_peer(ctx, peer)) { |
359 | 0 | fprintf(stderr, "failed to set peer\n"); |
360 | 0 | goto err; |
361 | 0 | } |
362 | | |
363 | 20 | if (!EVP_PKEY_derive(ctx, NULL, shared_len)) { |
364 | 0 | fprintf(stderr, "Derive failed 1\n"); |
365 | 0 | goto err; |
366 | 0 | } |
367 | | |
368 | 20 | if (*shared_len == 0) |
369 | 20 | goto err; |
370 | | |
371 | 0 | *shared = OPENSSL_zalloc(*shared_len); |
372 | 0 | if (*shared == NULL) { |
373 | 0 | fprintf(stderr, "Failed to alloc\n"); |
374 | 0 | goto err; |
375 | 0 | } |
376 | 0 | if (!EVP_PKEY_derive(ctx, *shared, shared_len)) { |
377 | 0 | fprintf(stderr, "Derive failed 2\n"); |
378 | 0 | OPENSSL_free(*shared); |
379 | 0 | *shared = NULL; |
380 | 0 | *shared_len = 0; |
381 | 0 | goto err; |
382 | 0 | } |
383 | 20 | err: |
384 | 20 | EVP_PKEY_CTX_free(ctx); |
385 | 20 | } |
386 | | |
387 | | /** |
388 | | * @brief Performs a key exchange using ML-KEM. |
389 | | * |
390 | | * This function derives shared secrets using the provided key pairs. |
391 | | * It calls `do_derive()` to compute shared secrets for both participants |
392 | | * and frees the allocated memory for the shared secrets. |
393 | | * |
394 | | * @param[out] buf Unused output buffer (reserved for future use). |
395 | | * @param[out] len Unused output length (reserved for future use). |
396 | | * @param[in] key1 First key (typically Alice's key). |
397 | | * @param[in] key2 Second key (typically Bob's key). |
398 | | * @param[out] out1 Unused output parameter (reserved for future use). |
399 | | * @param[out] out2 Unused output parameter (reserved for future use). |
400 | | * |
401 | | * @note Currently, this function does not validate whether the derived |
402 | | * shared secrets match. A check should be added when ML-KEM |
403 | | * supports this. |
404 | | */ |
405 | | static void mlkem_kex(uint8_t **buf, size_t *len, void *key1, void *key2, |
406 | | void **out1, void **out2) |
407 | 10 | { |
408 | 10 | EVP_PKEY *alice = (EVP_PKEY *)key1; |
409 | 10 | EVP_PKEY *bob = (EVP_PKEY *)key2; |
410 | 10 | size_t boblen, alicelen; |
411 | 10 | uint8_t *bobshare = NULL; |
412 | 10 | uint8_t *aliceshare = NULL; |
413 | | |
414 | 10 | do_derive(alice, bob, &aliceshare, &alicelen); |
415 | 10 | do_derive(bob, alice, &bobshare, &boblen); |
416 | | |
417 | | /* |
418 | | * TODO add check of shared secrets here when ML-KEM supports this |
419 | | */ |
420 | 10 | OPENSSL_free(bobshare); |
421 | 10 | OPENSSL_free(aliceshare); |
422 | 10 | } |
423 | | |
424 | | /** |
425 | | * @brief Exports and imports an ML-KEM key. |
426 | | * |
427 | | * This function extracts key material from the given key (`key1`), |
428 | | * exports it as parameters, and then attempts to reconstruct a new |
429 | | * key from those parameters. It uses OpenSSL's `EVP_PKEY_todata()` |
430 | | * and `EVP_PKEY_fromdata()` functions for this process. |
431 | | * |
432 | | * @param[out] buf Unused output buffer (reserved for future use). |
433 | | * @param[out] len Unused output length (reserved for future use). |
434 | | * @param[in] key1 The key to be exported and imported. |
435 | | * @param[in] key2 Unused input key (reserved for future use). |
436 | | * @param[out] out1 Unused output parameter (reserved for future use). |
437 | | * @param[out] out2 Unused output parameter (reserved for future use). |
438 | | * |
439 | | * @note If any step in the export-import process fails, the function |
440 | | * logs an error and cleans up allocated resources. |
441 | | */ |
442 | | static void mlkem_export_import(uint8_t **buf, size_t *len, void *key1, |
443 | | void *key2, void **out1, void **out2) |
444 | 3 | { |
445 | 3 | EVP_PKEY *alice = (EVP_PKEY *)key1; |
446 | 3 | EVP_PKEY *new = NULL; |
447 | 3 | EVP_PKEY_CTX *ctx = NULL; |
448 | 3 | OSSL_PARAM *params = NULL; |
449 | | |
450 | 3 | if (!EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, ¶ms)) { |
451 | 0 | fprintf(stderr, "Failed todata\n"); |
452 | 0 | goto err; |
453 | 0 | } |
454 | | |
455 | 3 | ctx = EVP_PKEY_CTX_new_from_pkey(NULL, alice, NULL); |
456 | 3 | if (ctx == NULL) { |
457 | 0 | fprintf(stderr, "Failed new ctx\n"); |
458 | 0 | goto err; |
459 | 0 | } |
460 | | |
461 | 3 | if (!EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params)) { |
462 | 0 | fprintf(stderr, "Failed fromdata\n"); |
463 | 0 | goto err; |
464 | 0 | } |
465 | | |
466 | 3 | err: |
467 | 3 | EVP_PKEY_CTX_free(ctx); |
468 | 3 | EVP_PKEY_free(new); |
469 | 3 | OSSL_PARAM_free(params); |
470 | 3 | } |
471 | | |
472 | | /** |
473 | | * @brief Compares two cryptographic keys and performs equality checks. |
474 | | * |
475 | | * This function takes in two cryptographic keys, casts them to `EVP_PKEY` |
476 | | * structures, and checks their equality using `EVP_PKEY_eq()`. The purpose |
477 | | * of `buf`, `len`, `out1`, and `out2` parameters is not clear from the |
478 | | * function's current implementation. |
479 | | * |
480 | | * @param buf Unused parameter (purpose unclear). |
481 | | * @param len Unused parameter (purpose unclear). |
482 | | * @param key1 First key, expected to be an `EVP_PKEY *`. |
483 | | * @param key2 Second key, expected to be an `EVP_PKEY *`. |
484 | | * @param out1 Unused parameter (purpose unclear). |
485 | | * @param out2 Unused parameter (purpose unclear). |
486 | | */ |
487 | | static void mlkem_compare(uint8_t **buf, size_t *len, void *key1, |
488 | | void *key2, void **out1, void **out2) |
489 | 19 | { |
490 | 19 | EVP_PKEY *alice = (EVP_PKEY *)key1; |
491 | 19 | EVP_PKEY *bob = (EVP_PKEY *)key2; |
492 | | |
493 | 19 | EVP_PKEY_eq(alice, alice); |
494 | 19 | EVP_PKEY_eq(alice, bob); |
495 | 19 | } |
496 | | |
497 | | /** |
498 | | * @brief Frees allocated ML-KEM keys. |
499 | | * |
500 | | * This function releases memory associated with up to four EVP_PKEY |
501 | | * objects by calling `EVP_PKEY_free()` on each provided key. |
502 | | * |
503 | | * @param key1 Pointer to the first key to be freed. |
504 | | * @param key2 Pointer to the second key to be freed. |
505 | | * @param key3 Pointer to the third key to be freed. |
506 | | * @param key4 Pointer to the fourth key to be freed. |
507 | | * |
508 | | * @note This function assumes that each key is either a valid EVP_PKEY |
509 | | * object or NULL. Passing NULL is safe and has no effect. |
510 | | */ |
511 | | static void cleanup_mlkem_keys(void *key1, void *key2, |
512 | | void *key3, void *key4) |
513 | 164 | { |
514 | 164 | EVP_PKEY_free((EVP_PKEY *)key1); |
515 | 164 | EVP_PKEY_free((EVP_PKEY *)key2); |
516 | 164 | EVP_PKEY_free((EVP_PKEY *)key3); |
517 | 164 | EVP_PKEY_free((EVP_PKEY *)key4); |
518 | 164 | return; |
519 | 164 | } |
520 | | |
521 | | /** |
522 | | * @brief Represents an operation table entry for cryptographic operations. |
523 | | * |
524 | | * This structure defines a table entry containing function pointers for |
525 | | * setting up, executing, and cleaning up cryptographic operations, along |
526 | | * with associated metadata such as a name and description. |
527 | | * |
528 | | * @struct op_table_entry |
529 | | */ |
530 | | struct op_table_entry { |
531 | | /** Name of the operation. */ |
532 | | char *name; |
533 | | |
534 | | /** Description of the operation. */ |
535 | | char *desc; |
536 | | |
537 | | /** |
538 | | * @brief Function pointer for setting up the operation. |
539 | | * |
540 | | * @param buf Pointer to the buffer pointer; may be updated. |
541 | | * @param len Pointer to the remaining buffer size; may be updated. |
542 | | * @param out1 Pointer to store the first output of the setup function. |
543 | | * @param out2 Pointer to store the second output of the setup function. |
544 | | */ |
545 | | void (*setup)(uint8_t **buf, size_t *len, void **out1, void **out2); |
546 | | |
547 | | /** |
548 | | * @brief Function pointer for executing the operation. |
549 | | * |
550 | | * @param buf Pointer to the buffer pointer; may be updated. |
551 | | * @param len Pointer to the remaining buffer size; may be updated. |
552 | | * @param in1 First input parameter for the operation. |
553 | | * @param in2 Second input parameter for the operation. |
554 | | * @param out1 Pointer to store the first output of the operation. |
555 | | * @param out2 Pointer to store the second output of the operation. |
556 | | */ |
557 | | void (*doit)(uint8_t **buf, size_t *len, void *in1, void *in2, |
558 | | void **out1, void **out2); |
559 | | |
560 | | /** |
561 | | * @brief Function pointer for cleaning up after the operation. |
562 | | * |
563 | | * @param in1 First input parameter to be cleaned up. |
564 | | * @param in2 Second input parameter to be cleaned up. |
565 | | * @param out1 First output parameter to be cleaned up. |
566 | | * @param out2 Second output parameter to be cleaned up. |
567 | | */ |
568 | | void (*cleanup)(void *in1, void *in2, void *out1, void *out2); |
569 | | }; |
570 | | |
571 | | static struct op_table_entry ops[] = { |
572 | | { |
573 | | "Generate ML-KEM raw key", |
574 | | "Try generate a raw keypair using random data. Usually fails", |
575 | | create_mlkem_raw_key, |
576 | | NULL, |
577 | | cleanup_mlkem_keys |
578 | | }, { |
579 | | "Generate ML-KEM keypair, using EVP_PKEY_keygen", |
580 | | "Generates a real ML-KEM keypair, should always work", |
581 | | keygen_mlkem_real_key, |
582 | | NULL, |
583 | | cleanup_mlkem_keys |
584 | | }, { |
585 | | "Do a key encap/decap operation on a key", |
586 | | "Generate key, encap it, decap it and compare, should work", |
587 | | keygen_mlkem_real_key, |
588 | | mlkem_encap_decap, |
589 | | cleanup_mlkem_keys |
590 | | }, { |
591 | | "Do a key exchange operation on two keys", |
592 | | "Gen keys, do a key exchange both ways and compare", |
593 | | keygen_mlkem_real_key, |
594 | | mlkem_kex, |
595 | | cleanup_mlkem_keys |
596 | | }, { |
597 | | "Do an export/import of key data", |
598 | | "Exercise EVP_PKEY_todata/fromdata", |
599 | | keygen_mlkem_real_key, |
600 | | mlkem_export_import, |
601 | | cleanup_mlkem_keys |
602 | | }, { |
603 | | "Compare keys for equality", |
604 | | "Compare key1/key1 and key1/key2 for equality", |
605 | | keygen_mlkem_real_key, |
606 | | mlkem_compare, |
607 | | cleanup_mlkem_keys |
608 | | } |
609 | | }; |
610 | | |
611 | | int FuzzerInitialize(int *argc, char ***argv) |
612 | 108 | { |
613 | 108 | return 0; |
614 | 108 | } |
615 | | |
616 | | /** |
617 | | * @brief Processes a fuzzing input by selecting and executing an operation. |
618 | | * |
619 | | * This function interprets the first byte of the input buffer to determine |
620 | | * an operation to execute. It then follows a setup, execution, and cleanup |
621 | | * sequence based on the selected operation. |
622 | | * |
623 | | * @param buf Pointer to the input buffer. |
624 | | * @param len Length of the input buffer. |
625 | | * |
626 | | * @return 0 on successful execution, -1 if the input is too short. |
627 | | * |
628 | | * @note The function requires at least 32 bytes in the buffer to proceed. |
629 | | * It utilizes the `ops` operation table to dynamically determine and |
630 | | * execute the selected operation. |
631 | | */ |
632 | | int FuzzerTestOneInput(const uint8_t *buf, size_t len) |
633 | 909 | { |
634 | 909 | uint8_t operation; |
635 | 909 | uint8_t *buffer_cursor; |
636 | 909 | void *in1 = NULL, *in2 = NULL; |
637 | 909 | void *out1 = NULL, *out2 = NULL; |
638 | | |
639 | 909 | if (len < 32) |
640 | 24 | return -1; |
641 | | /* |
642 | | * Get the first byte of the buffer to tell us what operation |
643 | | * to preform |
644 | | */ |
645 | 885 | buffer_cursor = consume_uint8t(buf, &len, &operation); |
646 | 885 | if (buffer_cursor == NULL) |
647 | 0 | return -1; |
648 | | |
649 | | /* |
650 | | * Adjust for operational array size |
651 | | */ |
652 | 885 | operation %= OSSL_NELEM(ops); |
653 | | |
654 | | /* |
655 | | * And run our setup/doit/cleanup sequence |
656 | | */ |
657 | 885 | if (ops[operation].setup != NULL) |
658 | 552 | ops[operation].setup(&buffer_cursor, &len, &in1, &in2); |
659 | 885 | if (ops[operation].doit != NULL) |
660 | 690 | ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); |
661 | 885 | if (ops[operation].cleanup != NULL) |
662 | 885 | ops[operation].cleanup(in1, in2, out1, out2); |
663 | | |
664 | 885 | return 0; |
665 | 885 | } |
666 | | |
667 | | void FuzzerCleanup(void) |
668 | 0 | { |
669 | 0 | OPENSSL_cleanup(); |
670 | 0 | } |