Coverage Report

Created: 2025-08-28 07:07

/src/openssl35/fuzz/ml-dsa.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
/* 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
586
{
36
586
    if (*len < sizeof(uint8_t))
37
0
        return NULL;
38
586
    *val = *buf;
39
586
    *len -= sizeof(uint8_t);
40
586
    return (uint8_t *)buf + 1;
41
586
}
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
399
{
58
399
    if (*len < sizeof(size_t))
59
0
        return NULL;
60
399
    *val = *buf;
61
399
    *len -= sizeof(size_t);
62
399
    return (uint8_t *)buf + sizeof(size_t);
63
399
}
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
1.03k
{
85
1.03k
    uint16_t keysize;
86
1.03k
    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
1.03k
    *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf);
93
1.03k
    *len -= sizeof(uint16_t);
94
95
1.03k
    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
1.03k
    if (only_valid)
104
898
        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
1.03k
    switch (keysize % modulus) {
111
338
    case 0:
112
338
        *keytype = "ML-DSA-44";
113
338
        *keylen = ML_DSA_44_PUB_LEN;
114
338
        break;
115
314
    case 1:
116
314
        *keytype = "ML-DSA-65";
117
314
        *keylen = ML_DSA_65_PUB_LEN;
118
314
        break;
119
288
    case 2:
120
288
        *keytype = "ML-DSA-87";
121
288
        *keylen = ML_DSA_87_PUB_LEN;
122
288
        break;
123
2
    case 3:
124
        /* select invalid alg */
125
2
        *keytype = "ML-DSA-33";
126
2
        *keylen = 33;
127
2
        break;
128
91
    case 4:
129
        /* Select valid alg, but bogus size */
130
91
        *keytype = "ML-DSA-87";
131
91
        *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf);
132
91
        *len -= sizeof(uint16_t);
133
91
        *keylen = (size_t)keysize;
134
91
        *keylen %= ML_DSA_87_PUB_LEN; /* size to our key buffer */
135
91
        break;
136
2
    default:
137
2
        *keytype = NULL;
138
2
        *keylen = 0;
139
2
        break;
140
1.03k
    }
141
1.03k
    return 1;
142
1.03k
}
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
137
{
162
137
    EVP_PKEY *pubkey;
163
137
    char *keytype = NULL;
164
137
    size_t keylen = 0;
165
    /* MAX_ML_DSA_PRIV_LEN is longer of that and ML_DSA_87_PUB_LEN */
166
137
    uint8_t key[MAX_ML_DSA_PRIV_LEN];
167
137
    int pub = 0;
168
169
137
    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
137
    if ((*buf)[0] & 0x1) {
179
41
        pub = 1;
180
96
    } else {
181
96
        switch (keylen) {
182
10
        case (ML_DSA_44_PUB_LEN):
183
10
            keylen = ML_DSA_44_PRIV_LEN;
184
10
            break;
185
23
        case (ML_DSA_65_PUB_LEN):
186
23
            keylen = ML_DSA_65_PRIV_LEN;
187
23
            break;
188
3
        case (ML_DSA_87_PUB_LEN):
189
3
            keylen = ML_DSA_87_PRIV_LEN;
190
3
            break;
191
60
        default:
192
60
            return;
193
96
        }
194
96
    }
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
77
    if (!RAND_bytes(key, 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
77
    if (pub == 1)
211
41
        pubkey = EVP_PKEY_new_raw_public_key_ex(NULL, keytype, NULL, key, keylen);
212
36
    else
213
36
        pubkey = EVP_PKEY_new_raw_private_key_ex(NULL, keytype, NULL, key, keylen);
214
215
77
    *key1 = pubkey;
216
77
    return;
217
77
}
218
219
static int keygen_ml_dsa_real_key_helper(uint8_t **buf, size_t *len,
220
                                         EVP_PKEY **key)
221
898
{
222
898
    char *keytype = NULL;
223
898
    size_t keylen = 0;
224
898
    EVP_PKEY_CTX *ctx = NULL;
225
898
    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
898
    if (!select_keytype_and_size(buf, len, &keytype, &keylen, 1))
233
0
        goto err;
234
235
898
    ctx = EVP_PKEY_CTX_new_from_name(NULL, keytype, NULL);
236
898
    if (!ctx) {
237
0
        fprintf(stderr, "Failed to generate ctx\n");
238
0
        goto err;
239
0
    }
240
241
898
    if (!EVP_PKEY_keygen_init(ctx)) {
242
0
        fprintf(stderr, "Failed to init keygen ctx\n");
243
0
        goto err;
244
0
    }
245
246
898
    *key = EVP_PKEY_new();
247
898
    if (*key == NULL)
248
0
        goto err;
249
250
898
    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
898
    ret = 1;
256
898
err:
257
898
    EVP_PKEY_CTX_free(ctx);
258
898
    return ret;
259
898
}
260
261
/**
262
 * @brief Generates a valid ML-DSA key using OpenSSL.
263
 *
264
 * This function selects a valid ML-DSA key type and size from the buffer,
265
 * initializes an OpenSSL EVP_PKEY context, and generates a cryptographic key
266
 * accordingly.
267
 *
268
 * @param buf    Pointer to the buffer pointer; updated after reading.
269
 * @param len    Pointer to the remaining buffer size; updated accordingly.
270
 * @param key1   Pointer to store the first generated EVP_PKEY key.
271
 * @param key2   Pointer to store the second generated EVP_PKEY key.
272
 *
273
 * @note The generated key is allocated using OpenSSL's EVP_PKEY functions
274
 *       and should be freed using `EVP_PKEY_free()`.
275
 */
276
static void keygen_ml_dsa_real_key(uint8_t **buf, size_t *len,
277
                                   void **key1, void **key2)
278
449
{
279
449
    if (!keygen_ml_dsa_real_key_helper(buf, len, (EVP_PKEY **)key1)
280
449
        || !keygen_ml_dsa_real_key_helper(buf, len, (EVP_PKEY **)key2))
281
0
        fprintf(stderr, "Unable to generate valid keys");
282
449
}
283
284
/**
285
 * @brief Performs key sign and verify using an EVP_PKEY.
286
 *
287
 * This function generates a random key, signs random data using the provided
288
 * public key, then verifies it. It makes use of OpenSSL's EVP_PKEY API for
289
 * encryption and decryption.
290
 *
291
 * @param[out] buf   Unused output buffer (reserved for future use).
292
 * @param[out] len   Unused length parameter (reserved for future use).
293
 * @param[in]  key1  Pointer to an EVP_PKEY structure used for key operations.
294
 * @param[in]  in2   Unused input parameter (reserved for future use).
295
 * @param[out] out1  Unused output parameter (reserved for future use).
296
 * @param[out] out2  Unused output parameter (reserved for future use).
297
 */
298
static void ml_dsa_sign_verify(uint8_t **buf, size_t *len, void *key1,
299
                               void *in2, void **out1, void **out2)
300
259
{
301
259
    EVP_PKEY *key = (EVP_PKEY *)key1;
302
259
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL);
303
259
    EVP_SIGNATURE *sig_alg = NULL;
304
259
    unsigned char *sig = NULL;
305
259
    size_t sig_len = 0, tbslen;
306
259
    unsigned char *tbs = NULL;
307
    /* Ownership of alg is retained by the pkey object */
308
259
    const char *alg = EVP_PKEY_get0_type_name(key);
309
259
    const OSSL_PARAM params[] = {
310
259
        OSSL_PARAM_octet_string("context-string",
311
259
                                (unsigned char *)"A context string", 16),
312
259
        OSSL_PARAM_END
313
259
    };
314
315
259
    if (!consume_size_t(*buf, len, &tbslen)) {
316
0
        fprintf(stderr, "Failed to set tbslen");
317
0
        goto err;
318
0
    }
319
    /* Keep tbslen within a reasonable value we can malloc */
320
259
    tbslen = (tbslen % 2048) + 1;
321
322
259
    if ((tbs = OPENSSL_malloc(tbslen)) == NULL
323
259
        || ctx == NULL || alg == NULL
324
259
        || !RAND_bytes_ex(NULL, tbs, tbslen, 0)) {
325
0
        fprintf(stderr, "Failed basic initialization\n");
326
0
        goto err;
327
0
    }
328
329
    /*
330
     * Because ML-DSA is fundamentally a one-shot algorithm like "pure" Ed25519
331
     * and Ed448, we don't have any immediate plans to implement intermediate
332
     * sign/verify functions. Therefore, we only test the one-shot functions.
333
     */
334
335
259
    if ((sig_alg = EVP_SIGNATURE_fetch(NULL, alg, NULL)) == NULL
336
259
        || EVP_PKEY_sign_message_init(ctx, sig_alg, params) <= 0
337
259
        || EVP_PKEY_sign(ctx, NULL, &sig_len, tbs, tbslen) <= 0
338
259
        || (sig = OPENSSL_zalloc(sig_len)) == NULL
339
259
        || EVP_PKEY_sign(ctx, sig, &sig_len, tbs, tbslen) <= 0) {
340
0
        fprintf(stderr, "Failed to sign message\n");
341
0
        goto err;
342
0
    }
343
344
    /* Verify signature */
345
259
    EVP_PKEY_CTX_free(ctx);
346
259
    ctx = NULL;
347
348
259
    if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL)) == NULL
349
259
        || EVP_PKEY_verify_message_init(ctx, sig_alg, params) <= 0
350
259
        || EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbslen) <= 0) {
351
0
        fprintf(stderr, "Failed to verify message\n");
352
0
        goto err;
353
0
    }
354
355
259
err:
356
259
    OPENSSL_free(tbs);
357
259
    EVP_PKEY_CTX_free(ctx);
358
259
    EVP_SIGNATURE_free(sig_alg);
359
259
    OPENSSL_free(sig);
360
259
    return;
361
259
}
362
363
/**
364
 * @brief Performs key sign and verify using an EVP_PKEY.
365
 *
366
 * This function generates a random key, signs random data using the provided
367
 * public key, then verifies it. It makes use of OpenSSL's EVP_PKEY API for
368
 * encryption and decryption.
369
 *
370
 * @param[out] buf   Unused output buffer (reserved for future use).
371
 * @param[out] len   Unused length parameter (reserved for future use).
372
 * @param[in]  key1  Pointer to an EVP_PKEY structure used for key operations.
373
 * @param[in]  in2   Unused input parameter (reserved for future use).
374
 * @param[out] out1  Unused output parameter (reserved for future use).
375
 * @param[out] out2  Unused output parameter (reserved for future use).
376
 */
377
static void ml_dsa_digest_sign_verify(uint8_t **buf, size_t *len, void *key1,
378
                                      void *in2, void **out1, void **out2)
379
140
{
380
140
    EVP_PKEY *key = (EVP_PKEY *)key1;
381
140
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
382
140
    EVP_SIGNATURE *sig_alg = NULL;
383
140
    unsigned char *sig = NULL;
384
140
    size_t sig_len, tbslen;
385
140
    unsigned char *tbs = NULL;
386
140
    const OSSL_PARAM params[] = {
387
140
        OSSL_PARAM_octet_string("context-string",
388
140
                                (unsigned char *)"A context string", 16),
389
140
        OSSL_PARAM_END
390
140
    };
391
392
140
    if (!consume_size_t(*buf, len, &tbslen)) {
393
0
        fprintf(stderr, "Failed to set tbslen");
394
0
        goto err;
395
0
    }
396
    /* Keep tbslen within a reasonable value we can malloc */
397
140
    tbslen = (tbslen % 2048) + 1;
398
399
140
    if ((tbs = OPENSSL_malloc(tbslen)) == NULL
400
140
        || ctx == NULL
401
140
        || !RAND_bytes_ex(NULL, tbs, tbslen, 0)) {
402
0
        fprintf(stderr, "Failed basic initialization\n");
403
0
        goto err;
404
0
    }
405
406
    /*
407
     * Because ML-DSA is fundamentally a one-shot algorithm like "pure" Ed25519
408
     * and Ed448, we don't have any immediate plans to implement intermediate
409
     * sign/verify functions. Therefore, we only test the one-shot functions.
410
     */
411
412
140
    if (!EVP_DigestSignInit_ex(ctx, NULL, NULL, NULL, "?fips=true", key, params)
413
140
        || EVP_DigestSign(ctx, NULL, &sig_len, tbs, tbslen) <= 0
414
140
        || (sig = OPENSSL_malloc(sig_len)) == NULL
415
140
        || EVP_DigestSign(ctx, sig, &sig_len, tbs, tbslen) <= 0) {
416
0
        fprintf(stderr, "Failed to sign digest with EVP_DigestSign\n");
417
0
        goto err;
418
0
    }
419
420
    /* Verify signature */
421
140
    EVP_MD_CTX_free(ctx);
422
140
    ctx = NULL;
423
424
140
    if ((ctx = EVP_MD_CTX_new()) == NULL
425
140
        || EVP_DigestVerifyInit_ex(ctx, NULL, NULL, NULL, "?fips=true", key,
426
140
                                   params) <= 0
427
140
        || EVP_DigestVerify(ctx, sig, sig_len, tbs, tbslen) <= 0) {
428
0
        fprintf(stderr, "Failed to verify digest with EVP_DigestVerify\n");
429
0
        goto err;
430
0
    }
431
432
140
err:
433
140
    OPENSSL_free(tbs);
434
140
    EVP_MD_CTX_free(ctx);
435
140
    EVP_SIGNATURE_free(sig_alg);
436
140
    OPENSSL_free(sig);
437
140
    return;
438
140
}
439
440
/**
441
 * @brief Exports and imports an ML-DSA key.
442
 *
443
 * This function extracts key material from the given key (`key1`), exports it
444
 * as parameters, and then attempts to reconstruct a new key from those
445
 * parameters. It uses OpenSSL's `EVP_PKEY_todata()` and `EVP_PKEY_fromdata()`
446
 * functions for this process.
447
 *
448
 * @param[out] buf Unused output buffer (reserved for future use).
449
 * @param[out] len Unused output length (reserved for future use).
450
 * @param[in] key1 The key to be exported and imported.
451
 * @param[in] key2 Unused input key (reserved for future use).
452
 * @param[out] out1 Unused output parameter (reserved for future use).
453
 * @param[out] out2 Unused output parameter (reserved for future use).
454
 *
455
 * @note If any step in the export-import process fails, the function
456
 *       logs an error and cleans up allocated resources.
457
 */
458
static void ml_dsa_export_import(uint8_t **buf, size_t *len, void *key1,
459
                                 void *key2, void **out1, void **out2)
460
6
{
461
6
    EVP_PKEY *alice = (EVP_PKEY *)key1;
462
6
    EVP_PKEY *new_key = NULL;
463
6
    EVP_PKEY_CTX *ctx = NULL;
464
6
    OSSL_PARAM *params = NULL;
465
466
6
    if (!EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, &params)) {
467
0
        fprintf(stderr, "Failed todata\n");
468
0
        goto err;
469
0
    }
470
471
6
    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, alice, NULL);
472
6
    if (ctx == NULL) {
473
0
        fprintf(stderr, "Failed new ctx\n");
474
0
        goto err;
475
0
    }
476
477
6
    if (!EVP_PKEY_fromdata(ctx, &new_key, EVP_PKEY_KEYPAIR, params)) {
478
0
        fprintf(stderr, "Failed fromdata\n");
479
0
        goto err;
480
0
    }
481
482
6
err:
483
6
    EVP_PKEY_CTX_free(ctx);
484
6
    EVP_PKEY_free(new_key);
485
6
    OSSL_PARAM_free(params);
486
6
}
487
488
/**
489
 * @brief Compares two cryptographic keys and performs equality checks.
490
 *
491
 * This function takes in two cryptographic keys, casts them to `EVP_PKEY`
492
 * structures, and checks their equality using `EVP_PKEY_eq()`. The purpose of
493
 * `buf`, `len`, `out1`, and `out2` parameters is not clear from the function's
494
 * current implementation.
495
 *
496
 * @param buf   Unused parameter (purpose unclear).
497
 * @param len   Unused parameter (purpose unclear).
498
 * @param key1  First key, expected to be an `EVP_PKEY *`.
499
 * @param key2  Second key, expected to be an `EVP_PKEY *`.
500
 * @param out1  Unused parameter (purpose unclear).
501
 * @param out2  Unused parameter (purpose unclear).
502
 */
503
static void ml_dsa_compare(uint8_t **buf, size_t *len, void *key1,
504
                           void *key2, void **out1, void **out2)
505
35
{
506
35
    EVP_PKEY *alice = (EVP_PKEY *)key1;
507
35
    EVP_PKEY *bob = (EVP_PKEY *)key2;
508
509
35
    EVP_PKEY_eq(alice, alice);
510
35
    EVP_PKEY_eq(alice, bob);
511
35
}
512
513
/**
514
 * @brief Frees allocated ML-DSA keys.
515
 *
516
 * This function releases memory associated with up to four EVP_PKEY objects by
517
 * calling `EVP_PKEY_free()` on each provided key.
518
 *
519
 * @param key1 Pointer to the first key to be freed.
520
 * @param key2 Pointer to the second key to be freed.
521
 * @param key3 Pointer to the third key to be freed.
522
 * @param key4 Pointer to the fourth key to be freed.
523
 *
524
 * @note This function assumes that each key is either a valid EVP_PKEY
525
 *       object or NULL. Passing NULL is safe and has no effect.
526
 */
527
static void cleanup_ml_dsa_keys(void *key1, void *key2,
528
                                void *key3, void *key4)
529
586
{
530
586
    EVP_PKEY_free((EVP_PKEY *)key1);
531
586
    EVP_PKEY_free((EVP_PKEY *)key2);
532
586
    EVP_PKEY_free((EVP_PKEY *)key3);
533
586
    EVP_PKEY_free((EVP_PKEY *)key4);
534
586
}
535
536
/**
537
 * @brief Represents an operation table entry for cryptographic operations.
538
 *
539
 * This structure defines a table entry containing function pointers for setting
540
 * up, executing, and cleaning up cryptographic operations, along with
541
 * associated metadata such as a name and description.
542
 *
543
 * @struct op_table_entry
544
 */
545
struct op_table_entry {
546
    /** Name of the operation. */
547
    char *name;
548
549
    /** Description of the operation. */
550
    char *desc;
551
552
    /**
553
     * @brief Function pointer for setting up the operation.
554
     *
555
     * @param buf   Pointer to the buffer pointer; may be updated.
556
     * @param len   Pointer to the remaining buffer size; may be updated.
557
     * @param out1  Pointer to store the first output of the setup function.
558
     * @param out2  Pointer to store the second output of the setup function.
559
     */
560
    void (*setup)(uint8_t **buf, size_t *len, void **out1, void **out2);
561
562
    /**
563
     * @brief Function pointer for executing the operation.
564
     *
565
     * @param buf   Pointer to the buffer pointer; may be updated.
566
     * @param len   Pointer to the remaining buffer size; may be updated.
567
     * @param in1   First input parameter for the operation.
568
     * @param in2   Second input parameter for the operation.
569
     * @param out1  Pointer to store the first output of the operation.
570
     * @param out2  Pointer to store the second output of the operation.
571
     */
572
    void (*doit)(uint8_t **buf, size_t *len, void *in1, void *in2,
573
                 void **out1, void **out2);
574
575
    /**
576
     * @brief Function pointer for cleaning up after the operation.
577
     *
578
     * @param in1   First input parameter to be cleaned up.
579
     * @param in2   Second input parameter to be cleaned up.
580
     * @param out1  First output parameter to be cleaned up.
581
     * @param out2  Second output parameter to be cleaned up.
582
     */
583
    void (*cleanup)(void *in1, void *in2, void *out1, void *out2);
584
};
585
586
static struct op_table_entry ops[] = {
587
    {
588
        "Generate ML-DSA raw key",
589
        "Try generate a raw keypair using random data. Usually fails",
590
        create_ml_dsa_raw_key,
591
        NULL,
592
        cleanup_ml_dsa_keys
593
    }, {
594
        "Generate ML-DSA keypair, using EVP_PKEY_keygen",
595
        "Generates a real ML-DSA keypair, should always work",
596
        keygen_ml_dsa_real_key,
597
        NULL,
598
        cleanup_ml_dsa_keys
599
    }, {
600
        "Do a sign/verify operation on a key",
601
        "Generate key, sign random data, verify it, should work",
602
        keygen_ml_dsa_real_key,
603
        ml_dsa_sign_verify,
604
        cleanup_ml_dsa_keys
605
    }, {
606
        "Do a digest sign/verify operation on a key",
607
        "Generate key, digest sign random data, verify it, should work",
608
        keygen_ml_dsa_real_key,
609
        ml_dsa_digest_sign_verify,
610
        cleanup_ml_dsa_keys
611
    }, {
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
    }, {
618
        "Compare keys for equality",
619
        "Compare key1/key1 and key1/key2 for equality",
620
        keygen_ml_dsa_real_key,
621
        ml_dsa_compare,
622
        cleanup_ml_dsa_keys
623
    }
624
};
625
626
int FuzzerInitialize(int *argc, char ***argv)
627
200
{
628
200
    return 0;
629
200
}
630
631
/**
632
 * @brief Processes a fuzzing input by selecting and executing an operation.
633
 *
634
 * This function interprets the first byte of the input buffer to determine an
635
 * operation to execute. It then follows a setup, execution, and cleanup
636
 * sequence based on the selected operation.
637
 *
638
 * @param buf Pointer to the input buffer.
639
 * @param len Length of the input buffer.
640
 *
641
 * @return 0 on successful execution, -1 if the input is too short.
642
 *
643
 * @note The function requires at least 32 bytes in the buffer to proceed.
644
 *       It utilizes the `ops` operation table to dynamically determine and
645
 *       execute the selected operation.
646
 */
647
int FuzzerTestOneInput(const uint8_t *buf, size_t len)
648
1.77k
{
649
1.77k
    uint8_t operation;
650
1.77k
    uint8_t *buffer_cursor;
651
1.77k
    void *in1 = NULL, *in2 = NULL;
652
1.77k
    void *out1 = NULL, *out2 = NULL;
653
654
1.77k
    if (len < 32)
655
48
        return -1;
656
657
    /* Get the first byte of the buffer to tell us what operation to perform */
658
1.72k
    buffer_cursor = consume_uint8_t(buf, &len, &operation);
659
1.72k
    if (buffer_cursor == NULL)
660
0
        return -1;
661
662
    /* Adjust for operational array size */
663
1.72k
    operation %= OSSL_NELEM(ops);
664
665
    /* And run our setup/doit/cleanup sequence */
666
1.72k
    if (ops[operation].setup != NULL)
667
1.09k
        ops[operation].setup(&buffer_cursor, &len, &in1, &in2);
668
1.72k
    if (ops[operation].doit != NULL)
669
1.31k
        ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2);
670
1.72k
    if (ops[operation].cleanup != NULL)
671
1.72k
        ops[operation].cleanup(in1, in2, out1, out2);
672
673
1.72k
    return 0;
674
1.72k
}
675
676
void FuzzerCleanup(void)
677
0
{
678
0
    OPENSSL_cleanup();
679
0
}