1 /* 2 * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * https://www.openssl.org/source/license.html 8 * or in the file LICENSE in the source distribution. 9 */ 10 11 /* 12 * Test slh-dsa operation. 13 */ 14 #include <string.h> 15 #include <openssl/evp.h> 16 #include <openssl/err.h> 17 #include <openssl/rand.h> 18 #include <openssl/byteorder.h> 19 #include <openssl/core_names.h> 20 #include "crypto/slh_dsa.h" 21 #include "internal/nelem.h" 22 #include "fuzzer.h" 23 24 /** 25 * @brief Consumes an 8-bit unsigned integer from a buffer. 26 * 27 * This function extracts an 8-bit unsigned integer from the provided buffer, 28 * updates the buffer pointer, and adjusts the remaining length. 29 * 30 * @param buf Pointer to the input buffer. 31 * @param len Pointer to the size of the remaining buffer; updated after consumption. 32 * @param val Pointer to store the extracted 8-bit value. 33 * 34 * @return Pointer to the updated buffer position after reading the value, 35 * or NULL if the buffer does not contain enough data. 36 */ 37 static uint8_t *consume_uint8t(const uint8_t *buf, size_t *len, uint8_t *val) 38 { 39 if (*len < sizeof(uint8_t)) 40 return NULL; 41 *val = *buf; 42 *len -= sizeof(uint8_t); 43 return (uint8_t *)buf + 1; 44 } 45 46 /** 47 * @brief Generates a DSA key pair using OpenSSL EVP API. 48 * 49 * This function creates a DSA key pair based on the specified key size and 50 * parameters. It supports generating keys using explicit parameters if provided. 51 * 52 * @param name The name of the key type (e.g., "DSA"). 53 * @param keysize The desired key size in bits. 54 * @param params Optional OpenSSL parameters for key generation. 55 * @param param_broken A flag indicating if the parameters are broken. 56 * If true, key generation will fail. 57 * 58 * @return A pointer to the generated EVP_PKEY structure on success, 59 * or NULL on failure. 60 */ 61 static EVP_PKEY *slh_dsa_gen_key(const char *name, uint32_t keysize, 62 OSSL_PARAM params[], uint8_t *param_broken) 63 { 64 EVP_PKEY_CTX *ctx; 65 EVP_PKEY *new = NULL; 66 int rc; 67 68 ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); 69 OPENSSL_assert(ctx != NULL); 70 if (params != NULL) { 71 new = EVP_PKEY_new(); 72 OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); 73 if (*param_broken) { 74 rc = EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params); 75 OPENSSL_assert(rc == 0); 76 EVP_PKEY_free(new); 77 new = NULL; 78 } else { 79 OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); 80 } 81 goto out; 82 } 83 84 OPENSSL_assert(EVP_PKEY_keygen_init(ctx)); 85 OPENSSL_assert(EVP_PKEY_generate(ctx, &new)); 86 87 out: 88 EVP_PKEY_CTX_free(ctx); 89 return new; 90 } 91 92 /** 93 * @brief Selects a key type and determines the key size. 94 * 95 * This function maps a selector value to a specific SLH-DSA algorithm 96 * using a modulo operation. It then retrieves the corresponding 97 * algorithm name and assigns an appropriate key size based on the 98 * selected algorithm. 99 * 100 * @param selector A random selector value used to determine the key type. 101 * @param keysize Pointer to a variable where the determined key size 102 * (in bytes) will be stored. 103 * 104 * @return A pointer to a string containing the long name of the 105 * selected key type, or NULL if invalid. 106 */ 107 static const char *select_keytype(uint8_t selector, uint32_t *keysize) 108 { 109 unsigned int choice; 110 const char *name = NULL; 111 112 *keysize = 0; 113 /* 114 * There are 12 SLH-DSA algs with registered NIDS at the moment 115 * So use our random selector value to get one of them by computing 116 * its modulo 12 value and adding the offset of the first NID, 1460 117 * Then convert that to a long name 118 */ 119 choice = (selector % 12) + 1460; 120 121 name = OBJ_nid2ln(choice); 122 123 /* 124 * Select a keysize, values taken from 125 * man7/EVP_PKEY-SLH-DSA.pod 126 */ 127 switch (choice) { 128 case NID_SLH_DSA_SHA2_128s: 129 case NID_SLH_DSA_SHA2_128f: 130 case NID_SLH_DSA_SHAKE_128s: 131 case NID_SLH_DSA_SHAKE_128f: 132 *keysize = 16; 133 break; 134 case NID_SLH_DSA_SHA2_192s: 135 case NID_SLH_DSA_SHA2_192f: 136 case NID_SLH_DSA_SHAKE_192s: 137 case NID_SLH_DSA_SHAKE_192f: 138 *keysize = 24; 139 break; 140 case NID_SLH_DSA_SHA2_256s: 141 case NID_SLH_DSA_SHA2_256f: 142 case NID_SLH_DSA_SHAKE_256s: 143 case NID_SLH_DSA_SHAKE_256f: 144 *keysize = 32; 145 break; 146 default: 147 fprintf(stderr, "Selecting invalid key size\n"); 148 *keysize = 0; 149 break; 150 } 151 return name; 152 } 153 154 /** 155 * @brief Generates two SLH-DSA key pairs based on consumed selector values. 156 * 157 * This function extracts two selector values from the provided buffer, 158 * determines the corresponding key types and sizes, and generates two 159 * SLH-DSA key pairs. 160 * 161 * @param buf Pointer to a buffer containing selector values. The buffer 162 * pointer is updated as values are consumed. 163 * @param len Pointer to the remaining buffer length, updated as values 164 * are consumed. 165 * @param out1 Pointer to store the first generated key. 166 * @param out2 Pointer to store the second generated key. 167 */ 168 static void slh_dsa_gen_keys(uint8_t **buf, size_t *len, 169 void **out1, void **out2) 170 { 171 uint8_t selector = 0; 172 const char *keytype = NULL; 173 uint32_t keysize; 174 175 *buf = consume_uint8t(*buf, len, &selector); 176 keytype = select_keytype(selector, &keysize); 177 *out1 = (void *)slh_dsa_gen_key(keytype, keysize, NULL, 0); 178 179 *buf = consume_uint8t(*buf, len, &selector); 180 keytype = select_keytype(selector, &keysize); 181 *out2 = (void *)slh_dsa_gen_key(keytype, keysize, NULL, 0); 182 return; 183 } 184 185 #define PARAM_BUF_SZ 256 186 187 /** 188 * @brief Generates an SLH-DSA key pair with custom parameters. 189 * 190 * This function extracts a selector value from the provided buffer, 191 * determines the corresponding key type and size, and generates an 192 * SLH-DSA key pair using randomly generated public and private key 193 * buffers. It also introduces intentional modifications to test 194 * invalid parameter handling. 195 * 196 * @param buf Pointer to a buffer containing the selector value. The 197 * buffer pointer is updated as values are consumed. 198 * @param len Pointer to the remaining buffer length, updated as values 199 * are consumed. 200 * @param out1 Pointer to store the generated key. Will be NULL if key 201 * generation fails due to invalid parameters. 202 * @param out2 Unused output parameter (placeholder for symmetry with 203 * other key generation functions). 204 */ 205 static void slh_dsa_gen_key_with_params(uint8_t **buf, size_t *len, 206 void **out1, void **out2) 207 { 208 uint8_t selector = 0; 209 const char *keytype = NULL; 210 uint32_t keysize; 211 uint8_t pubbuf[PARAM_BUF_SZ]; /* expressly bigger than max key size * 3 */ 212 uint8_t prvbuf[PARAM_BUF_SZ]; /* expressly bigger than max key size * 3 */ 213 uint8_t sdbuf[PARAM_BUF_SZ]; /* expressly bigger than max key size * 3 */ 214 uint8_t *bufptr; 215 OSSL_PARAM params[3]; 216 size_t buflen; 217 uint8_t broken = 0; 218 219 *out1 = NULL; 220 221 *buf = consume_uint8t(*buf, len, &selector); 222 keytype = select_keytype(selector, &keysize); 223 224 RAND_bytes(pubbuf, PARAM_BUF_SZ); 225 RAND_bytes(prvbuf, PARAM_BUF_SZ); 226 RAND_bytes(sdbuf, PARAM_BUF_SZ); 227 228 /* 229 * select an invalid length if the buffer 0th bit is one 230 * make it too big if the 2nd bit is 0, smaller otherwise 231 */ 232 buflen = keysize * 2; /* these params are 2 * the keysize */ 233 if ((*buf)[0] & 0x1) { 234 buflen = ((*buf)[0] & 0x2) ? buflen - 1 : buflen + 1; 235 broken = 1; 236 } 237 238 /* pass a null buffer if the third bit of the buffer is 1 */ 239 bufptr = ((*buf)[0] & 0x4) ? NULL : pubbuf; 240 if (!broken) 241 broken = (bufptr == NULL) ? 1 : 0; 242 243 params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, 244 (char *)bufptr, buflen); 245 246 buflen = keysize * 2; 247 /* select an invalid length if the 4th bit is true */ 248 if ((*buf)[0] & 0x8) { 249 buflen = (*buf[0] & 0x1) ? buflen - 1 : buflen + 1; 250 broken = 1; 251 } 252 253 /* pass a null buffer if the 5th bit is true */ 254 bufptr = ((*buf)[0] & 0x10) ? NULL : prvbuf; 255 if (!broken) 256 broken = (bufptr == NULL) ? 1 : 0; 257 params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, 258 (char *)bufptr, buflen); 259 260 params[2] = OSSL_PARAM_construct_end(); 261 262 *out1 = (void *)slh_dsa_gen_key(keytype, keysize, params, &broken); 263 264 if (broken) 265 OPENSSL_assert(*out1 == NULL); 266 else 267 OPENSSL_assert(*out1 != NULL); 268 return; 269 } 270 271 /** 272 * @brief Frees allocated SLH-DSA key structures. 273 * 274 * This function releases memory allocated for SLH-DSA key pairs 275 * by freeing the provided EVP_PKEY structures. 276 * 277 * @param in1 Pointer to the first input key to be freed. 278 * @param in2 Pointer to the second input key to be freed. 279 * @param out1 Pointer to the first output key to be freed. 280 * @param out2 Pointer to the second output key to be freed. 281 */ 282 static void slh_dsa_clean_keys(void *in1, void *in2, void *out1, void *out2) 283 { 284 EVP_PKEY_free((EVP_PKEY *)in1); 285 EVP_PKEY_free((EVP_PKEY *)in2); 286 EVP_PKEY_free((EVP_PKEY *)out1); 287 EVP_PKEY_free((EVP_PKEY *)out2); 288 } 289 290 /** 291 * @brief Performs SLH-DSA signing and verification on a given message. 292 * 293 * This function generates an SLH-DSA key, signs a message, and verifies 294 * the generated signature. It extracts necessary parameters from the buffer 295 * to determine signing options. 296 * 297 * @param buf Pointer to a buffer containing the selector and message data. 298 * The buffer pointer is updated as values are consumed. 299 * @param len Pointer to the remaining buffer length, updated as values 300 * are consumed. 301 * @param key1 Unused key parameter (placeholder for function signature consistency). 302 * @param key2 Unused key parameter (placeholder for function signature consistency). 303 * @param out1 Pointer to store the generated key (for cleanup purposes). 304 * @param out2 Unused output parameter (placeholder for consistency). 305 */ 306 static void slh_dsa_sign_verify(uint8_t **buf, size_t *len, void *key1, 307 void *key2, void **out1, void **out2) 308 { 309 EVP_PKEY_CTX *ctx = NULL; 310 EVP_PKEY *key = NULL; 311 EVP_SIGNATURE *sig_alg = NULL; 312 const char *keytype; 313 uint32_t keylen; 314 uint8_t selector = 0; 315 unsigned char *msg = NULL; 316 size_t msg_len; 317 size_t sig_len; 318 unsigned char *sig = NULL; 319 OSSL_PARAM params[4]; 320 int paramidx = 0; 321 int intval1, intval2; 322 int expect_init_rc = 1; 323 324 *buf = consume_uint8t(*buf, len, &selector); 325 if (*buf == NULL) 326 return; 327 328 keytype = select_keytype(selector, &keylen); 329 330 /* 331 * Consume another byte to figure out our params 332 */ 333 *buf = consume_uint8t(*buf, len, &selector); 334 if (*buf == NULL) 335 return; 336 337 /* 338 * Remainder of the buffer is the msg to sign 339 */ 340 msg = (unsigned char *)*buf; 341 msg_len = *len; 342 343 /* if msg_len > 255, sign_message_init will fail */ 344 if (msg_len > 255 && (selector & 0x1) != 0) 345 expect_init_rc = 0; 346 347 *len = 0; 348 349 if (selector & 0x1) 350 params[paramidx++] = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, 351 msg, msg_len); 352 353 if (selector & 0x2) { 354 intval1 = selector & 0x4; 355 params[paramidx++] = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 356 &intval1); 357 } 358 359 if (selector & 0x8) { 360 intval2 = selector & 0x10; 361 params[paramidx++] = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 362 &intval2); 363 } 364 365 params[paramidx] = OSSL_PARAM_construct_end(); 366 367 key = (void *)slh_dsa_gen_key(keytype, keylen, NULL, 0); 368 OPENSSL_assert(key != NULL); 369 *out1 = key; /* for cleanup */ 370 371 ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL); 372 OPENSSL_assert(ctx != NULL); 373 374 sig_alg = EVP_SIGNATURE_fetch(NULL, keytype, NULL); 375 OPENSSL_assert(sig_alg != NULL); 376 377 OPENSSL_assert(EVP_PKEY_sign_message_init(ctx, sig_alg, params) == expect_init_rc); 378 /* 379 * the context_string parameter can be no more than 255 bytes, so if 380 * our random input buffer is greater than that, we expect failure above, 381 * which we check for. In that event, theres nothing more we can do here 382 * so bail out 383 */ 384 if (expect_init_rc == 0) 385 goto out; 386 387 OPENSSL_assert(EVP_PKEY_sign(ctx, NULL, &sig_len, msg, msg_len)); 388 sig = OPENSSL_zalloc(sig_len); 389 OPENSSL_assert(sig != NULL); 390 391 OPENSSL_assert(EVP_PKEY_sign(ctx, sig, &sig_len, msg, msg_len)); 392 393 OPENSSL_assert(EVP_PKEY_verify_message_init(ctx, sig_alg, params)); 394 OPENSSL_assert(EVP_PKEY_verify(ctx, sig, sig_len, msg, msg_len)); 395 396 out: 397 OPENSSL_free(sig); 398 EVP_SIGNATURE_free(sig_alg); 399 EVP_PKEY_CTX_free(ctx); 400 } 401 402 /** 403 * @brief Exports and imports SLH-DSA key pairs, verifying equivalence. 404 * 405 * This function extracts key data from two given SLH-DSA keys (`alice` and `bob`), 406 * reconstructs new keys from the extracted data, and verifies that the imported 407 * keys are equivalent to the originals. It ensures that key export/import 408 * functionality is working correctly. 409 * 410 * @param buf Unused buffer parameter (placeholder for function signature consistency). 411 * @param len Unused length parameter (placeholder for function signature consistency). 412 * @param key1 Pointer to the first key (`alice`) to be exported and imported. 413 * @param key2 Pointer to the second key (`bob`) to be exported and imported. 414 * @param out1 Unused output parameter (placeholder for consistency). 415 * @param out2 Unused output parameter (placeholder for consistency). 416 */ 417 static void slh_dsa_export_import(uint8_t **buf, size_t *len, void *key1, 418 void *key2, void **out1, void **out2) 419 { 420 int rc; 421 EVP_PKEY *alice = (EVP_PKEY *)key1; 422 EVP_PKEY *bob = (EVP_PKEY *)key2; 423 EVP_PKEY *new = NULL; 424 EVP_PKEY_CTX *ctx = NULL; 425 OSSL_PARAM *params = NULL; 426 427 OPENSSL_assert(EVP_PKEY_todata(alice, EVP_PKEY_KEYPAIR, ¶ms) == 1); 428 429 ctx = EVP_PKEY_CTX_new_from_pkey(NULL, alice, NULL); 430 OPENSSL_assert(ctx != NULL); 431 432 OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); 433 434 new = EVP_PKEY_new(); 435 OPENSSL_assert(new != NULL); 436 OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); 437 438 /* 439 * EVP_PKEY returns: 440 * 1 if the keys are equivalent 441 * 0 if the keys are not equivalent 442 * -1 if the key types are differnt 443 * -2 if the operation is not supported 444 */ 445 OPENSSL_assert(EVP_PKEY_eq(alice, new) == 1); 446 EVP_PKEY_free(new); 447 EVP_PKEY_CTX_free(ctx); 448 OSSL_PARAM_free(params); 449 params = NULL; 450 ctx = NULL; 451 new = NULL; 452 453 OPENSSL_assert(EVP_PKEY_todata(bob, EVP_PKEY_KEYPAIR, ¶ms) == 1); 454 455 ctx = EVP_PKEY_CTX_new_from_pkey(NULL, bob, NULL); 456 OPENSSL_assert(ctx != NULL); 457 458 OPENSSL_assert(EVP_PKEY_fromdata_init(ctx)); 459 460 new = EVP_PKEY_new(); 461 OPENSSL_assert(new != NULL); 462 OPENSSL_assert(EVP_PKEY_fromdata(ctx, &new, EVP_PKEY_KEYPAIR, params) == 1); 463 464 OPENSSL_assert(EVP_PKEY_eq(bob, new) == 1); 465 466 /* 467 * Depending on the types of eys that get generated 468 * we might get a simple non-equivalence or a type mismatch here 469 */ 470 rc = EVP_PKEY_eq(alice, new); 471 OPENSSL_assert(rc == 0 || rc == -1); 472 473 EVP_PKEY_CTX_free(ctx); 474 EVP_PKEY_free(new); 475 OSSL_PARAM_free(params); 476 } 477 478 /** 479 * @brief Represents an operation table entry for cryptographic operations. 480 * 481 * This structure defines a table entry containing function pointers for 482 * setting up, executing, and cleaning up cryptographic operations, along 483 * with associated metadata such as a name and description. 484 * 485 * @struct op_table_entry 486 */ 487 struct op_table_entry { 488 /** Name of the operation. */ 489 char *name; 490 491 /** 492 * @brief Function pointer for setting up the operation. 493 * 494 * @param buf Pointer to the buffer pointer; may be updated. 495 * @param len Pointer to the remaining buffer size; may be updated. 496 * @param out1 Pointer to store the first output of the setup function. 497 * @param out2 Pointer to store the second output of the setup function. 498 */ 499 void (*setup)(uint8_t **buf, size_t *len, void **out1, void **out2); 500 501 /** 502 * @brief Function pointer for executing the operation. 503 * 504 * @param buf Pointer to the buffer pointer; may be updated. 505 * @param len Pointer to the remaining buffer size; may be updated. 506 * @param in1 First input parameter for the operation. 507 * @param in2 Second input parameter for the operation. 508 * @param out1 Pointer to store the first output of the operation. 509 * @param out2 Pointer to store the second output of the operation. 510 */ 511 void (*doit)(uint8_t **buf, size_t *len, void *in1, void *in2, 512 void **out1, void **out2); 513 514 /** 515 * @brief Function pointer for cleaning up after the operation. 516 * 517 * @param in1 First input parameter to be cleaned up. 518 * @param in2 Second input parameter to be cleaned up. 519 * @param out1 First output parameter to be cleaned up. 520 * @param out2 Second output parameter to be cleaned up. 521 */ 522 void (*cleanup)(void *in1, void *in2, void *out1, void *out2); 523 }; 524 525 static struct op_table_entry ops[] = { 526 { 527 "Generate SLH-DSA keys", 528 slh_dsa_gen_keys, 529 NULL, 530 slh_dsa_clean_keys 531 }, { 532 "Generate SLH-DSA keys with params", 533 slh_dsa_gen_key_with_params, 534 NULL, 535 slh_dsa_clean_keys 536 }, { 537 "SLH-DSA Export/Import", 538 slh_dsa_gen_keys, 539 slh_dsa_export_import, 540 slh_dsa_clean_keys 541 }, { 542 "SLH-DSA sign and verify", 543 NULL, 544 slh_dsa_sign_verify, 545 slh_dsa_clean_keys 546 } 547 }; 548 549 int FuzzerInitialize(int *argc, char ***argv) 550 { 551 return 0; 552 } 553 554 /** 555 * @brief Processes a fuzzing input by selecting and executing an operation. 556 * 557 * This function interprets the first byte of the input buffer to determine 558 * an operation to execute. It then follows a setup, execution, and cleanup 559 * sequence based on the selected operation. 560 * 561 * @param buf Pointer to the input buffer. 562 * @param len Length of the input buffer. 563 * 564 * @return 0 on successful execution, -1 if the input is too short. 565 * 566 * @note The function requires at least 32 bytes in the buffer to proceed. 567 * It utilizes the `ops` operation table to dynamically determine and 568 * execute the selected operation. 569 */ 570 int FuzzerTestOneInput(const uint8_t *buf, size_t len) 571 { 572 uint8_t operation; 573 uint8_t *buffer_cursor; 574 void *in1 = NULL, *in2 = NULL; 575 void *out1 = NULL, *out2 = NULL; 576 577 if (len < 32) 578 return -1; 579 /* 580 * Get the first byte of the buffer to tell us what operation 581 * to preform 582 */ 583 buffer_cursor = consume_uint8t(buf, &len, &operation); 584 if (buffer_cursor == NULL) 585 return -1; 586 587 /* 588 * Adjust for operational array size 589 */ 590 operation %= OSSL_NELEM(ops); 591 592 /* 593 * And run our setup/doit/cleanup sequence 594 */ 595 if (ops[operation].setup != NULL) 596 ops[operation].setup(&buffer_cursor, &len, &in1, &in2); 597 if (ops[operation].doit != NULL) 598 ops[operation].doit(&buffer_cursor, &len, in1, in2, &out1, &out2); 599 if (ops[operation].cleanup != NULL) 600 ops[operation].cleanup(in1, in2, out1, out2); 601 602 return 0; 603 } 604 605 void FuzzerCleanup(void) 606 { 607 OPENSSL_cleanup(); 608 } 609