1 /* 2 * Copyright 2015-2025 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <openssl/conf.h> 14 #include <openssl/crypto.h> 15 #include <openssl/err.h> 16 #include <openssl/evp.h> 17 #include <openssl/provider.h> 18 #include <openssl/core_names.h> 19 #include <openssl/params.h> 20 #include <openssl/param_build.h> 21 #include <openssl/rand.h> 22 #include <crypto/ml_kem.h> 23 #include "testutil.h" 24 25 static OSSL_LIB_CTX *testctx = NULL; 26 27 typedef enum OPTION_choice { 28 OPT_ERR = -1, 29 OPT_EOF = 0, 30 OPT_CONFIG_FILE, 31 OPT_TEST_RAND, 32 OPT_TEST_ENUM 33 } OPTION_CHOICE; 34 35 const OPTIONS *test_get_options(void) 36 { 37 static const OPTIONS options[] = { 38 OPT_TEST_OPTIONS_DEFAULT_USAGE, 39 { "test-rand", OPT_TEST_RAND, '-', "Test non-derandomised ML-KEM" }, 40 { NULL } 41 }; 42 return options; 43 } 44 45 static uint8_t gen_seed[64] = { 46 0x7c, 0x99, 0x35, 0xa0, 0xb0, 0x76, 0x94, 0xaa, 0x0c, 0x6d, 0x10, 0xe4, 47 0xdb, 0x6b, 0x1a, 0xdd, 0x2f, 0xd8, 0x1a, 0x25, 0xcc, 0xb1, 0x48, 0x03, 48 0x2d, 0xcd, 0x73, 0x99, 0x36, 0x73, 0x7f, 0x2d, 0x86, 0x26, 0xed, 0x79, 49 0xd4, 0x51, 0x14, 0x08, 0x00, 0xe0, 0x3b, 0x59, 0xb9, 0x56, 0xf8, 0x21, 50 0x0e, 0x55, 0x60, 0x67, 0x40, 0x7d, 0x13, 0xdc, 0x90, 0xfa, 0x9e, 0x8b, 51 0x87, 0x2b, 0xfb, 0x8f 52 }; 53 static uint8_t enc_seed[32] = { 54 0x14, 0x7c, 0x03, 0xf7, 0xa5, 0xbe, 0xbb, 0xa4, 0x06, 0xc8, 0xfa, 0xe1, 55 0x87, 0x4d, 0x7f, 0x13, 0xc8, 0x0e, 0xfe, 0x79, 0xa3, 0xa9, 0xa8, 0x74, 56 0xcc, 0x09, 0xfe, 0x76, 0xf6, 0x99, 0x76, 0x15 57 }; 58 static uint8_t dec_seed[32] = { 59 0x4e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x72, 0x6f, 0x69, 60 0x64, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6c, 0x6f, 61 0x6f, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f 62 }; 63 static uint8_t expected_rho[3][32] = { 64 { 65 0x7e, 0xfb, 0x9e, 0x40, 0xc3, 0xbf, 0x0f, 0xf0, 0x43, 0x29, 0x86, 0xae, 66 0x4b, 0xc1, 0xa2, 0x42, 0xce, 0x99, 0x21, 0xaa, 0x9e, 0x22, 0x44, 0x88, 67 0x19, 0x58, 0x5d, 0xea, 0x30, 0x8e, 0xb0, 0x39 68 }, 69 { 70 0x16, 0x2e, 0xc0, 0x98, 0xa9, 0x00, 0xb1, 0x2d, 0xd8, 0xfa, 0xbb, 0xfb, 71 0x3f, 0xe8, 0xcb, 0x1d, 0xc4, 0xe8, 0x31, 0x5f, 0x2a, 0xf0, 0xd3, 0x2f, 72 0x00, 0x17, 0xae, 0x13, 0x6e, 0x19, 0xf0, 0x28 73 }, 74 { 75 0x29, 0xb4, 0xf9, 0xf8, 0xcf, 0xba, 0xdf, 0x2e, 0x41, 0x86, 0x9a, 0xbf, 76 0xba, 0xd1, 0x07, 0x38, 0xad, 0x04, 0xcc, 0x75, 0x2b, 0xc2, 0x0c, 0x39, 77 0x47, 0x46, 0x85, 0x0e, 0x0c, 0x48, 0x47, 0xdb 78 } 79 }; 80 static uint8_t expected_ctext_sha256[3][32] = { 81 { 82 0xbc, 0x29, 0xd7, 0xdf, 0x8b, 0xc5, 0x46, 0x5d, 0x98, 0x06, 0x01, 0xd8, 83 0x00, 0x25, 0x97, 0x93, 0xe2, 0x60, 0x38, 0x25, 0xa5, 0x72, 0xda, 0x6c, 84 0xd1, 0x98, 0xa5, 0x12, 0xcc, 0x6d, 0x1a, 0x34 85 }, 86 { 87 0x36, 0x82, 0x9a, 0x2f, 0x35, 0xcb, 0xf4, 0xde, 0xb6, 0x2c, 0x0a, 0x12, 88 0xa1, 0x5c, 0x22, 0xda, 0xe9, 0xf8, 0xd2, 0xc2, 0x52, 0x56, 0x6f, 0xc2, 89 0x4f, 0x88, 0xab, 0xe8, 0x05, 0xcb, 0x57, 0x5e 90 }, 91 { 92 0x50, 0x81, 0x36, 0xa1, 0x3f, 0x8a, 0x79, 0x20, 0xe3, 0x43, 0x44, 0x98, 93 0xc6, 0x97, 0x5c, 0xbb, 0xab, 0x45, 0x7d, 0x80, 0x93, 0x09, 0xeb, 0x2f, 94 0x92, 0x45, 0x3e, 0x74, 0x09, 0x73, 0x82, 0x10 95 } 96 }; 97 static uint8_t expected_shared_secret[3][32] = { 98 { 99 0x31, 0x98, 0x39, 0xe8, 0x2a, 0xb6, 0xb2, 0x22, 0xde, 0x7b, 0x61, 0x9e, 100 0x80, 0xda, 0x83, 0x91, 0x52, 0x2b, 0xbb, 0x37, 0x67, 0x70, 0x18, 0x49, 101 0x4a, 0x47, 0x42, 0xc5, 0x3f, 0x9a, 0xbf, 0xdf 102 }, 103 { 104 0xe7, 0x18, 0x4a, 0x09, 0x75, 0xee, 0x34, 0x70, 0x87, 0x8d, 0x2d, 0x15, 105 0x9e, 0xc8, 0x31, 0x29, 0xc8, 0xae, 0xc2, 0x53, 0xd4, 0xee, 0x17, 0xb4, 106 0x81, 0x03, 0x11, 0xd1, 0x98, 0xcd, 0x03, 0x68 107 }, 108 { 109 0x48, 0x9d, 0xd1, 0xe9, 0xc2, 0xbe, 0x4a, 0xf3, 0x48, 0x2b, 0xdb, 0x35, 110 0xbb, 0x26, 0xce, 0x76, 0x0e, 0x6e, 0x41, 0x4d, 0xa6, 0xec, 0xbe, 0x48, 111 0x99, 0x85, 0x74, 0x8a, 0x82, 0x5f, 0x1c, 0xd6 112 }, 113 }; 114 115 static int test_ml_kem(void) 116 { 117 EVP_PKEY *akey, *bkey = NULL; 118 int res = 0; 119 size_t publen; 120 unsigned char *rawpub = NULL; 121 EVP_PKEY_CTX *ctx = NULL; 122 unsigned char *wrpkey = NULL, *agenkey = NULL, *bgenkey = NULL; 123 size_t wrpkeylen, agenkeylen, bgenkeylen, i; 124 125 /* Generate Alice's key */ 126 akey = EVP_PKEY_Q_keygen(testctx, NULL, "ML-KEM-768"); 127 if (!TEST_ptr(akey)) 128 goto err; 129 130 /* Get the raw public key */ 131 publen = EVP_PKEY_get1_encoded_public_key(akey, &rawpub); 132 if (!TEST_size_t_gt(publen, 0)) 133 goto err; 134 135 /* Create Bob's key and populate it with Alice's public key data */ 136 bkey = EVP_PKEY_new(); 137 if (!TEST_ptr(bkey)) 138 goto err; 139 140 if (!TEST_int_gt(EVP_PKEY_copy_parameters(bkey, akey), 0)) 141 goto err; 142 143 /* Bob's empty key is not equal to Alice's */ 144 if (!TEST_false(EVP_PKEY_eq(akey, bkey)) 145 || !TEST_false(EVP_PKEY_eq(bkey, akey))) 146 goto err; 147 148 if (!TEST_true(EVP_PKEY_set1_encoded_public_key(bkey, rawpub, publen))) 149 goto err; 150 151 /* Bob's copy of Alice's public key makes the two equal */ 152 if (!TEST_true(EVP_PKEY_eq(akey, bkey)) 153 || !TEST_true(EVP_PKEY_eq(bkey, akey))) 154 goto err; 155 156 /* Encapsulate Bob's key */ 157 ctx = EVP_PKEY_CTX_new_from_pkey(testctx, bkey, NULL); 158 if (!TEST_ptr(ctx)) 159 goto err; 160 161 if (!TEST_int_gt(EVP_PKEY_encapsulate_init(ctx, NULL), 0)) 162 goto err; 163 164 if (!TEST_int_gt(EVP_PKEY_encapsulate(ctx, NULL, &wrpkeylen, NULL, 165 &bgenkeylen), 0)) 166 goto err; 167 168 if (!TEST_size_t_gt(wrpkeylen, 0) || !TEST_size_t_gt(bgenkeylen, 0)) 169 goto err; 170 171 wrpkey = OPENSSL_zalloc(wrpkeylen); 172 bgenkey = OPENSSL_zalloc(bgenkeylen); 173 if (!TEST_ptr(wrpkey) || !TEST_ptr(bgenkey)) 174 goto err; 175 176 if (!TEST_int_gt(EVP_PKEY_encapsulate(ctx, wrpkey, &wrpkeylen, bgenkey, 177 &bgenkeylen), 0)) 178 goto err; 179 180 EVP_PKEY_CTX_free(ctx); 181 182 /* Alice now decapsulates Bob's key */ 183 ctx = EVP_PKEY_CTX_new_from_pkey(testctx, akey, NULL); 184 if (!TEST_ptr(ctx)) 185 goto err; 186 187 if (!TEST_int_gt(EVP_PKEY_decapsulate_init(ctx, NULL), 0)) 188 goto err; 189 190 if (!TEST_int_gt(EVP_PKEY_decapsulate(ctx, NULL, &agenkeylen, wrpkey, 191 wrpkeylen), 0)) 192 goto err; 193 194 if (!TEST_size_t_gt(agenkeylen, 0)) 195 goto err; 196 197 agenkey = OPENSSL_zalloc(agenkeylen); 198 if (!TEST_ptr(agenkey)) 199 goto err; 200 201 if (!TEST_int_gt(EVP_PKEY_decapsulate(ctx, agenkey, &agenkeylen, wrpkey, 202 wrpkeylen), 0)) 203 goto err; 204 205 /* Hopefully we ended up with a shared key */ 206 if (!TEST_mem_eq(agenkey, agenkeylen, bgenkey, bgenkeylen)) 207 goto err; 208 209 /* Verify we generated a non-zero shared key */ 210 for (i = 0; i < agenkeylen; i++) 211 if (agenkey[i] != 0) 212 break; 213 if (!TEST_size_t_ne(i, agenkeylen)) 214 goto err; 215 216 res = 1; 217 err: 218 EVP_PKEY_CTX_free(ctx); 219 EVP_PKEY_free(akey); 220 EVP_PKEY_free(bkey); 221 OPENSSL_free(rawpub); 222 OPENSSL_free(wrpkey); 223 OPENSSL_free(agenkey); 224 OPENSSL_free(bgenkey); 225 return res; 226 } 227 228 static int test_non_derandomised_ml_kem(void) 229 { 230 static const int alg[3] = { 231 EVP_PKEY_ML_KEM_512, 232 EVP_PKEY_ML_KEM_768, 233 EVP_PKEY_ML_KEM_1024 234 }; 235 EVP_RAND_CTX *privctx; 236 EVP_RAND_CTX *pubctx; 237 EVP_MD *sha256; 238 int i, ret = 0; 239 240 if (!TEST_ptr(privctx = RAND_get0_private(NULL)) 241 || !TEST_ptr(pubctx = RAND_get0_public(NULL))) 242 return 0; 243 244 if (!TEST_ptr(sha256 = EVP_MD_fetch(NULL, "sha256", NULL))) 245 return 0; 246 247 for (i = 0; i < (int) OSSL_NELEM(alg); ++i) { 248 const ML_KEM_VINFO *v; 249 OSSL_PARAM params[3], *p; 250 uint8_t hash[32]; 251 EVP_PKEY *akey = NULL, *bkey = NULL; 252 size_t publen; 253 unsigned char *rawpub = NULL; 254 EVP_PKEY_CTX *ctx = NULL; 255 unsigned char *wrpkey = NULL, *agenkey = NULL, *bgenkey = NULL; 256 size_t wrpkeylen, agenkeylen, bgenkeylen; 257 unsigned int strength = 256; 258 unsigned char c; 259 int res = -1; 260 261 if ((v = ossl_ml_kem_get_vinfo(alg[i])) == NULL) 262 goto done; 263 264 /* Configure the private RNG to output just the keygen seed */ 265 p = params; 266 *p++ = OSSL_PARAM_construct_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY, 267 gen_seed, sizeof(gen_seed)); 268 *p++ = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, &strength); 269 *p = OSSL_PARAM_construct_end(); 270 if (!TEST_true(EVP_RAND_CTX_set_params(privctx, params))) 271 goto done; 272 273 res = -2; 274 /* Generate Alice's key */ 275 akey = EVP_PKEY_Q_keygen(testctx, NULL, v->algorithm_name); 276 if (!TEST_ptr(akey)) 277 goto done; 278 279 /* Check that no more entropy is available! */ 280 if (!TEST_int_le(RAND_priv_bytes(&c, 1), 0)) 281 goto done; 282 283 /* Get the raw public key */ 284 publen = EVP_PKEY_get1_encoded_public_key(akey, &rawpub); 285 if (!TEST_size_t_eq(publen, v->pubkey_bytes)) 286 goto done; 287 288 res = -3; 289 /* Check that we got the expected 'rho' value in the ciphertext */ 290 if (!TEST_mem_eq(rawpub + v->vector_bytes, ML_KEM_RANDOM_BYTES, 291 expected_rho[i], ML_KEM_RANDOM_BYTES)) 292 goto done; 293 294 res = -4; 295 /* Create Bob's key and populate it with Alice's public key data */ 296 bkey = EVP_PKEY_new(); 297 if (!TEST_ptr(bkey)) 298 goto done; 299 if (!TEST_int_gt(EVP_PKEY_copy_parameters(bkey, akey), 0)) 300 goto done; 301 if (!TEST_true(EVP_PKEY_set1_encoded_public_key(bkey, rawpub, publen))) 302 goto done; 303 304 /* Configure the public RNG to output just the encap seed */ 305 p = params; 306 *p = OSSL_PARAM_construct_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY, 307 enc_seed, sizeof(enc_seed)); 308 if (!TEST_true(EVP_RAND_CTX_set_params(pubctx, params))) 309 goto done; 310 311 /* Encapsulate Bob's key */ 312 res = -5; 313 ctx = EVP_PKEY_CTX_new_from_pkey(testctx, bkey, NULL); 314 if (!TEST_ptr(ctx)) 315 goto done; 316 if (!TEST_int_gt(EVP_PKEY_encapsulate_init(ctx, NULL), 0)) 317 goto done; 318 if (!TEST_int_gt(EVP_PKEY_encapsulate(ctx, NULL, &wrpkeylen, NULL, 319 &bgenkeylen), 0)) 320 goto done; 321 if (!TEST_size_t_eq(wrpkeylen, v->ctext_bytes) 322 || !TEST_size_t_eq(bgenkeylen, ML_KEM_SHARED_SECRET_BYTES)) 323 goto done; 324 wrpkey = OPENSSL_zalloc(wrpkeylen); 325 bgenkey = OPENSSL_zalloc(bgenkeylen); 326 if (!TEST_ptr(wrpkey) || !TEST_ptr(bgenkey)) 327 goto done; 328 if (!TEST_true(EVP_PKEY_encapsulate(ctx, wrpkey, &wrpkeylen, bgenkey, 329 &bgenkeylen))) 330 goto done; 331 EVP_PKEY_CTX_free(ctx); 332 ctx = NULL; 333 /* Check that no more public entropy is available! */ 334 if (!TEST_int_le(RAND_bytes(&c, 1), 0)) 335 goto done; 336 337 res = -6; 338 /* Check the ciphertext hash */ 339 if (!TEST_true(EVP_Digest(wrpkey, v->ctext_bytes, 340 hash, NULL, sha256, NULL)) 341 || !TEST_mem_eq(hash, sizeof(hash), 342 expected_ctext_sha256[i], 343 sizeof(expected_ctext_sha256[i]))) 344 goto done; 345 /* Check for the expected shared secret */ 346 if (!TEST_mem_eq(bgenkey, bgenkeylen, 347 expected_shared_secret[i], ML_KEM_SHARED_SECRET_BYTES)) 348 goto done; 349 350 /* 351 * Alice now decapsulates Bob's key. Decap should not need a seed if 352 * the ciphertext length is good. 353 */ 354 res = -7; 355 ctx = EVP_PKEY_CTX_new_from_pkey(testctx, akey, NULL); 356 if (!TEST_ptr(ctx)) 357 goto done; 358 if (!TEST_int_gt(EVP_PKEY_decapsulate_init(ctx, NULL), 0)) 359 goto done; 360 if (!TEST_true(EVP_PKEY_decapsulate(ctx, NULL, &agenkeylen, wrpkey, 361 wrpkeylen))) 362 goto done; 363 if (!TEST_size_t_eq(agenkeylen, ML_KEM_SHARED_SECRET_BYTES)) 364 goto done; 365 agenkey = OPENSSL_zalloc(agenkeylen); 366 if (!TEST_ptr(agenkey)) 367 goto done; 368 if (!TEST_true(EVP_PKEY_decapsulate(ctx, agenkey, &agenkeylen, wrpkey, 369 wrpkeylen))) 370 goto done; 371 /* Hopefully we ended up with a shared key */ 372 if (!TEST_mem_eq(agenkey, agenkeylen, bgenkey, bgenkeylen)) 373 goto done; 374 375 res = -8; 376 /* Now a quick negative test by zeroing the ciphertext */ 377 memset(wrpkey, 0, v->ctext_bytes); 378 if (!TEST_true(EVP_PKEY_decapsulate(ctx, agenkey, &agenkeylen, wrpkey, 379 wrpkeylen))) 380 goto done; 381 if (!TEST_mem_ne(agenkey, agenkeylen, bgenkey, bgenkeylen)) 382 goto done; 383 384 res = -9; 385 /* Configure decap entropy for a bad ciphertext length */ 386 p = params; 387 *p = OSSL_PARAM_construct_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY, 388 dec_seed, sizeof(dec_seed)); 389 if (!TEST_true(EVP_RAND_CTX_set_params(pubctx, params))) 390 goto done; 391 392 /* This time decap should fail, and return the decap entropy */ 393 if (!TEST_false(EVP_PKEY_decapsulate(ctx, agenkey, &agenkeylen, wrpkey, 394 wrpkeylen - 1))) 395 goto done; 396 if (!TEST_mem_eq(agenkey, agenkeylen, dec_seed, sizeof(dec_seed))) 397 goto done; 398 399 res = 0; 400 401 done: 402 EVP_PKEY_CTX_free(ctx); 403 EVP_PKEY_free(akey); 404 EVP_PKEY_free(bkey); 405 OPENSSL_free(rawpub); 406 OPENSSL_free(wrpkey); 407 OPENSSL_free(agenkey); 408 OPENSSL_free(bgenkey); 409 if (res != 0) 410 ret = res; 411 } 412 413 EVP_MD_free(sha256); 414 return ret == 0; 415 } 416 417 int setup_tests(void) 418 { 419 int test_rand = 0; 420 OPTION_CHOICE o; 421 422 while ((o = opt_next()) != OPT_EOF) { 423 switch (o) { 424 case OPT_TEST_RAND: 425 test_rand = 1; 426 break; 427 case OPT_TEST_CASES: 428 break; 429 default: 430 return 0; 431 } 432 } 433 434 if (test_rand != 0) { 435 /* Cargo-culted from test/rand_test.c, this may need changes */ 436 if (!TEST_true(RAND_set_DRBG_type(NULL, "TEST-RAND", "fips=no", 437 NULL, NULL))) 438 return 0; 439 ADD_TEST(test_non_derandomised_ml_kem); 440 return 1; 441 } 442 443 ADD_TEST(test_ml_kem); 444 return 1; 445 } 446