xref: /freebsd/crypto/openssh/mlkem_mldsa.sh (revision bb5c77e9d281d6def6835d48249898764bc6a5fe)
1#!/bin/sh
2#       $OpenBSD: mlkem_mldsa.sh,v 1.1 2026/06/14 03:59:34 djm Exp $
3#       Placed in the Public Domain.
4#
5
6WANT_LIBCRUX_REVISION="origin/jonas/combined-extraction-mldsa"
7
8BASE="libcrux/combined_extraction/generated"
9FILES="
10	$BASE/eurydice_glue.h
11	$BASE/combined_core.h
12	$BASE/libcrux_sha3_portable.h
13	$BASE/libcrux_mlkem_core.h
14	$BASE/libcrux_mldsa_core.h
15	$BASE/libcrux_ct_ops.h
16	$BASE/libcrux_mldsa_portable.h
17	$BASE/libcrux_mldsa44_portable.h
18	$BASE/libcrux_mldsa65_portable.h
19	$BASE/libcrux_mldsa87_portable.h
20	$BASE/libcrux_mlkem768_portable.h
21"
22
23START="$PWD"
24die() {
25	echo "$@" 1>&2
26	exit 1
27}
28
29set -xeuo pipefail
30test -d libcrux || git clone https://github.com/cryspen/libcrux
31cd libcrux
32test `git diff | wc -l` -ne 0 && die "tree has unstaged changes"
33git fetch
34git checkout -B extract 1>&2
35git reset --hard $WANT_LIBCRUX_REVISION 1>&2
36LIBCRUX_REVISION=`git rev-parse HEAD`
37set +x
38
39cd $START
40(
41printf '/*  $Open'; printf 'BSD$ */\n' # Sigh
42echo
43echo "/* Extracted from libcrux revision $LIBCRUX_REVISION */"
44echo
45echo '/*'
46cat libcrux/LICENSE-MIT | sed 's/^/ * /;s/ *$//'
47echo ' */'
48echo
49
50LSHIFT="<<"
51cat << _EOF
52#if !defined(__GNUC__) || (__GNUC__ < 2)
53# define __attribute__(x)
54#endif
55#define KRML_MUSTINLINE inline
56#define KRML_NOINLINE __attribute__((noinline, unused))
57#define KRML_HOST_EPRINTF(...)
58#define KRML_HOST_EXIT(x) fatal_f("internal error")
59#define KRML_UNION_CONSTRUCTOR(T)
60
61static inline void
62store64_le(uint8_t dst[8], uint64_t src)
63{
64	dst[0] = src & 0xff;
65	dst[1] = (src >> 8) & 0xff;
66	dst[2] = (src >> 16) & 0xff;
67	dst[3] = (src >> 24) & 0xff;
68	dst[4] = (src >> 32) & 0xff;
69	dst[5] = (src >> 40) & 0xff;
70	dst[6] = (src >> 48) & 0xff;
71	dst[7] = (src >> 56) & 0xff;
72}
73
74static inline void
75store32_le(uint8_t dst[4], uint32_t src)
76{
77	dst[0] = src & 0xff;
78	dst[1] = (src >> 8) & 0xff;
79	dst[2] = (src >> 16) & 0xff;
80	dst[3] = (src >> 24) & 0xff;
81}
82
83static inline void
84store16_le(uint8_t dst[2], uint16_t src)
85{
86	dst[0] = src & 0xff;
87	dst[1] = (src >> 8) & 0xff;
88}
89
90static inline void
91store32_be(uint8_t dst[4], uint32_t src)
92{
93	dst[0] = (src >> 24) & 0xff;
94	dst[1] = (src >> 16) & 0xff;
95	dst[2] = (src >> 8) & 0xff;
96	dst[3] = src & 0xff;
97}
98
99static inline uint64_t
100load64_le(uint8_t src[8])
101{
102	return (uint64_t)(src[0]) |
103	    ((uint64_t)(src[1]) $LSHIFT 8) |
104	    ((uint64_t)(src[2]) $LSHIFT 16) |
105	    ((uint64_t)(src[3]) $LSHIFT 24) |
106	    ((uint64_t)(src[4]) $LSHIFT 32) |
107	    ((uint64_t)(src[5]) $LSHIFT 40) |
108	    ((uint64_t)(src[6]) $LSHIFT 48) |
109	    ((uint64_t)(src[7]) $LSHIFT 56);
110}
111
112static inline uint32_t
113load32_le(uint8_t src[4])
114{
115	return (uint32_t)(src[0]) |
116	    ((uint32_t)(src[1]) $LSHIFT 8) |
117	    ((uint32_t)(src[2]) $LSHIFT 16) |
118	    ((uint32_t)(src[3]) $LSHIFT 24);
119}
120
121static inline uint16_t
122load16_le(uint8_t src[4])
123{
124	return (uint16_t)(src[0]) |
125	    ((uint16_t)(src[1]) $LSHIFT 8);
126}
127
128#ifdef MISSING_BUILTIN_POPCOUNT
129static inline unsigned int
130__builtin_popcount(unsigned int num)
131{
132  const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
133  return v[num & 0xf] + v[(num >> 4) & 0xf];
134}
135#endif
136
137_EOF
138
139for i in $FILES; do
140	echo "/* from $i */"
141	# Changes to all files:
142	#  - remove all includes, we inline everything required.
143	#  - cleanup whitespace
144	#  - convert C++-style constructors to C-style compound literals
145	#  - convert Result constructors to C initializers
146	#  - use anonymous unions to avoid "union U" redefinition errors
147	sed -e "/#include/d" \
148	    -e 's/[	 ]*$//' \
149	    $i | \
150	case "$i" in
151	*/eurydice_glue.h)
152		# Replace endian function for consistency.
153		perl -0777 -pe 's/(static inline void core_num__u32__to_be_bytes.*\n)([^}]*\n)/\1  store32_be(dst, src);\n/'
154		;;
155	# Default: pass through.
156	*)
157		cat
158		;;
159	esac | \
160	perl -0777 -pe 's/ <<\n\s+\(uint32_t\)\(int32_t\)0//g'
161	echo
162done
163
164cat << _EOF
165
166/* rename some types to be a bit more ergonomic */
167
168/* ML-KEM 768 */
169typedef Eurydice_arr_c7 libcrux_mlkem768_keypair_rnd;
170typedef Eurydice_arr_ec libcrux_mlkem768_enc_rnd;
171typedef libcrux_ml_kem_mlkem768_MlKem768KeyPair libcrux_mlkem768_keypair;
172typedef Eurydice_arr_5f libcrux_mlkem768_pk;
173typedef Eurydice_arr_7d libcrux_mlkem768_sk;
174typedef Eurydice_arr_2b libcrux_mlkem768_ciphertext;
175typedef tuple_f4 libcrux_mlkem768_enc_result;
176typedef Eurydice_arr_ec libcrux_mlkem768_dec_result;
177/* ML-DSA 44 */
178typedef Eurydice_arr_ec libcrux_mldsa44_keypair_rnd;
179typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_44_MLDSA44KeyPair
180    libcrux_mldsa44_keypair;
181typedef Eurydice_arr_10 libcrux_mldsa44_sk;
182typedef Eurydice_arr_02 libcrux_mldsa44_pk;
183typedef Eurydice_borrow_slice_u8 libcrux_mldsa44_message;
184typedef Eurydice_arr_ec libcrux_mldsa44_sign_rnd;
185typedef core_result_Result_48 libcrux_mldsa44_sign_result;
186typedef core_result_Result_41 libcrux_mldsa44_verify_result;
187typedef Eurydice_arr_85 libcrux_mldsa44_signature;
188/* ML-DSA 65 */
189typedef Eurydice_arr_ec libcrux_mldsa65_keypair_rnd;
190typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_65_MLDSA65KeyPair
191    libcrux_mldsa65_keypair;
192typedef Eurydice_arr_24 libcrux_mldsa65_sk;
193typedef Eurydice_arr_29 libcrux_mldsa65_pk;
194typedef Eurydice_borrow_slice_u8 libcrux_mldsa65_message;
195typedef Eurydice_arr_ec libcrux_mldsa65_sign_rnd;
196typedef core_result_Result_8c libcrux_mldsa65_sign_result;
197typedef core_result_Result_41 libcrux_mldsa65_verify_result;
198typedef Eurydice_arr_0c libcrux_mldsa65_signature;
199/* ML-DSA 87 */
200typedef Eurydice_arr_ec libcrux_mldsa87_keypair_rnd;
201typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_87_MLDSA87KeyPair
202    libcrux_mldsa87_keypair;
203typedef Eurydice_arr_e2 libcrux_mldsa87_sk;
204typedef Eurydice_arr_43 libcrux_mldsa87_pk;
205typedef Eurydice_borrow_slice_u8 libcrux_mldsa87_message;
206typedef Eurydice_arr_ec libcrux_mldsa87_sign_rnd;
207typedef core_result_Result_8b libcrux_mldsa87_sign_result;
208typedef core_result_Result_41 libcrux_mldsa87_verify_result;
209typedef Eurydice_arr_93 libcrux_mldsa87_signature;
210
211#define LIBCRUX_RESULT_OK	core_result_Ok
212
213_EOF
214
215) > libcrux_internal.h_new
216
217# Do some checks on the resultant file
218
219cat > libcrux_internal_check.c << _EOF
220#include <sys/types.h>
221#include <stdio.h>
222#include <stdint.h>
223#include <stdlib.h>
224#include <stdbool.h>
225#include <string.h>
226#include <signal.h>
227#include <err.h>
228#include "crypto_api.h"
229#define fatal_f(x) exit(1)
230#include "libcrux_internal.h_new"
231
232#define TEST_MLDSA(L) do { \
233	libcrux_mldsa##L##_keypair_rnd kpseed##L = {0}; \
234	libcrux_mldsa##L##_keypair kp##L = {0}; \
235	libcrux_mldsa##L##_sk sk##L = {0}; \
236	libcrux_mldsa##L##_pk pk##L = {0}; \
237	const uint8_t msgdata##L[6] = { 0x4a, 0x75, 0x6c, 0x69, 0x75, 0x73 }; \
238	libcrux_mldsa##L##_message msg##L = { \
239		msgdata##L, sizeof(msgdata##L) \
240	}; \
241	const uint8_t ctxdata##L[4] = { 0x48, 0x75, 0x67, 0x6f }; \
242	libcrux_mldsa##L##_message ctx##L = { \
243		ctxdata##L, sizeof(ctxdata##L) \
244	}; \
245	libcrux_mldsa##L##_sign_rnd signseed##L = {0}; \
246	libcrux_mldsa##L##_sign_result signresult##L = {0}; \
247	libcrux_mldsa##L##_verify_result verifyresult##L = {0}; \
248	libcrux_mldsa##L##_signature sig##L = {0}; \
249	\
250	if (sizeof(kpseed##L.data) != MLDSA##L##_SEEDBYTES) \
251		errx(1, "libcrux_mldsa%d_keypair_rnd bad", L); \
252	if (sizeof(kp##L.verification_key.data) != MLDSA##L##_PUBLICKEYBYTES) \
253		errx(1, "libcrux_mldsa%d_keypair verification_key bad", L); \
254	if (sizeof(kp##L.signing_key.data) != MLDSA##L##_SECRETKEYBYTES) \
255		errx(1, "libcrux_mldsa%d_keypair signing_key bad", L); \
256	if (sizeof(pk##L.data) != MLDSA##L##_PUBLICKEYBYTES) \
257		errx(1, "libcrux_mldsa%d_pk bad", L); \
258	if (sizeof(sk##L.data) != MLDSA##L##_SECRETKEYBYTES) \
259		errx(1, "libcrux_mldsa%d_sk bad", L); \
260	if (sizeof(signresult##L.val.case_Ok.data) != MLDSA##L##_SIGBYTES) \
261		errx(1, "libcrux_mldsa%d_sign_result bad", L); \
262	if (sizeof(sig##L.data) != MLDSA##L##_SIGBYTES) \
263		errx(1, "libcrux_mldsa%d_signature bad", L); \
264	\
265	kp##L = libcrux_ml_dsa_ml_dsa_##L##_portable_generate_key_pair( \
266	    kpseed##L); \
267	sk##L = kp##L.signing_key; \
268	pk##L = kp##L.verification_key; \
269	signresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_sign(&sk##L, \
270	    msg##L, ctx##L, signseed##L); \
271	if (signresult##L.tag != LIBCRUX_RESULT_OK) \
272		errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_sign failed", L); \
273	sig##L = signresult##L.val.case_Ok; \
274	verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
275	    msg##L, ctx##L, &sig##L); \
276	if (verifyresult##L.tag != LIBCRUX_RESULT_OK) \
277		errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify failed", L); \
278	sig##L.data[10] ^= 0x10; /* corrupt the signature */ \
279	verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
280	    msg##L, ctx##L, &sig##L); \
281	if (verifyresult##L.tag == LIBCRUX_RESULT_OK) \
282		errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify fail2", L); \
283} while (0)
284
285int main(void) {
286	libcrux_mlkem768_keypair keypair = {0};
287	libcrux_mlkem768_pk pk = {0};
288	libcrux_mlkem768_sk sk = {0};
289	libcrux_mlkem768_ciphertext ct = {0};
290	libcrux_mlkem768_enc_result enc_result = {0};
291	libcrux_mlkem768_keypair_rnd kp_seed = {0};
292	libcrux_mlkem768_enc_rnd enc_seed = {0};
293	libcrux_mlkem768_dec_result shared_secret = {0};
294	uint8_t shared_key[crypto_kem_mlkem768_BYTES];
295
296	if (sizeof(keypair.pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
297		errx(1, "keypair.pk bad");
298	if (sizeof(keypair.sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
299		errx(1, "keypair.sk bad");
300	if (sizeof(pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
301		errx(1, "pk bad");
302	if (sizeof(sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
303		errx(1, "sk bad");
304	if (sizeof(ct.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
305		errx(1, "ct bad");
306	if (sizeof(enc_result.fst.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
307		errx(1, "enc_result ct bad");
308	if (sizeof(enc_result.snd.data) != crypto_kem_mlkem768_BYTES)
309		errx(1, "enc_result shared key bad");
310	if (sizeof(kp_seed.data) != crypto_kem_mlkem768_KEYPAIRSEEDBYTES)
311		errx(1, "keypair rnd bad");
312	if (sizeof(enc_seed.data) != crypto_kem_mlkem768_ENCSEEDBYTES)
313		errx(1, "keypair rnd bad");
314
315	keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(kp_seed);
316	if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&keypair.pk))
317		errx(1, "valid smoke failed");
318	enc_result = libcrux_ml_kem_mlkem768_portable_encapsulate(&keypair.pk,
319	    enc_seed);
320	shared_secret = libcrux_ml_kem_mlkem768_portable_decapsulate(
321	    &keypair.sk, &enc_result.fst);
322	memcpy(shared_key, shared_secret.data, sizeof(shared_key));
323	if (memcmp(shared_key, enc_result.snd.data, sizeof(shared_key)) != 0)
324		errx(1, "smoke failed");
325
326	TEST_MLDSA(44);
327	TEST_MLDSA(65);
328	TEST_MLDSA(87);
329
330	return 0;
331}
332_EOF
333cc -Wall -Wextra -Wno-unused-parameter -I . -o libcrux_internal_check \
334	libcrux_internal_check.c
335./libcrux_internal_check
336
337mv libcrux_internal.h_new libcrux_internal.h
338rm libcrux_internal_check libcrux_internal_check.c
339echo 1>&2
340echo "libcrux_internal.h OK" 1>&2
341
342