summaryrefslogtreecommitdiff
path: root/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
diff options
context:
space:
mode:
authorbeck <>2024-12-13 00:03:57 +0000
committerbeck <>2024-12-13 00:03:57 +0000
commitc3bf83f7cf1ff567aae1e260425898b2af6bf4cc (patch)
treea9ff1c725db56dbeb46224505b3dd6fd05a21777 /src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
parentf0b174e51673db27d4324ea95b112eb95ad05291 (diff)
downloadopenbsd-c3bf83f7cf1ff567aae1e260425898b2af6bf4cc.tar.gz
openbsd-c3bf83f7cf1ff567aae1e260425898b2af6bf4cc.tar.bz2
openbsd-c3bf83f7cf1ff567aae1e260425898b2af6bf4cc.zip
Add ML-KEM 768 from BoringSSL
Changes include conversion from C++, basic KNF, then adaptation to use our sha3 functions for sha3 and shake instead of the BorinSSL version. This Adds units tests to run against BoringSSL and NIST test vectors. The future public API is the same as Boring's - but is not yet exposed pending making bytesring.h public (which will happen separately) and a minor bump Currently this will just ensure we build and run regress. ok tb@ to get it into the tree and massage from there.
Diffstat (limited to 'src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c')
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c b/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
new file mode 100644
index 0000000000..4df8171273
--- /dev/null
+++ b/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
@@ -0,0 +1,157 @@
1/* Copyright (c) 2024, Google Inc.
2 * Copyright (c) 2024, Bob Beck <beck@obtuse.com>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
15
16#include <stdlib.h>
17#include <stdio.h>
18#include <string.h>
19
20#include "bytestring.h"
21
22#include "sha3_internal.h"
23#include "mlkem.h"
24#include "mlkem_internal.h"
25#include "mlkem_tests_util.h"
26
27static int
28encode_private_key(const struct MLKEM768_private_key *priv, uint8_t **out_buf,
29 size_t *out_len)
30{
31 CBB cbb;
32 if (!CBB_init(&cbb, MLKEM768_PUBLIC_KEY_BYTES))
33 return 0;
34 if (!MLKEM768_marshal_private_key(&cbb, priv))
35 return 0;
36 if (!CBB_finish(&cbb, out_buf, out_len))
37 return 0;
38 CBB_cleanup(&cbb);
39 return 1;
40}
41
42/*
43 * The structure of this test is taken from
44 * https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
45 * but the final value has been updated to reflect the change from Kyber to
46 * ML-KEM.
47 *
48 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
49 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
50 */
51
52static void
53MlkemIterativeTest()
54{
55 /* https://github.com/C2SP/CCTV/tree/main/ML-KEM */
56 /*
57 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
58 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
59 */
60 const uint8_t kExpectedSeedStart[16] = {
61 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
62 0x50, 0x76, 0x05, 0x85, 0x3e
63 };
64 /*
65 * Filippo says:
66 * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3
67 * but Boring believes this:
68 */
69 const uint8_t kExpectedAdam[32] = {
70 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
71 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
72 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1
73 };
74 uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
75 uint8_t invalid_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
76 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
77 uint8_t ciphertext[MLKEM768_CIPHERTEXT_BYTES];
78 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
79 uint8_t seed[MLKEM_SEED_BYTES] = {0};
80 struct MLKEM768_private_key priv;
81 struct MLKEM768_public_key pub;
82 sha3_ctx drng, results;
83 uint8_t out[32];
84 int i;
85
86 shake128_init(&drng);
87 shake128_init(&results);
88
89 shake_xof(&drng);
90 for (i = 0; i < 10000; i++) {
91 uint8_t *encoded_private_key = NULL;
92 size_t encoded_private_key_len;
93
94 /*
95 * This should draw both d and z from DRNG concatenating in
96 * seed.
97 */
98 shake_out(&drng, seed, sizeof(seed));
99 if (i == 0) {
100 TEST_DATAEQ(seed, kExpectedSeedStart,
101 sizeof(kExpectedSeedStart), "seed start");
102 }
103
104 /* generate ek as encoded_public_key */
105 MLKEM768_generate_key_external_entropy(encoded_public_key,
106 &priv, seed);
107 MLKEM768_public_from_private(&pub, &priv);
108
109 /* hash in ek */
110 shake_update(&results, encoded_public_key,
111 sizeof(encoded_public_key));
112
113 /* marshal priv to dk as encoded_private_key */
114 TEST(!encode_private_key(&priv, &encoded_private_key,
115 &encoded_private_key_len), "encode_private_key");
116
117 /* hash in dk */
118 shake_update(&results, encoded_private_key,
119 encoded_private_key_len);
120
121 free(encoded_private_key);
122
123 /* draw m as encap entropy from DRNG */
124 shake_out(&drng, encap_entropy, sizeof(encap_entropy));
125
126 /* generate ct as ciphertext, k as shared_secret */
127 MLKEM768_encap_external_entropy(ciphertext, shared_secret,
128 &pub, encap_entropy);
129
130 /* hash in ct */
131 shake_update(&results, ciphertext, sizeof(ciphertext));
132 /* hash in k */
133 shake_update(&results, shared_secret, sizeof(shared_secret));
134
135 /* draw ct as invalid_ciphertxt from DRNG */
136 shake_out(&drng, invalid_ciphertext,
137 sizeof(invalid_ciphertext));
138
139 /* generte k as shared secret from invalid ciphertext */
140 TEST(!MLKEM768_decap(shared_secret, invalid_ciphertext,
141 sizeof(invalid_ciphertext), &priv), "decap failed!");
142
143 /* hash in k */
144 shake_update(&results, shared_secret, sizeof(shared_secret));
145 }
146 shake_xof(&results);
147 shake_out(&results, out, 32);
148
149 TEST_DATAEQ(out, kExpectedAdam, 32, "final result hash");
150}
151
152int
153main(int argc, char **argv)
154{
155 MlkemIterativeTest();
156 exit(failure);
157}