Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mlkem768x25519 Kex as per draft-kampanakis-curdle-ssh-pq-ke #342

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
# sntrup761.c is not c89 compliant
localoptions: |
#define DROPBEAR_SNTRUP761 0
#define DROPBEAR_MLKEM768 0

- name: macos 14
os: macos-14
Expand Down
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ _CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
tcp-accept.o listener.o process-packet.o dh_groups.o \
common-runopts.o circbuffer.o list.o netio.o chachapoly.o gcm.o \
kex-x25519.o kex-dh.o kex-ecdh.o kex-pqhybrid.o \
sntrup761.o
sntrup761.o mlkem768.o
CLISVROBJS = $(patsubst %,$(OBJ_DIR)/%,$(_CLISVROBJS))

_KEYOBJS=dropbearkey.o
Expand Down
155 changes: 155 additions & 0 deletions mlkem768.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/bin/sh
# $OpenBSD: mlkem768.sh,v 1.3 2024/10/27 02:06:01 djm Exp $
# Placed in the Public Domain.
#

#WANT_LIBCRUX_REVISION="origin/main"
WANT_LIBCRUX_REVISION="84c5d87b3092c59294345aa269ceefe0eb97cc35"

FILES="
libcrux/libcrux-ml-kem/cg/eurydice_glue.h
libcrux/libcrux-ml-kem/cg/libcrux_core.h
libcrux/libcrux-ml-kem/cg/libcrux_ct_ops.h
libcrux/libcrux-ml-kem/cg/libcrux_sha3_portable.h
libcrux/libcrux-ml-kem/cg/libcrux_mlkem768_portable.h
"

START="$PWD"
die() {
echo "$@" 1>&2
exit 1
}

set -xeuo pipefail
test -d libcrux || git clone https://github.com/cryspen/libcrux
cd libcrux
test `git diff | wc -l` -ne 0 && die "tree has unstaged changes"
git fetch
git checkout -B extract 1>&2
git reset --hard $WANT_LIBCRUX_REVISION 1>&2
LIBCRUX_REVISION=`git rev-parse HEAD`
set +x

cd $START
(
printf '/* $Open'; printf 'BSD$ */\n' # Sigh
echo
echo "/* Extracted from libcrux revision $LIBCRUX_REVISION */"
echo
echo '/*'
cat libcrux/LICENSE-MIT | sed 's/^/ * /;s/ *$//'
echo ' */'
echo
echo '#if !defined(__GNUC__) || (__GNUC__ < 2)'
echo '# define __attribute__(x)'
echo '#endif'
echo '#define KRML_MUSTINLINE inline'
echo '#define KRML_NOINLINE __attribute__((noinline, unused))'
echo '#define KRML_HOST_EPRINTF(...)'
echo '#define KRML_HOST_EXIT(x) fatal_f("internal error")'
echo

for i in $FILES; do
echo "/* from $i */"
# Changes to all files:
# - remove all includes, we inline everything required.
# - cleanup whitespace
sed -e "/#include/d" \
-e 's/[ ]*$//' \
$i | \
case "$i" in
*/libcrux-ml-kem/cg/eurydice_glue.h)
# Replace endian functions with versions that work.
perl -0777 -pe 's/(static inline void core_num__u64_9__to_le_bytes.*\n)([^}]*\n)/\1 v = htole64(v);\n\2/' |
perl -0777 -pe 's/(static inline uint64_t core_num__u64_9__from_le_bytes.*?)return v;/\1return le64toh(v);/s' |
perl -0777 -pe 's/(static inline uint32_t core_num__u32_8__from_le_bytes.*?)return v;/\1return le32toh(v);/s'
;;
# Default: pass through.
*)
cat
;;
esac
echo
done

echo
echo '/* rename some types to be a bit more ergonomic */'
echo '#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s'
echo '#define libcrux_mlkem768_pk_valid_result Option_92_s'
echo '#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_15_s'
echo '#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_55_s'
echo '#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s'
echo '#define libcrux_mlkem768_enc_result tuple_3c_s'
) > libcrux_mlkem768_sha3.h_new

# Do some checks on the resultant file

cat > libcrux_mlkem768_sha3_check.c << _EOF
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <signal.h>
#include <err.h>
#include "crypto_api.h"
#define fatal_f(x) exit(1)
#include "libcrux_mlkem768_sha3.h_new"
int main(void) {
struct libcrux_mlkem768_keypair keypair = {0};
struct libcrux_mlkem768_pk pk = {0};
struct libcrux_mlkem768_sk sk = {0};
struct libcrux_mlkem768_ciphertext ct = {0};
struct libcrux_mlkem768_enc_result enc_result = {0};
uint8_t kp_seed[64] = {0}, enc_seed[32] = {0};
uint8_t shared_key[crypto_kem_mlkem768_BYTES];

if (sizeof(keypair.pk.value) != crypto_kem_mlkem768_PUBLICKEYBYTES)
errx(1, "keypair.pk bad");
if (sizeof(keypair.sk.value) != crypto_kem_mlkem768_SECRETKEYBYTES)
errx(1, "keypair.sk bad");
if (sizeof(pk.value) != crypto_kem_mlkem768_PUBLICKEYBYTES)
errx(1, "pk bad");
if (sizeof(sk.value) != crypto_kem_mlkem768_SECRETKEYBYTES)
errx(1, "sk bad");
if (sizeof(ct.value) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
errx(1, "ct bad");
if (sizeof(enc_result.fst.value) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
errx(1, "enc_result ct bad");
if (sizeof(enc_result.snd) != crypto_kem_mlkem768_BYTES)
errx(1, "enc_result shared key bad");

keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(kp_seed);
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&keypair.pk))
errx(1, "valid smoke failed");
enc_result = libcrux_ml_kem_mlkem768_portable_encapsulate(&keypair.pk,
enc_seed);
libcrux_ml_kem_mlkem768_portable_decapsulate(&keypair.sk,
&enc_result.fst, shared_key);
if (memcmp(shared_key, enc_result.snd, sizeof(shared_key)) != 0)
errx(1, "smoke failed");
return 0;
}
_EOF
cc -Wall -Wextra -Wno-unused-parameter -o libcrux_mlkem768_sha3_check \
libcrux_mlkem768_sha3_check.c
./libcrux_mlkem768_sha3_check

# Extract PRNG inputs; there's no nice #defines for these
key_pair_rng_len=`sed -e '/^libcrux_ml_kem_mlkem768_portable_kyber_generate_key_pair[(]$/,/[)] {$/!d' < libcrux_mlkem768_sha3.h_new | grep 'uint8_t randomness\[[0-9]*U\][)]' | sed 's/.*randomness\[\([0-9]*\)U\].*/\1/'`
enc_rng_len=`sed -e '/^static inline tuple_3c libcrux_ml_kem_mlkem768_portable_kyber_encapsulate[(]$/,/[)] {$/!d' < libcrux_mlkem768_sha3.h_new | grep 'uint8_t randomness\[[0-9]*U\][)]' | sed 's/.*randomness\[\([0-9]*\)U\].*/\1/'`
test -z "$key_pair_rng_len" && die "couldn't find size of libcrux_ml_kem_mlkem768_portable_kyber_generate_key_pair randomness argument"
test -z "$enc_rng_len" && die "couldn't find size of libcrux_ml_kem_mlkem768_portable_kyber_encapsulate randomness argument"

(
echo "/* defines for PRNG inputs */"
echo "#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN $key_pair_rng_len"
echo "#define LIBCRUX_ML_KEM_ENC_PRNG_LEN $enc_rng_len"
) >> libcrux_mlkem768_sha3.h_new

mv libcrux_mlkem768_sha3.h_new libcrux_mlkem768_sha3.h
rm libcrux_mlkem768_sha3_check libcrux_mlkem768_sha3_check.c
echo 1>&2
echo "libcrux_mlkem768_sha3.h OK" 1>&2

16 changes: 16 additions & 0 deletions src/common-algo.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "chachapoly.h"
#include "ssh.h"
#include "sntrup761.h"
#include "mlkem768.h"

/* This file (algo.c) organises the ciphers which can be used, and is used to
* decide which ciphers/hashes/compression/signing to use during key exchange*/
Expand Down Expand Up @@ -270,6 +271,18 @@ static const struct dropbear_kex kex_ecdh_nistp521 = {DROPBEAR_KEX_ECDH, NULL, 0
static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc };
#endif

#if DROPBEAR_MLKEM768
static const struct dropbear_kem_desc mlkem768_desc = {
.public_len = crypto_kem_mlkem768_PUBLICKEYBYTES,
.secret_len = crypto_kem_mlkem768_SECRETKEYBYTES,
.ciphertext_len = crypto_kem_mlkem768_CIPHERTEXTBYTES,
.output_len = crypto_kem_mlkem768_BYTES,
.kem_gen = crypto_kem_mlkem768_keypair,
.kem_enc = crypto_kem_mlkem768_enc,
.kem_dec = crypto_kem_mlkem768_dec,
};
static const struct dropbear_kex kex_mlkem768 = {DROPBEAR_KEX_PQHYBRID, NULL, 0, &mlkem768_desc, &sha256_desc };
#endif

#if DROPBEAR_SNTRUP761
static const struct dropbear_kem_desc sntrup761_desc = {
Expand All @@ -292,6 +305,9 @@ volatile int64_t crypto_int64_optblocker = 0;

/* data == NULL for non-kex algorithm identifiers */
algo_type sshkex[] = {
#if DROPBEAR_MLKEM768
{"mlkem768x25519-sha256", 0, &kex_mlkem768, 1, NULL},
#endif
#if DROPBEAR_SNTRUP761
{"sntrup761x25519-sha512", 0, &kex_sntrup761, 1, NULL},
{"[email protected]", 0, &kex_sntrup761, 1, NULL},
Expand Down
4 changes: 4 additions & 0 deletions src/default_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ IMPORTANT: Some options will require "make clean" after changes */
* curve25519 - elliptic curve DH
* ecdh - NIST elliptic curve DH (256, 384, 521)
* sntrup761 - post-quantum hybrid with x25519.
* mlkem768 - post-quantum hybrid with x25519.
*
* group1 is too small for security though is necessary if you need
compatibility with some implementations such as Dropbear versions < 0.53
Expand All @@ -198,6 +199,8 @@ IMPORTANT: Some options will require "make clean" after changes */
* by future quantum computers.
* It is fast, but adds ~9kB code size (32-bit armv7)

* mlkem768 is also recommended to avoid possible decryption
* by future quantum computers.
* Small systems should generally include either curve25519 or ecdh for performance.
* curve25519 is less widely supported but is faster
*/
Expand All @@ -206,6 +209,7 @@ IMPORTANT: Some options will require "make clean" after changes */
#define DROPBEAR_DH_GROUP16 0
#define DROPBEAR_CURVE25519 1
#define DROPBEAR_SNTRUP761 1
#define DROPBEAR_MLKEM768 1
#define DROPBEAR_ECDH 1
#define DROPBEAR_DH_GROUP1 0

Expand Down
Loading
Loading