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 { 36 if (*len < sizeof(uint8_t)) 37 return NULL; 38 *val = *buf; 39 *len -= sizeof(uint8_t); 40 return (uint8_t *)buf + 1; 41 } 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 { 58 if (*len < sizeof(size_t)) 59 return NULL; 60 *val = *buf; 61 *len -= sizeof(size_t); 62 return (uint8_t *)buf + sizeof(size_t); 63 } 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 { 85 uint16_t keysize; 86 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 *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf); 93 *len -= sizeof(uint16_t); 94 95 if (*buf == NULL) 96 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 if (only_valid) 104 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 switch (keysize % modulus) { 111 case 0: 112 *keytype = "ML-DSA-44"; 113 *keylen = ML_DSA_44_PUB_LEN; 114 break; 115 case 1: 116 *keytype = "ML-DSA-65"; 117 *keylen = ML_DSA_65_PUB_LEN; 118 break; 119 case 2: 120 *keytype = "ML-DSA-87"; 121 *keylen = ML_DSA_87_PUB_LEN; 122 break; 123 case 3: 124 /* select invalid alg */ 125 *keytype = "ML-DSA-33"; 126 *keylen = 33; 127 break; 128 case 4: 129 /* Select valid alg, but bogus size */ 130 *keytype = "ML-DSA-87"; 131 *buf = (uint8_t *)OPENSSL_load_u16_le(&keysize, *buf); 132 *len -= sizeof(uint16_t); 133 *keylen = (size_t)keysize; 134 *keylen %= ML_DSA_87_PUB_LEN; /* size to our key buffer */ 135 break; 136 default: 137 *keytype = NULL; 138 *keylen = 0; 139 break; 140 } 141 return 1; 142 } 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 { 162 EVP_PKEY *pubkey; 163 char *keytype = NULL; 164 size_t keylen = 0; 165 /* MAX_ML_DSA_PRIV_LEN is longer of that and ML_DSA_87_PUB_LEN */ 166 uint8_t key[MAX_ML_DSA_PRIV_LEN]; 167 int pub = 0; 168 169 if (!select_keytype_and_size(buf, len, &keytype, &keylen, 0)) 170 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 if ((*buf)[0] & 0x1) { 179 pub = 1; 180 } else { 181 switch (keylen) { 182 case (ML_DSA_44_PUB_LEN): 183 keylen = ML_DSA_44_PRIV_LEN; 184 break; 185 case (ML_DSA_65_PUB_LEN): 186 keylen = ML_DSA_65_PRIV_LEN; 187 break; 188 case (ML_DSA_87_PUB_LEN): 189 keylen = ML_DSA_87_PRIV_LEN; 190 break; 191 default: 192 return; 193 } 194 } 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 if (!RAND_bytes(key, keylen)) 202 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 if (pub == 1) 211 pubkey = EVP_PKEY_new_raw_public_key_ex(NULL, keytype, NULL, key, keylen); 212 else 213 pubkey = EVP_PKEY_new_raw_private_key_ex(NULL, keytype, NULL, key, keylen); 214 215 *key1 = pubkey; 216 return; 217 } 218 219 static int keygen_ml_dsa_real_key_helper(uint8_t **buf, size_t *len, 220 EVP_PKEY **key) 221 { 222 char *keytype = NULL; 223 size_t keylen = 0; 224 EVP_PKEY_CTX *ctx = NULL; 225 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 if (!select_keytype_and_size(buf, len, &keytype, &keylen, 1)) 233 goto err; 234 235 ctx = EVP_PKEY_CTX_new_from_name(NULL, keytype, NULL); 236 if (!ctx) { 237 fprintf(stderr, "Failed to generate ctx\n"); 238 goto err; 239 } 240 241 if (!EVP_PKEY_keygen_init(ctx)) { 242 fprintf(stderr, "Failed to init keygen ctx\n"); 243 goto err; 244 } 245 246 *key = EVP_PKEY_new(); 247 if (*key == NULL) 248 goto err; 249 250 if (!EVP_PKEY_generate(ctx, key)) { 251 fprintf(stderr, "Failed to generate new real key\n"); 252 goto err; 253 } 254 255 ret = 1; 256 err: 257 EVP_PKEY_CTX_free(ctx); 258 return ret; 259 } 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 { 279 if (!keygen_ml_dsa_real_key_helper(buf, len, (EVP_PKEY **)key1) 280 || !keygen_ml_dsa_real_key_helper(buf, len, (EVP_PKEY **)key2)) 281 fprintf(stderr, "Unable to generate valid keys"); 282 } 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 { 301 EVP_PKEY *key = (EVP_PKEY *)key1; 302 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); 303 EVP_SIGNATURE *sig_alg = NULL; 304 unsigned char *sig = NULL; 305 size_t sig_len = 0, tbslen; 306 unsigned char *tbs = NULL; 307 /* Ownership of alg is retained by the pkey object */ 308 const char *alg = EVP_PKEY_get0_type_name(key); 309 const OSSL_PARAM params[] = { 310 OSSL_PARAM_octet_string("context-string", 311 (unsigned char *)"A context string", 16), 312 OSSL_PARAM_END 313 }; 314 315 if (!consume_size_t(*buf, len, &tbslen)) { 316 fprintf(stderr, "Failed to set tbslen"); 317 goto err; 318 } 319 /* Keep tbslen within a reasonable value we can malloc */ 320 tbslen = (tbslen % 2048) + 1; 321 322 if ((tbs = OPENSSL_malloc(tbslen)) == NULL 323 || ctx == NULL || alg == NULL 324 || !RAND_bytes_ex(NULL, tbs, tbslen, 0)) { 325 fprintf(stderr, "Failed basic initialization\n"); 326 goto err; 327 } 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 if ((sig_alg = EVP_SIGNATURE_fetch(NULL, alg, NULL)) == NULL 336 || EVP_PKEY_sign_message_init(ctx, sig_alg, params) <= 0 337 || EVP_PKEY_sign(ctx, NULL, &sig_len, tbs, tbslen) <= 0 338 || (sig = OPENSSL_zalloc(sig_len)) == NULL 339 || EVP_PKEY_sign(ctx, sig, &sig_len, tbs, tbslen) <= 0) { 340 fprintf(stderr, "Failed to sign message\n"); 341 goto err; 342 } 343 344 /* Verify signature */ 345 EVP_PKEY_CTX_free(ctx); 346 ctx = NULL; 347 348 if ((ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL)) == NULL 349 || EVP_PKEY_verify_message_init(ctx, sig_alg, params) <= 0 350 || EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbslen) <= 0) { 351 fprintf(stderr, "Failed to verify message\n"); 352 goto err; 353 } 354 355 err: 356 OPENSSL_free(tbs); 357 EVP_PKEY_CTX_free(ctx); 358 EVP_SIGNATURE_free(sig_alg); 359 OPENSSL_free(sig); 360 return; 361 } 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 { 380 EVP_PKEY *key = (EVP_PKEY *)key1; 381 EVP_MD_CTX *ctx = EVP_MD_CTX_new(); 382 EVP_SIGNATURE *sig_alg = NULL; 383 unsigned char *sig = NULL; 384 size_t sig_len, tbslen; 385 unsigned char *tbs = NULL; 386 const OSSL_PARAM params[] = { 387 OSSL_PARAM_octet_string("context-string", 388 (unsigned char *)"A context string", 16), 389 OSSL_PARAM_END 390 }; 391 392 if (!consume_size_t(*buf, len, &tbslen)) { 393 fprintf(stderr, "Failed to set tbslen"); 394 goto err; 395 } 396 /* Keep tbslen within a reasonable value we can malloc */ 397 tbslen = (tbslen % 2048) + 1; 398 399 if ((tbs = OPENSSL_malloc(tbslen)) == NULL 400 || ctx == NULL 401 || !RAND_bytes_ex(NULL, tbs, tbslen, 0)) { 402 fprintf(stderr, "Failed basic initialization\n"); 403 goto err; 404 } 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 if (!EVP_DigestSignInit_ex(ctx, NULL, NULL, NULL, "?fips=true", key, params) 413 || EVP_DigestSign(ctx, NULL, &sig_len, tbs, tbslen) <= 0 414 || (sig = OPENSSL_malloc(sig_len)) == NULL 415 || EVP_DigestSign(ctx, sig, &sig_len, tbs, tbslen) <= 0) { 416 fprintf(stderr, "Failed to sign digest with EVP_DigestSign\n"); 417 goto err; 418 } 419 420 /* Verify signature */ 421 EVP_MD_CTX_free(ctx); 422 ctx = NULL; 423 424 if ((ctx = EVP_MD_CTX_new()) == NULL 425 || EVP_DigestVerifyInit_ex(ctx, NULL, NULL, NULL, "?fips=true", key, 426 params) <= 0 427 || EVP_DigestVerify(ctx, sig, sig_len, tbs, tbslen) <= 0) { 428 fprintf(stderr, "Failed to verify digest with EVP_DigestVerify\n"); 429 goto err; 430 } 431 432 err: 433 OPENSSL_free(tbs); 434 EVP_MD_CTX_free(ctx); 435 EVP_SIGNATURE_free(sig_alg); 436 OPENSSL_free(sig); 437 return; 438 } 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 { 461 EVP_PKEY *alice = (EVP_PKEY *)key1; 462 EVP_PKEY *new_key = NULL; 463 EVP_PKEY_CTX *ctx = NULL; 464 OSSL_PARAM *params = NULL; 465 466 if (!EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, ¶ms)) { 467 fprintf(stderr, "Failed todata\n"); 468 goto err; 469 } 470 471 ctx = EVP_PKEY_CTX_new_from_pkey(NULL, alice, NULL); 472 if (ctx == NULL) { 473 fprintf(stderr, "Failed new ctx\n"); 474 goto err; 475 } 476 477 if (!EVP_PKEY_fromdata(ctx, &new_key, EVP_PKEY_KEYPAIR, params)) { 478 fprintf(stderr, "Failed fromdata\n"); 479 goto err; 480 } 481 482 err: 483 EVP_PKEY_CTX_free(ctx); 484 EVP_PKEY_free(new_key); 485 OSSL_PARAM_free(params); 486 } 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 { 506 EVP_PKEY *alice = (EVP_PKEY *)key1; 507 EVP_PKEY *bob = (EVP_PKEY *)key2; 508 509 EVP_PKEY_eq(alice, alice); 510 EVP_PKEY_eq(alice, bob); 511 } 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 { 530 EVP_PKEY_free((EVP_PKEY *)key1); 531 EVP_PKEY_free((EVP_PKEY *)key2); 532 EVP_PKEY_free((EVP_PKEY *)key3); 533 EVP_PKEY_free((EVP_PKEY *)key4); 534 } 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 { 628 return 0; 629 } 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 { 649 uint8_t operation; 650 uint8_t *buffer_cursor; 651 void *in1 = NULL, *in2 = NULL; 652 void *out1 = NULL, *out2 = NULL; 653 654 if (len < 32) 655 return -1; 656 657 /* Get the first byte of the buffer to tell us what operation to perform */ 658 buffer_cursor = consume_uint8_t(buf, &len, &operation); 659 if (buffer_cursor == NULL) 660 return -1; 661 662 /* Adjust for operational array size */ 663 operation %= OSSL_NELEM(ops); 664 665 /* And run our setup/doit/cleanup sequence */ 666 if (ops[operation].setup != NULL) 667 ops[operation].setup(&buffer_cursor, &len, &in1, &in2); 668 if (ops[operation].doit != NULL) 669 ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); 670 if (ops[operation].cleanup != NULL) 671 ops[operation].cleanup(in1, in2, out1, out2); 672 673 return 0; 674 } 675 676 void FuzzerCleanup(void) 677 { 678 OPENSSL_cleanup(); 679 } 680