1
0

ml-dsa.c 22 KB

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