xref: /openbsd-src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c (revision 745491882adade544876937148104d21ba956b8b)
1 /*	$OpenBSD: mlkem_iteration_tests.c,v 1.2 2024/12/26 07:26:45 tb Exp $ */
2 /*
3  * Copyright (c) 2024 Google Inc.
4  * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5  * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
14  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
16  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <err.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 #include "mlkem.h"
26 
27 #include "mlkem_internal.h"
28 #include "mlkem_tests_util.h"
29 #include "sha3_internal.h"
30 
31 /*
32  * Based on https://c2sp.org/CCTV/ML-KEM
33  *
34  * The final value has been updated to reflect the change from Kyber to ML-KEM.
35  *
36  * The deterministic RNG is a single SHAKE-128 instance with an empty input.
37  * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
38  */
39 const uint8_t kExpectedSeedStart[16] = {
40 	0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
41 	0x50, 0x76, 0x05, 0x85, 0x3e
42 };
43 
44 /*
45  * Filippo says:
46  * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3
47  * but Boring believes this:
48  */
49 const uint8_t kExpectedAdam768[32] = {
50 	0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
51 	0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
52 	0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1
53 };
54 
55 /*
56  * Filippo says:
57  * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543
58  * but Boring believes this:
59  */
60 const uint8_t kExpectedAdam1024[32] = {
61 	0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
62 	0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
63 	0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
64 };
65 
66 struct iteration_ctx {
67 	uint8_t *encoded_public_key;
68 	size_t encoded_public_key_len;
69 	uint8_t *ciphertext;
70 	size_t ciphertext_len;
71 	uint8_t *invalid_ciphertext;
72 	size_t invalid_ciphertext_len;
73 	void *priv;
74 	void *pub;
75 
76 	mlkem_encode_private_key_fn encode_private_key;
77 	mlkem_encap_external_entropy_fn encap_external_entropy;
78 	mlkem_generate_key_external_entropy_fn generate_key_external_entropy;
79 	mlkem_public_from_private_fn public_from_private;
80 	mlkem_decap_fn decap;
81 
82 	const uint8_t *start;
83 	size_t start_len;
84 
85 	const uint8_t *expected;
86 	size_t expected_len;
87 };
88 
89 static int
90 MlkemIterativeTest(struct iteration_ctx *ctx)
91 {
92 	uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
93 	uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
94 	uint8_t seed[MLKEM_SEED_BYTES] = {0};
95 	sha3_ctx drng, results;
96 	uint8_t out[32];
97 	int i;
98 
99 	shake128_init(&drng);
100 	shake128_init(&results);
101 
102 	shake_xof(&drng);
103 	for (i = 0; i < 10000; i++) {
104 		uint8_t *encoded_private_key = NULL;
105 		size_t encoded_private_key_len;
106 
107 		/*
108 		 * This should draw both d and z from DRNG concatenating in
109 		 * seed.
110 		 */
111 		shake_out(&drng, seed, sizeof(seed));
112 		if (i == 0) {
113 			if (compare_data(seed, ctx->start, ctx->start_len,
114 			    "seed start") != 0)
115 				errx(1, "compare_data");
116 		}
117 
118 		/* generate ek as encoded_public_key */
119 		ctx->generate_key_external_entropy(ctx->encoded_public_key,
120 		    ctx->priv, seed);
121 		ctx->public_from_private(ctx->pub, ctx->priv);
122 
123 		/* hash in ek */
124 		shake_update(&results, ctx->encoded_public_key,
125 		    ctx->encoded_public_key_len);
126 
127 		/* marshal priv to dk as encoded_private_key */
128 		if (!ctx->encode_private_key(ctx->priv, &encoded_private_key,
129 		    &encoded_private_key_len))
130 			errx(1, "encode private key");
131 
132 		/* hash in dk */
133 		shake_update(&results, encoded_private_key,
134 		    encoded_private_key_len);
135 
136 		free(encoded_private_key);
137 
138 		/* draw m as encap entropy from DRNG */
139 		shake_out(&drng, encap_entropy, sizeof(encap_entropy));
140 
141 		/* generate ct as ciphertext, k as shared_secret */
142 		ctx->encap_external_entropy(ctx->ciphertext, shared_secret,
143 		    ctx->pub, encap_entropy);
144 
145 		/* hash in ct */
146 		shake_update(&results, ctx->ciphertext, ctx->ciphertext_len);
147 		/* hash in k */
148 		shake_update(&results, shared_secret, sizeof(shared_secret));
149 
150 		/* draw ct as invalid_ciphertxt from DRNG */
151 		shake_out(&drng, ctx->invalid_ciphertext,
152 		    ctx->invalid_ciphertext_len);
153 
154 		/* generate k as shared secret from invalid ciphertext */
155 		if (!ctx->decap(shared_secret, ctx->invalid_ciphertext,
156 		    ctx->invalid_ciphertext_len, ctx->priv))
157 			errx(1, "decap failed");
158 
159 		/* hash in k */
160 		shake_update(&results, shared_secret, sizeof(shared_secret));
161 	}
162 	shake_xof(&results);
163 	shake_out(&results, out, sizeof(out));
164 
165 	return compare_data(ctx->expected, out, sizeof(out), "final result hash");
166 }
167 
168 int
169 main(void)
170 {
171 	uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES];
172 	uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
173 	uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
174 	struct MLKEM768_private_key priv768;
175 	struct MLKEM768_public_key pub768;
176 	struct iteration_ctx iteration768 = {
177 		.encoded_public_key = encoded_public_key768,
178 		.encoded_public_key_len = sizeof(encoded_public_key768),
179 		.ciphertext = ciphertext768,
180 		.ciphertext_len = sizeof(ciphertext768),
181 		.invalid_ciphertext = invalid_ciphertext768,
182 		.invalid_ciphertext_len = sizeof(invalid_ciphertext768),
183 		.priv = &priv768,
184 		.pub = &pub768,
185 		.encap_external_entropy = mlkem768_encap_external_entropy,
186 		.encode_private_key = mlkem768_encode_private_key,
187 		.generate_key_external_entropy =
188 		    mlkem768_generate_key_external_entropy,
189 		.public_from_private = mlkem768_public_from_private,
190 		.decap = mlkem768_decap,
191 		.start = kExpectedSeedStart,
192 		.start_len = sizeof(kExpectedSeedStart),
193 		.expected = kExpectedAdam768,
194 		.expected_len = sizeof(kExpectedAdam768),
195 	};
196 	uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES];
197 	uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
198 	uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
199 	struct MLKEM1024_private_key priv1024;
200 	struct MLKEM1024_public_key pub1024;
201 	struct iteration_ctx iteration1024 = {
202 		.encoded_public_key = encoded_public_key1024,
203 		.encoded_public_key_len = sizeof(encoded_public_key1024),
204 		.ciphertext = ciphertext1024,
205 		.ciphertext_len = sizeof(ciphertext1024),
206 		.invalid_ciphertext = invalid_ciphertext1024,
207 		.invalid_ciphertext_len = sizeof(invalid_ciphertext1024),
208 		.priv = &priv1024,
209 		.pub = &pub1024,
210 		.encap_external_entropy = mlkem1024_encap_external_entropy,
211 		.encode_private_key = mlkem1024_encode_private_key,
212 		.generate_key_external_entropy =
213 		    mlkem1024_generate_key_external_entropy,
214 		.public_from_private = mlkem1024_public_from_private,
215 		.decap = mlkem1024_decap,
216 		.start = kExpectedSeedStart,
217 		.start_len = sizeof(kExpectedSeedStart),
218 		.expected = kExpectedAdam1024,
219 		.expected_len = sizeof(kExpectedAdam1024),
220 	};
221 	int failed = 0;
222 
223 	failed |= MlkemIterativeTest(&iteration768);
224 	failed |= MlkemIterativeTest(&iteration1024);
225 
226 	return failed;
227 }
228