diff options
author | beck <> | 2025-08-14 15:48:48 +0000 |
---|---|---|
committer | beck <> | 2025-08-14 15:48:48 +0000 |
commit | 6452fa9fc6f33dac80ee572764b9fe29a469f8ce (patch) | |
tree | 0956ae670e4f193442bcf99d2b1fb70a43a6b5b5 /src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c | |
parent | 9bef27f78e41e8026f1d588e4e36e385061f3deb (diff) | |
download | openbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.tar.gz openbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.tar.bz2 openbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.zip |
Add a reasonable ML-KEM API for public use.
Adapt the tests to use this API.
This does not yet make the symbols public in Symbols.list
which will happen shortly with a bump.
This includes some partial rototilling of the non-public
interfaces which will be shortly continued when the internal
code is deduplicated to not have multiple copies for ML-KEM
768 and ML-KEM 1024 (which is just an artifact of unravelling
the boring C++ code).
ok jsing@, tb@
Diffstat (limited to 'src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c')
-rw-r--r-- | src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c | 179 |
1 files changed, 77 insertions, 102 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c index b93243023c..10c4b1f4e0 100644 --- a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c +++ b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: mlkem_iteration_tests.c,v 1.5 2025/05/20 00:33:41 beck Exp $ */ | 1 | /* $OpenBSD: mlkem_iteration_tests.c,v 1.6 2025/08/14 15:48:48 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2024 Google Inc. | 3 | * Copyright (c) 2024 Google Inc. |
4 | * Copyright (c) 2024 Bob Beck <beck@obtuse.com> | 4 | * Copyright (c) 2024 Bob Beck <beck@obtuse.com> |
@@ -21,6 +21,7 @@ | |||
21 | #include <stdint.h> | 21 | #include <stdint.h> |
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <string.h> | ||
24 | 25 | ||
25 | #include "mlkem.h" | 26 | #include "mlkem.h" |
26 | 27 | ||
@@ -63,46 +64,49 @@ const uint8_t kExpectedAdam1024[32] = { | |||
63 | 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 | 64 | 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 |
64 | }; | 65 | }; |
65 | 66 | ||
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_marshal_private_key_fn marshal_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 | 67 | static int |
90 | MlkemIterativeTest(struct iteration_ctx *ctx) | 68 | MlkemIterativeTest(int rank) |
91 | { | 69 | { |
92 | uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; | 70 | const uint8_t *start, *expected; |
71 | size_t start_len; | ||
93 | uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; | 72 | uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; |
94 | uint8_t seed[MLKEM_SEED_BYTES] = {0}; | 73 | uint8_t seed[MLKEM_SEED_LENGTH] = {0}; |
74 | uint8_t *shared_secret = NULL; | ||
95 | sha3_ctx drng, results; | 75 | sha3_ctx drng, results; |
96 | uint8_t out[32]; | 76 | uint8_t out[32]; |
97 | int i; | 77 | int i; |
98 | 78 | ||
79 | start = kExpectedSeedStart; | ||
80 | start_len = sizeof(kExpectedSeedStart); | ||
81 | switch(rank){ | ||
82 | case RANK768: | ||
83 | expected = kExpectedAdam768; | ||
84 | break; | ||
85 | case RANK1024: | ||
86 | expected = kExpectedAdam1024; | ||
87 | break; | ||
88 | default: | ||
89 | errx(1, "invalid rank %d", rank); | ||
90 | } | ||
91 | |||
99 | shake128_init(&drng); | 92 | shake128_init(&drng); |
100 | shake128_init(&results); | 93 | shake128_init(&results); |
101 | 94 | ||
102 | shake_xof(&drng); | 95 | shake_xof(&drng); |
103 | for (i = 0; i < 10000; i++) { | 96 | for (i = 0; i < 10000; i++) { |
104 | uint8_t *encoded_private_key = NULL; | 97 | uint8_t *encoded_public_key = NULL, *ciphertext = NULL, |
105 | size_t encoded_private_key_len; | 98 | *encoded_private_key = NULL, *invalid_ciphertext = NULL; |
99 | size_t encoded_public_key_len, ciphertext_len, | ||
100 | encoded_private_key_len, invalid_ciphertext_len; | ||
101 | MLKEM_private_key *priv; | ||
102 | MLKEM_public_key *pub; | ||
103 | size_t s_len = 0; | ||
104 | |||
105 | /* allocate keys for this iteration */ | ||
106 | if ((priv = MLKEM_private_key_new(rank)) == NULL) | ||
107 | errx(1, "malloc"); | ||
108 | if ((pub = MLKEM_public_key_new(rank)) == NULL) | ||
109 | errx(1, "malloc"); | ||
106 | 110 | ||
107 | /* | 111 | /* |
108 | * This should draw both d and z from DRNG concatenating in | 112 | * This should draw both d and z from DRNG concatenating in |
@@ -110,120 +114,91 @@ MlkemIterativeTest(struct iteration_ctx *ctx) | |||
110 | */ | 114 | */ |
111 | shake_out(&drng, seed, sizeof(seed)); | 115 | shake_out(&drng, seed, sizeof(seed)); |
112 | if (i == 0) { | 116 | if (i == 0) { |
113 | if (compare_data(seed, ctx->start, ctx->start_len, | 117 | if (compare_data(seed, start, start_len, |
114 | "seed start") != 0) | 118 | "seed start") != 0) |
115 | errx(1, "compare_data"); | 119 | errx(1, "compare_data"); |
116 | } | 120 | } |
117 | 121 | ||
118 | /* generate ek as encoded_public_key */ | 122 | /* generate ek as encoded_public_key */ |
119 | if (!ctx->generate_key_external_entropy(ctx->encoded_public_key, | 123 | if (!MLKEM_generate_key_external_entropy(priv, |
120 | ctx->priv, seed)) { | 124 | &encoded_public_key, &encoded_public_key_len, |
125 | seed)) | ||
121 | errx(1, "generate_key_external_entropy"); | 126 | errx(1, "generate_key_external_entropy"); |
122 | } | 127 | |
123 | ctx->public_from_private(ctx->pub, ctx->priv); | 128 | if (!MLKEM_public_from_private(priv, pub)) |
129 | errx(1, "public_from_private"); | ||
124 | 130 | ||
125 | /* hash in ek */ | 131 | /* hash in ek */ |
126 | shake_update(&results, ctx->encoded_public_key, | 132 | shake_update(&results, encoded_public_key, |
127 | ctx->encoded_public_key_len); | 133 | encoded_public_key_len); |
128 | 134 | ||
129 | /* marshal priv to dk as encoded_private_key */ | 135 | /* marshal priv to dk as encoded_private_key */ |
130 | if (!ctx->marshal_private_key(ctx->priv, &encoded_private_key, | 136 | if (!MLKEM_marshal_private_key(priv, &encoded_private_key, |
131 | &encoded_private_key_len)) | 137 | &encoded_private_key_len)) |
132 | errx(1, "encode private key"); | 138 | errx(1, "marshal private key"); |
133 | 139 | ||
134 | /* hash in dk */ | 140 | /* hash in dk */ |
135 | shake_update(&results, encoded_private_key, | 141 | shake_update(&results, encoded_private_key, |
136 | encoded_private_key_len); | 142 | encoded_private_key_len); |
137 | 143 | ||
138 | free(encoded_private_key); | 144 | freezero(encoded_private_key, encoded_private_key_len); |
139 | 145 | ||
140 | /* draw m as encap entropy from DRNG */ | 146 | /* draw m as encap entropy from DRNG */ |
141 | shake_out(&drng, encap_entropy, sizeof(encap_entropy)); | 147 | shake_out(&drng, encap_entropy, sizeof(encap_entropy)); |
142 | 148 | ||
143 | /* generate ct as ciphertext, k as shared_secret */ | 149 | /* generate ct as ciphertext, k as shared_secret */ |
144 | ctx->encap_external_entropy(ctx->ciphertext, shared_secret, | 150 | if (!MLKEM_encap_external_entropy(pub, encap_entropy, |
145 | ctx->pub, encap_entropy); | 151 | &ciphertext, &ciphertext_len, &shared_secret, &s_len)) |
152 | errx(1, "encap_external_entropy"); | ||
146 | 153 | ||
147 | /* hash in ct */ | 154 | /* hash in ct */ |
148 | shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); | 155 | shake_update(&results, ciphertext, ciphertext_len); |
149 | /* hash in k */ | 156 | /* hash in k */ |
150 | shake_update(&results, shared_secret, sizeof(shared_secret)); | 157 | shake_update(&results, shared_secret, s_len); |
158 | |||
159 | freezero(shared_secret, s_len); | ||
160 | shared_secret = NULL; | ||
161 | |||
162 | invalid_ciphertext_len = ciphertext_len; | ||
163 | if ((invalid_ciphertext = calloc(1, invalid_ciphertext_len)) | ||
164 | == NULL) | ||
165 | errx(1, "malloc"); | ||
151 | 166 | ||
152 | /* draw ct as invalid_ciphertxt from DRNG */ | 167 | /* draw ct as invalid_ciphertxt from DRNG */ |
153 | shake_out(&drng, ctx->invalid_ciphertext, | 168 | shake_out(&drng, invalid_ciphertext, invalid_ciphertext_len); |
154 | ctx->invalid_ciphertext_len); | ||
155 | 169 | ||
156 | /* generate k as shared secret from invalid ciphertext */ | 170 | /* generate k as shared secret from invalid ciphertext */ |
157 | if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, | 171 | if (!MLKEM_decap(priv, invalid_ciphertext, |
158 | ctx->invalid_ciphertext_len, ctx->priv)) | 172 | invalid_ciphertext_len, &shared_secret, &s_len)) |
159 | errx(1, "decap failed"); | 173 | errx(1, "decap failed, iteration %d", i); |
160 | 174 | ||
161 | /* hash in k */ | 175 | /* hash in k */ |
162 | shake_update(&results, shared_secret, sizeof(shared_secret)); | 176 | shake_update(&results, shared_secret, s_len); |
177 | |||
178 | freezero(shared_secret, s_len); | ||
179 | shared_secret = NULL; | ||
180 | freezero(invalid_ciphertext, invalid_ciphertext_len); | ||
181 | invalid_ciphertext = NULL; | ||
182 | |||
183 | /* free keys and intermediate products for this iteration */ | ||
184 | MLKEM_private_key_free(priv); | ||
185 | MLKEM_public_key_free(pub); | ||
186 | freezero(encoded_public_key, encoded_public_key_len); | ||
187 | freezero(ciphertext, ciphertext_len); | ||
163 | } | 188 | } |
164 | shake_xof(&results); | 189 | shake_xof(&results); |
165 | shake_out(&results, out, sizeof(out)); | 190 | shake_out(&results, out, sizeof(out)); |
166 | 191 | ||
167 | return compare_data(ctx->expected, out, sizeof(out), "final result hash"); | 192 | return compare_data(expected, out, sizeof(out), "final result hash"); |
168 | } | 193 | } |
169 | 194 | ||
170 | int | 195 | int |
171 | main(void) | 196 | main(void) |
172 | { | 197 | { |
173 | uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; | ||
174 | uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; | ||
175 | uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; | ||
176 | struct MLKEM768_private_key priv768; | ||
177 | struct MLKEM768_public_key pub768; | ||
178 | struct iteration_ctx iteration768 = { | ||
179 | .encoded_public_key = encoded_public_key768, | ||
180 | .encoded_public_key_len = sizeof(encoded_public_key768), | ||
181 | .ciphertext = ciphertext768, | ||
182 | .ciphertext_len = sizeof(ciphertext768), | ||
183 | .invalid_ciphertext = invalid_ciphertext768, | ||
184 | .invalid_ciphertext_len = sizeof(invalid_ciphertext768), | ||
185 | .priv = &priv768, | ||
186 | .pub = &pub768, | ||
187 | .encap_external_entropy = mlkem768_encap_external_entropy, | ||
188 | .marshal_private_key = mlkem768_marshal_private_key, | ||
189 | .generate_key_external_entropy = | ||
190 | mlkem768_generate_key_external_entropy, | ||
191 | .public_from_private = mlkem768_public_from_private, | ||
192 | .decap = mlkem768_decap, | ||
193 | .start = kExpectedSeedStart, | ||
194 | .start_len = sizeof(kExpectedSeedStart), | ||
195 | .expected = kExpectedAdam768, | ||
196 | .expected_len = sizeof(kExpectedAdam768), | ||
197 | }; | ||
198 | uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; | ||
199 | uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; | ||
200 | uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; | ||
201 | struct MLKEM1024_private_key priv1024; | ||
202 | struct MLKEM1024_public_key pub1024; | ||
203 | struct iteration_ctx iteration1024 = { | ||
204 | .encoded_public_key = encoded_public_key1024, | ||
205 | .encoded_public_key_len = sizeof(encoded_public_key1024), | ||
206 | .ciphertext = ciphertext1024, | ||
207 | .ciphertext_len = sizeof(ciphertext1024), | ||
208 | .invalid_ciphertext = invalid_ciphertext1024, | ||
209 | .invalid_ciphertext_len = sizeof(invalid_ciphertext1024), | ||
210 | .priv = &priv1024, | ||
211 | .pub = &pub1024, | ||
212 | .encap_external_entropy = mlkem1024_encap_external_entropy, | ||
213 | .marshal_private_key = mlkem1024_marshal_private_key, | ||
214 | .generate_key_external_entropy = | ||
215 | mlkem1024_generate_key_external_entropy, | ||
216 | .public_from_private = mlkem1024_public_from_private, | ||
217 | .decap = mlkem1024_decap, | ||
218 | .start = kExpectedSeedStart, | ||
219 | .start_len = sizeof(kExpectedSeedStart), | ||
220 | .expected = kExpectedAdam1024, | ||
221 | .expected_len = sizeof(kExpectedAdam1024), | ||
222 | }; | ||
223 | int failed = 0; | 198 | int failed = 0; |
224 | 199 | ||
225 | failed |= MlkemIterativeTest(&iteration768); | 200 | failed |= MlkemIterativeTest(RANK768); |
226 | failed |= MlkemIterativeTest(&iteration1024); | 201 | failed |= MlkemIterativeTest(RANK1024); |
227 | 202 | ||
228 | return failed; | 203 | return failed; |
229 | } | 204 | } |