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