xref: /openbsd-src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c (revision 745491882adade544876937148104d21ba956b8b)
1*74549188Stb /*	$OpenBSD: mlkem_iteration_tests.c,v 1.2 2024/12/26 07:26:45 tb Exp $ */
20c814320Stb /*
30c814320Stb  * Copyright (c) 2024 Google Inc.
40c814320Stb  * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
50c814320Stb  * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
60c814320Stb  *
70c814320Stb  * Permission to use, copy, modify, and/or distribute this software for any
80c814320Stb  * purpose with or without fee is hereby granted, provided that the above
90c814320Stb  * copyright notice and this permission notice appear in all copies.
100c814320Stb  *
110c814320Stb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
120c814320Stb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
130c814320Stb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
140c814320Stb  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
150c814320Stb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
160c814320Stb  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
170c814320Stb  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180c814320Stb  */
190c814320Stb 
200c814320Stb #include <err.h>
210c814320Stb #include <stdint.h>
220c814320Stb #include <stdio.h>
230c814320Stb #include <stdlib.h>
240c814320Stb 
250c814320Stb #include "mlkem.h"
260c814320Stb 
270c814320Stb #include "mlkem_internal.h"
280c814320Stb #include "mlkem_tests_util.h"
290c814320Stb #include "sha3_internal.h"
300c814320Stb 
310c814320Stb /*
320c814320Stb  * Based on https://c2sp.org/CCTV/ML-KEM
330c814320Stb  *
340c814320Stb  * The final value has been updated to reflect the change from Kyber to ML-KEM.
350c814320Stb  *
360c814320Stb  * The deterministic RNG is a single SHAKE-128 instance with an empty input.
370c814320Stb  * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
380c814320Stb  */
390c814320Stb const uint8_t kExpectedSeedStart[16] = {
400c814320Stb 	0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
410c814320Stb 	0x50, 0x76, 0x05, 0x85, 0x3e
420c814320Stb };
430c814320Stb 
440c814320Stb /*
450c814320Stb  * Filippo says:
460c814320Stb  * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3
470c814320Stb  * but Boring believes this:
480c814320Stb  */
490c814320Stb const uint8_t kExpectedAdam768[32] = {
500c814320Stb 	0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
510c814320Stb 	0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
520c814320Stb 	0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1
530c814320Stb };
540c814320Stb 
550c814320Stb /*
560c814320Stb  * Filippo says:
570c814320Stb  * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543
580c814320Stb  * but Boring believes this:
590c814320Stb  */
600c814320Stb const uint8_t kExpectedAdam1024[32] = {
610c814320Stb 	0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
620c814320Stb 	0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
630c814320Stb 	0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
640c814320Stb };
650c814320Stb 
660c814320Stb struct iteration_ctx {
670c814320Stb 	uint8_t *encoded_public_key;
680c814320Stb 	size_t encoded_public_key_len;
690c814320Stb 	uint8_t *ciphertext;
700c814320Stb 	size_t ciphertext_len;
710c814320Stb 	uint8_t *invalid_ciphertext;
720c814320Stb 	size_t invalid_ciphertext_len;
730c814320Stb 	void *priv;
740c814320Stb 	void *pub;
750c814320Stb 
760c814320Stb 	mlkem_encode_private_key_fn encode_private_key;
770c814320Stb 	mlkem_encap_external_entropy_fn encap_external_entropy;
780c814320Stb 	mlkem_generate_key_external_entropy_fn generate_key_external_entropy;
790c814320Stb 	mlkem_public_from_private_fn public_from_private;
800c814320Stb 	mlkem_decap_fn decap;
810c814320Stb 
820c814320Stb 	const uint8_t *start;
830c814320Stb 	size_t start_len;
840c814320Stb 
850c814320Stb 	const uint8_t *expected;
860c814320Stb 	size_t expected_len;
870c814320Stb };
880c814320Stb 
890c814320Stb static int
900c814320Stb MlkemIterativeTest(struct iteration_ctx *ctx)
910c814320Stb {
920c814320Stb 	uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
930c814320Stb 	uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
940c814320Stb 	uint8_t seed[MLKEM_SEED_BYTES] = {0};
950c814320Stb 	sha3_ctx drng, results;
960c814320Stb 	uint8_t out[32];
970c814320Stb 	int i;
980c814320Stb 
990c814320Stb 	shake128_init(&drng);
1000c814320Stb 	shake128_init(&results);
1010c814320Stb 
1020c814320Stb 	shake_xof(&drng);
1030c814320Stb 	for (i = 0; i < 10000; i++) {
1040c814320Stb 		uint8_t *encoded_private_key = NULL;
1050c814320Stb 		size_t encoded_private_key_len;
1060c814320Stb 
1070c814320Stb 		/*
1080c814320Stb 		 * This should draw both d and z from DRNG concatenating in
1090c814320Stb 		 * seed.
1100c814320Stb 		 */
1110c814320Stb 		shake_out(&drng, seed, sizeof(seed));
1120c814320Stb 		if (i == 0) {
1130c814320Stb 			if (compare_data(seed, ctx->start, ctx->start_len,
1140c814320Stb 			    "seed start") != 0)
1150c814320Stb 				errx(1, "compare_data");
1160c814320Stb 		}
1170c814320Stb 
1180c814320Stb 		/* generate ek as encoded_public_key */
1190c814320Stb 		ctx->generate_key_external_entropy(ctx->encoded_public_key,
1200c814320Stb 		    ctx->priv, seed);
1210c814320Stb 		ctx->public_from_private(ctx->pub, ctx->priv);
1220c814320Stb 
1230c814320Stb 		/* hash in ek */
1240c814320Stb 		shake_update(&results, ctx->encoded_public_key,
1250c814320Stb 		    ctx->encoded_public_key_len);
1260c814320Stb 
1270c814320Stb 		/* marshal priv to dk as encoded_private_key */
1280c814320Stb 		if (!ctx->encode_private_key(ctx->priv, &encoded_private_key,
1290c814320Stb 		    &encoded_private_key_len))
1300c814320Stb 			errx(1, "encode private key");
1310c814320Stb 
1320c814320Stb 		/* hash in dk */
1330c814320Stb 		shake_update(&results, encoded_private_key,
1340c814320Stb 		    encoded_private_key_len);
1350c814320Stb 
1360c814320Stb 		free(encoded_private_key);
1370c814320Stb 
1380c814320Stb 		/* draw m as encap entropy from DRNG */
1390c814320Stb 		shake_out(&drng, encap_entropy, sizeof(encap_entropy));
1400c814320Stb 
1410c814320Stb 		/* generate ct as ciphertext, k as shared_secret */
1420c814320Stb 		ctx->encap_external_entropy(ctx->ciphertext, shared_secret,
1430c814320Stb 		    ctx->pub, encap_entropy);
1440c814320Stb 
1450c814320Stb 		/* hash in ct */
1460c814320Stb 		shake_update(&results, ctx->ciphertext, ctx->ciphertext_len);
1470c814320Stb 		/* hash in k */
1480c814320Stb 		shake_update(&results, shared_secret, sizeof(shared_secret));
1490c814320Stb 
1500c814320Stb 		/* draw ct as invalid_ciphertxt from DRNG */
1510c814320Stb 		shake_out(&drng, ctx->invalid_ciphertext,
1520c814320Stb 		    ctx->invalid_ciphertext_len);
1530c814320Stb 
1540c814320Stb 		/* generate k as shared secret from invalid ciphertext */
1550c814320Stb 		if (!ctx->decap(shared_secret, ctx->invalid_ciphertext,
1560c814320Stb 		    ctx->invalid_ciphertext_len, ctx->priv))
1570c814320Stb 			errx(1, "decap failed");
1580c814320Stb 
1590c814320Stb 		/* hash in k */
1600c814320Stb 		shake_update(&results, shared_secret, sizeof(shared_secret));
1610c814320Stb 	}
1620c814320Stb 	shake_xof(&results);
1630c814320Stb 	shake_out(&results, out, sizeof(out));
1640c814320Stb 
1650c814320Stb 	return compare_data(ctx->expected, out, sizeof(out), "final result hash");
1660c814320Stb }
1670c814320Stb 
1680c814320Stb int
1690c814320Stb main(void)
1700c814320Stb {
1710c814320Stb 	uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES];
1720c814320Stb 	uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
1730c814320Stb 	uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
1740c814320Stb 	struct MLKEM768_private_key priv768;
1750c814320Stb 	struct MLKEM768_public_key pub768;
1760c814320Stb 	struct iteration_ctx iteration768 = {
1770c814320Stb 		.encoded_public_key = encoded_public_key768,
1780c814320Stb 		.encoded_public_key_len = sizeof(encoded_public_key768),
1790c814320Stb 		.ciphertext = ciphertext768,
1800c814320Stb 		.ciphertext_len = sizeof(ciphertext768),
1810c814320Stb 		.invalid_ciphertext = invalid_ciphertext768,
1820c814320Stb 		.invalid_ciphertext_len = sizeof(invalid_ciphertext768),
1830c814320Stb 		.priv = &priv768,
1840c814320Stb 		.pub = &pub768,
1850c814320Stb 		.encap_external_entropy = mlkem768_encap_external_entropy,
1860c814320Stb 		.encode_private_key = mlkem768_encode_private_key,
1870c814320Stb 		.generate_key_external_entropy =
1880c814320Stb 		    mlkem768_generate_key_external_entropy,
1890c814320Stb 		.public_from_private = mlkem768_public_from_private,
1900c814320Stb 		.decap = mlkem768_decap,
1910c814320Stb 		.start = kExpectedSeedStart,
1920c814320Stb 		.start_len = sizeof(kExpectedSeedStart),
1930c814320Stb 		.expected = kExpectedAdam768,
1940c814320Stb 		.expected_len = sizeof(kExpectedAdam768),
1950c814320Stb 	};
1960c814320Stb 	uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES];
1970c814320Stb 	uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
1980c814320Stb 	uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
1990c814320Stb 	struct MLKEM1024_private_key priv1024;
2000c814320Stb 	struct MLKEM1024_public_key pub1024;
2010c814320Stb 	struct iteration_ctx iteration1024 = {
2020c814320Stb 		.encoded_public_key = encoded_public_key1024,
2030c814320Stb 		.encoded_public_key_len = sizeof(encoded_public_key1024),
2040c814320Stb 		.ciphertext = ciphertext1024,
2050c814320Stb 		.ciphertext_len = sizeof(ciphertext1024),
2060c814320Stb 		.invalid_ciphertext = invalid_ciphertext1024,
2070c814320Stb 		.invalid_ciphertext_len = sizeof(invalid_ciphertext1024),
2080c814320Stb 		.priv = &priv1024,
2090c814320Stb 		.pub = &pub1024,
2100c814320Stb 		.encap_external_entropy = mlkem1024_encap_external_entropy,
2110c814320Stb 		.encode_private_key = mlkem1024_encode_private_key,
2120c814320Stb 		.generate_key_external_entropy =
2130c814320Stb 		    mlkem1024_generate_key_external_entropy,
2140c814320Stb 		.public_from_private = mlkem1024_public_from_private,
2150c814320Stb 		.decap = mlkem1024_decap,
2160c814320Stb 		.start = kExpectedSeedStart,
2170c814320Stb 		.start_len = sizeof(kExpectedSeedStart),
2180c814320Stb 		.expected = kExpectedAdam1024,
2190c814320Stb 		.expected_len = sizeof(kExpectedAdam1024),
2200c814320Stb 	};
2210c814320Stb 	int failed = 0;
2220c814320Stb 
2230c814320Stb 	failed |= MlkemIterativeTest(&iteration768);
2240c814320Stb 	failed |= MlkemIterativeTest(&iteration1024);
2250c814320Stb 
2260c814320Stb 	return failed;
2270c814320Stb }
228