diff options
Diffstat (limited to 'src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c')
-rw-r--r-- | src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c new file mode 100644 index 0000000000..1ecda95bb9 --- /dev/null +++ b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* $OpenBSD: mlkem_iteration_tests.c,v 1.1 2024/12/26 00:04:24 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 | typedef void (*mlkem_public_from_private_fn)(void *out_public_key, | ||
67 | const void *private_key); | ||
68 | |||
69 | struct iteration_ctx { | ||
70 | uint8_t *encoded_public_key; | ||
71 | size_t encoded_public_key_len; | ||
72 | uint8_t *ciphertext; | ||
73 | size_t ciphertext_len; | ||
74 | uint8_t *invalid_ciphertext; | ||
75 | size_t invalid_ciphertext_len; | ||
76 | void *priv; | ||
77 | void *pub; | ||
78 | |||
79 | mlkem_encode_private_key_fn encode_private_key; | ||
80 | mlkem_encap_external_entropy_fn encap_external_entropy; | ||
81 | mlkem_generate_key_external_entropy_fn generate_key_external_entropy; | ||
82 | mlkem_public_from_private_fn public_from_private; | ||
83 | mlkem_decap_fn decap; | ||
84 | |||
85 | const uint8_t *start; | ||
86 | size_t start_len; | ||
87 | |||
88 | const uint8_t *expected; | ||
89 | size_t expected_len; | ||
90 | }; | ||
91 | |||
92 | static int | ||
93 | MlkemIterativeTest(struct iteration_ctx *ctx) | ||
94 | { | ||
95 | uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; | ||
96 | uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; | ||
97 | uint8_t seed[MLKEM_SEED_BYTES] = {0}; | ||
98 | sha3_ctx drng, results; | ||
99 | uint8_t out[32]; | ||
100 | int i; | ||
101 | |||
102 | shake128_init(&drng); | ||
103 | shake128_init(&results); | ||
104 | |||
105 | shake_xof(&drng); | ||
106 | for (i = 0; i < 10000; i++) { | ||
107 | uint8_t *encoded_private_key = NULL; | ||
108 | size_t encoded_private_key_len; | ||
109 | |||
110 | /* | ||
111 | * This should draw both d and z from DRNG concatenating in | ||
112 | * seed. | ||
113 | */ | ||
114 | shake_out(&drng, seed, sizeof(seed)); | ||
115 | if (i == 0) { | ||
116 | if (compare_data(seed, ctx->start, ctx->start_len, | ||
117 | "seed start") != 0) | ||
118 | errx(1, "compare_data"); | ||
119 | } | ||
120 | |||
121 | /* generate ek as encoded_public_key */ | ||
122 | ctx->generate_key_external_entropy(ctx->encoded_public_key, | ||
123 | ctx->priv, seed); | ||
124 | ctx->public_from_private(ctx->pub, ctx->priv); | ||
125 | |||
126 | /* hash in ek */ | ||
127 | shake_update(&results, ctx->encoded_public_key, | ||
128 | ctx->encoded_public_key_len); | ||
129 | |||
130 | /* marshal priv to dk as encoded_private_key */ | ||
131 | if (!ctx->encode_private_key(ctx->priv, &encoded_private_key, | ||
132 | &encoded_private_key_len)) | ||
133 | errx(1, "encode private key"); | ||
134 | |||
135 | /* hash in dk */ | ||
136 | shake_update(&results, encoded_private_key, | ||
137 | encoded_private_key_len); | ||
138 | |||
139 | free(encoded_private_key); | ||
140 | |||
141 | /* draw m as encap entropy from DRNG */ | ||
142 | shake_out(&drng, encap_entropy, sizeof(encap_entropy)); | ||
143 | |||
144 | /* generate ct as ciphertext, k as shared_secret */ | ||
145 | ctx->encap_external_entropy(ctx->ciphertext, shared_secret, | ||
146 | ctx->pub, encap_entropy); | ||
147 | |||
148 | /* hash in ct */ | ||
149 | shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); | ||
150 | /* hash in k */ | ||
151 | shake_update(&results, shared_secret, sizeof(shared_secret)); | ||
152 | |||
153 | /* draw ct as invalid_ciphertxt from DRNG */ | ||
154 | shake_out(&drng, ctx->invalid_ciphertext, | ||
155 | ctx->invalid_ciphertext_len); | ||
156 | |||
157 | /* generate k as shared secret from invalid ciphertext */ | ||
158 | if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, | ||
159 | ctx->invalid_ciphertext_len, ctx->priv)) | ||
160 | errx(1, "decap failed"); | ||
161 | |||
162 | /* hash in k */ | ||
163 | shake_update(&results, shared_secret, sizeof(shared_secret)); | ||
164 | } | ||
165 | shake_xof(&results); | ||
166 | shake_out(&results, out, sizeof(out)); | ||
167 | |||
168 | return compare_data(ctx->expected, out, sizeof(out), "final result hash"); | ||
169 | } | ||
170 | |||
171 | int | ||
172 | main(void) | ||
173 | { | ||
174 | uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; | ||
175 | uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; | ||
176 | uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; | ||
177 | struct MLKEM768_private_key priv768; | ||
178 | struct MLKEM768_public_key pub768; | ||
179 | struct iteration_ctx iteration768 = { | ||
180 | .encoded_public_key = encoded_public_key768, | ||
181 | .encoded_public_key_len = sizeof(encoded_public_key768), | ||
182 | .ciphertext = ciphertext768, | ||
183 | .ciphertext_len = sizeof(ciphertext768), | ||
184 | .invalid_ciphertext = invalid_ciphertext768, | ||
185 | .invalid_ciphertext_len = sizeof(invalid_ciphertext768), | ||
186 | .priv = &priv768, | ||
187 | .pub = &pub768, | ||
188 | .encap_external_entropy = mlkem768_encap_external_entropy, | ||
189 | .encode_private_key = mlkem768_encode_private_key, | ||
190 | .generate_key_external_entropy = | ||
191 | mlkem768_generate_key_external_entropy, | ||
192 | .public_from_private = mlkem768_public_from_private, | ||
193 | .decap = mlkem768_decap, | ||
194 | .start = kExpectedSeedStart, | ||
195 | .start_len = sizeof(kExpectedSeedStart), | ||
196 | .expected = kExpectedAdam768, | ||
197 | .expected_len = sizeof(kExpectedAdam768), | ||
198 | }; | ||
199 | uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; | ||
200 | uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; | ||
201 | uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; | ||
202 | struct MLKEM1024_private_key priv1024; | ||
203 | struct MLKEM1024_public_key pub1024; | ||
204 | struct iteration_ctx iteration1024 = { | ||
205 | .encoded_public_key = encoded_public_key1024, | ||
206 | .encoded_public_key_len = sizeof(encoded_public_key1024), | ||
207 | .ciphertext = ciphertext1024, | ||
208 | .ciphertext_len = sizeof(ciphertext1024), | ||
209 | .invalid_ciphertext = invalid_ciphertext1024, | ||
210 | .invalid_ciphertext_len = sizeof(invalid_ciphertext1024), | ||
211 | .priv = &priv1024, | ||
212 | .pub = &pub1024, | ||
213 | .encap_external_entropy = mlkem1024_encap_external_entropy, | ||
214 | .encode_private_key = mlkem1024_encode_private_key, | ||
215 | .generate_key_external_entropy = | ||
216 | mlkem1024_generate_key_external_entropy, | ||
217 | .public_from_private = mlkem1024_public_from_private, | ||
218 | .decap = mlkem1024_decap, | ||
219 | .start = kExpectedSeedStart, | ||
220 | .start_len = sizeof(kExpectedSeedStart), | ||
221 | .expected = kExpectedAdam1024, | ||
222 | .expected_len = sizeof(kExpectedAdam1024), | ||
223 | }; | ||
224 | int failed = 0; | ||
225 | |||
226 | failed |= MlkemIterativeTest(&iteration768); | ||
227 | failed |= MlkemIterativeTest(&iteration1024); | ||
228 | |||
229 | return failed; | ||
230 | } | ||