Coverage Report

Created: 2026-09-12 06:55

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