1
0
mirror of https://github.com/openbsd/src.git synced 2026-06-18 07:13:36 +02:00

Add experimental support for a composite post-quantum signature

scheme that combines ML-DSA 44 and Ed25519 using the construction
specified in draft-ietf-lamps-pq-composite-sigs. There's also an
early draft documenting use of the integration of this scheme into
SSH as draft-miller-sshm-mldsa44-ed25519-composite-sigs

This scheme is not enabled by default. To you use, you'll need
to add it to HostKeyAlgorithms, PubkeyAcceptedAlgorithms, etc.
Keys may be generated using "ssh-keygen -t mldsa44-ed25519".

The ML-DSA implementation comes from libcrux. Thanks to
Jonas Schneider-Bensch and Jonathan Protzenko for their work to
make this available.

Consensus is that it's time to get this in to allow people to
experiment with it.

feedback markus@ tb@ logan@ deraadt@
This commit is contained in:
djm
2026-06-14 03:59:34 +00:00
parent 7f3d25a7c2
commit 831e2e1785
26 changed files with 28869 additions and 12050 deletions
+3 -1
View File
@@ -1,4 +1,4 @@
# $OpenBSD: Makefile.inc,v 1.105 2026/02/06 23:31:29 dtucker Exp $
# $OpenBSD: Makefile.inc,v 1.106 2026/06/14 03:59:34 djm Exp $
.include <bsd.own.mk>
@@ -93,6 +93,8 @@ SRCS_KEY+= cipher-chachapoly.c
.endif
SRCS_KEY+= ssh-ed25519.c
SRCS_KEY+= ssh-ed25519-sk.c
SRCS_KEY+= libcrux-mlkem-mldsa.c
SRCS_KEY+= ssh-mldsa-eddsa.c
SRCS_KEY+= ssherr-libcrypto.c
.if (${OPENSSL:L} == "yes")
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: authfd.c,v 1.141 2026/03/05 05:44:15 djm Exp $ */
/* $OpenBSD: authfd.c,v 1.142 2026/06/14 03:59:34 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -607,6 +607,8 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
#endif
case KEY_ED25519:
case KEY_ED25519_CERT:
case KEY_MLDSA44_ED25519:
case KEY_MLDSA44_ED25519_CERT:
case KEY_ED25519_SK:
case KEY_ED25519_SK_CERT:
type = constrained ?
+2 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: authfile.c,v 1.149 2026/02/14 00:18:34 jsg Exp $ */
/* $OpenBSD: authfile.c,v 1.150 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
*
@@ -317,6 +317,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
case KEY_ECDSA:
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_MLDSA44_ED25519:
case KEY_UNSPEC:
break;
default:
+126 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: crypto_api.h,v 1.10 2025/10/30 23:19:33 djm Exp $ */
/* $OpenBSD: crypto_api.h,v 1.11 2026/06/14 03:59:34 djm Exp $ */
/*
* Assembled from generated headers and source files by Markus Friedl.
@@ -54,6 +54,7 @@ crypto_hash_sha512(unsigned char *out, const unsigned char *in,
#define crypto_sign_ed25519_SECRETKEYBYTES 64U
#define crypto_sign_ed25519_PUBLICKEYBYTES 32U
#define crypto_sign_ed25519_SEEDBYTES 32U
#define crypto_sign_ed25519_BYTES 64U
int crypto_sign_ed25519(unsigned char *, unsigned long long *,
@@ -61,11 +62,15 @@ int crypto_sign_ed25519(unsigned char *, unsigned long long *,
int crypto_sign_ed25519_open(unsigned char *, unsigned long long *,
const unsigned char *, unsigned long long, const unsigned char *);
int crypto_sign_ed25519_keypair(unsigned char *, unsigned char *);
int crypto_sign_ed25519_keypair_from_seed(unsigned char *, unsigned char *,
const unsigned char *);
#define crypto_kem_sntrup761_PUBLICKEYBYTES 1158
#define crypto_kem_sntrup761_SECRETKEYBYTES 1763
#define crypto_kem_sntrup761_CIPHERTEXTBYTES 1039
#define crypto_kem_sntrup761_BYTES 32
#define crypto_kem_mlkem768_ENCSEEDBYTES 32
#define crypto_kem_mlkem768_KEYPAIRSEEDBYTES 64
int crypto_kem_sntrup761_enc(unsigned char *cstr, unsigned char *k,
const unsigned char *pk);
@@ -73,9 +78,129 @@ int crypto_kem_sntrup761_dec(unsigned char *k,
const unsigned char *cstr, const unsigned char *sk);
int crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk);
/* ML-KEM-768 */
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
#define crypto_kem_mlkem768_BYTES 32
/* Aliases for MLKEM768 */
#define MLKEM768_PUBLICKEYBYTES crypto_kem_mlkem768_PUBLICKEYBYTES
#define MLKEM768_SECRETKEYBYTES crypto_kem_mlkem768_SECRETKEYBYTES
#define MLKEM768_CIPHERTEXTBYTES crypto_kem_mlkem768_CIPHERTEXTBYTES
#define MLKEM768_BYTES crypto_kem_mlkem768_BYTES
int crypto_kem_mlkem768_keypair(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES]);
int crypto_kem_mlkem768_enc(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES]);
int crypto_kem_mlkem768_dec(uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
const uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES]);
int crypto_kem_mlkem768_keypair_seeded(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES], const uint8_t seed[64]);
int crypto_kem_mlkem768_enc_seeded(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
const uint8_t seed[32]);
/* ML-DSA-44 */
#define MLDSA44_PUBLICKEYBYTES 1312
#define MLDSA44_SECRETKEYBYTES 2560
#define MLDSA44_SIGBYTES 2420
#define MLDSA44_SEEDBYTES 32
int crypto_sign_mldsa44_keypair(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
uint8_t sk[MLDSA44_SECRETKEYBYTES]);
int crypto_sign_mldsa44(uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_SECRETKEYBYTES]);
int crypto_sign_mldsa44_verify(const uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA44_PUBLICKEYBYTES]);
int crypto_sign_mldsa44_keypair_seeded(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
uint8_t sk[MLDSA44_SECRETKEYBYTES], const uint8_t seed[MLDSA44_SEEDBYTES]);
int crypto_sign_mldsa44_seeded(uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_SECRETKEYBYTES],
const uint8_t seed[MLDSA44_SEEDBYTES]);
/* ML-DSA-65 */
#define MLDSA65_PUBLICKEYBYTES 1952
#define MLDSA65_SECRETKEYBYTES 4032
#define MLDSA65_SIGBYTES 3309
#define MLDSA65_SEEDBYTES 32
#if 0
int crypto_sign_mldsa65_keypair(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
uint8_t sk[MLDSA65_SECRETKEYBYTES]);
int crypto_sign_mldsa65(uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA65_SECRETKEYBYTES]);
int crypto_sign_mldsa65_verify(const uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA65_PUBLICKEYBYTES]);
int crypto_sign_mldsa65_keypair_seeded(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
uint8_t sk[MLDSA65_SECRETKEYBYTES], const uint8_t seed[MLDSA65_SEEDBYTES]);
int crypto_sign_mldsa65_seeded(uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA65_SECRETKEYBYTES],
const uint8_t seed[MLDSA65_SEEDBYTES]);
#endif
/* ML-DSA-87 */
#define MLDSA87_PUBLICKEYBYTES 2592
#define MLDSA87_SECRETKEYBYTES 4896
#define MLDSA87_SIGBYTES 4627
#define MLDSA87_SEEDBYTES 32
int crypto_sign_mldsa87_keypair(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
uint8_t sk[MLDSA87_SECRETKEYBYTES]);
int crypto_sign_mldsa87(uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA87_SECRETKEYBYTES]);
int crypto_sign_mldsa87_verify(const uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA87_PUBLICKEYBYTES]);
int crypto_sign_mldsa87_keypair_seeded(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
uint8_t sk[MLDSA87_SECRETKEYBYTES], const uint8_t seed[MLDSA87_SEEDBYTES]);
int crypto_sign_mldsa87_seeded(uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA87_SECRETKEYBYTES],
const uint8_t seed[MLDSA87_SEEDBYTES]);
/* MLDSA44-Ed25519-SHA512 */
#define MLDSA44_ED25519_PK_SZ (MLDSA44_PUBLICKEYBYTES + crypto_sign_ed25519_PUBLICKEYBYTES)
#define MLDSA44_ED25519_SK_SZ (MLDSA44_SEEDBYTES + crypto_sign_ed25519_SEEDBYTES)
#define MLDSA44_ED25519_SIG_SZ (MLDSA44_SIGBYTES + crypto_sign_ed25519_BYTES)
int crypto_sign_mldsa44_ed25519_keygen(uint8_t pk[MLDSA44_ED25519_PK_SZ],
uint8_t sk[MLDSA44_ED25519_SK_SZ]);
int crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ],
uint8_t sk[MLDSA44_ED25519_SK_SZ],
const uint8_t mldsa_seed[MLDSA44_SEEDBYTES],
const uint8_t ed25519_seed[crypto_sign_ed25519_SEEDBYTES]);
int crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_ED25519_SK_SZ]);
int crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA44_ED25519_PK_SZ]);
/* Utility */
void sha3_256(uint8_t digest[32], const uint8_t *data, size_t len);
void sha3_512(uint8_t digest[64], const uint8_t *data, size_t len);
#endif /* crypto_api_h */
+33 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ed25519-openssl.c,v 1.1 2025/10/30 20:49:10 djm Exp $ */
/* $OpenBSD: ed25519-openssl.c,v 1.2 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2025 OpenSSH
*
@@ -91,6 +91,38 @@ out:
return ret;
}
int
crypto_sign_ed25519_keypair_from_seed(unsigned char *pk, unsigned char *sk,
const unsigned char *seed)
{
EVP_PKEY *pkey = NULL;
size_t pklen;
int ret = -1;
if ((pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
seed, SSH_ED25519_RAW_SECRET_KEY_LEN)) == NULL) {
debug3_f("EVP_PKEY_new_raw_private_key failed");
goto out;
}
/* Extract public key */
pklen = crypto_sign_ed25519_PUBLICKEYBYTES;
if (!EVP_PKEY_get_raw_public_key(pkey, pk, &pklen)) {
debug3_f("EVP_PKEY_get_raw_public_key failed");
goto out;
}
/* Build sk = seed || pk */
memcpy(sk, seed, SSH_ED25519_RAW_SECRET_KEY_LEN);
memcpy(sk + SSH_ED25519_RAW_SECRET_KEY_LEN, pk,
crypto_sign_ed25519_PUBLICKEYBYTES);
ret = 0;
out:
EVP_PKEY_free(pkey);
return ret;
}
int
crypto_sign_ed25519(unsigned char *sm, unsigned long long *smlen,
const unsigned char *m, unsigned long long mlen,
+40 -58
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: kexmlkem768x25519.c,v 1.2 2024/10/27 02:06:59 djm Exp $ */
/* $OpenBSD: kexmlkem768x25519.c,v 1.3 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2023 Markus Friedl. All rights reserved.
*
@@ -39,32 +39,30 @@
#include "ssherr.h"
#include "log.h"
#include "libcrux_mlkem768_sha3.h"
#include "crypto_api.h"
int
kex_kem_mlkem768x25519_keypair(struct kex *kex)
{
struct sshbuf *buf = NULL;
u_char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN], *cp = NULL;
u_char *cp = NULL;
size_t need;
int r = SSH_ERR_INTERNAL_ERROR;
struct libcrux_mlkem768_keypair keypair;
if ((buf = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
goto out;
arc4random_buf(rnd, sizeof(rnd));
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
memcpy(cp, keypair.pk.value, crypto_kem_mlkem768_PUBLICKEYBYTES);
memcpy(kex->mlkem768_client_key, keypair.sk.value,
sizeof(kex->mlkem768_client_key));
if (crypto_kem_mlkem768_keypair(cp, kex->mlkem768_client_key) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
#ifdef DEBUG_KEXECDH
dump_digest("client public key mlkem768:", cp,
crypto_kem_mlkem768_PUBLICKEYBYTES);
MLKEM768_PUBLICKEYBYTES);
#endif
cp += crypto_kem_mlkem768_PUBLICKEYBYTES;
cp += MLKEM768_PUBLICKEYBYTES;
kexc25519_keygen(kex->c25519_client_key, cp);
#ifdef DEBUG_KEXECDH
dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
@@ -74,8 +72,6 @@ kex_kem_mlkem768x25519_keypair(struct kex *kex)
kex->client_pub = buf;
buf = NULL;
out:
explicit_bzero(&keypair, sizeof(keypair));
explicit_bzero(rnd, sizeof(rnd));
sshbuf_free(buf);
return r;
}
@@ -88,20 +84,18 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
struct sshbuf *server_blob = NULL;
struct sshbuf *buf = NULL;
const u_char *client_pub;
u_char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
u_char server_pub[CURVE25519_SIZE], server_key[CURVE25519_SIZE];
u_char hash[SSH_DIGEST_MAX_LENGTH];
u_char ct[MLKEM768_CIPHERTEXTBYTES];
u_char shared_secret[MLKEM768_BYTES];
size_t need;
int r = SSH_ERR_INTERNAL_ERROR;
struct libcrux_mlkem768_enc_result enc;
struct libcrux_mlkem768_pk mlkem_pub;
*server_blobp = NULL;
*shared_secretp = NULL;
memset(&mlkem_pub, 0, sizeof(mlkem_pub));
/* client_blob contains both KEM and ECDH client pubkeys */
need = crypto_kem_mlkem768_PUBLICKEYBYTES + CURVE25519_SIZE;
need = MLKEM768_PUBLICKEYBYTES + CURVE25519_SIZE;
if (sshbuf_len(client_blob) != need) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
@@ -109,17 +103,11 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
client_pub = sshbuf_ptr(client_blob);
#ifdef DEBUG_KEXECDH
dump_digest("client public key mlkem768:", client_pub,
crypto_kem_mlkem768_PUBLICKEYBYTES);
MLKEM768_PUBLICKEYBYTES);
dump_digest("client public key 25519:",
client_pub + crypto_kem_mlkem768_PUBLICKEYBYTES,
client_pub + MLKEM768_PUBLICKEYBYTES,
CURVE25519_SIZE);
#endif
/* check public key validity */
memcpy(mlkem_pub.value, client_pub, crypto_kem_mlkem768_PUBLICKEYBYTES);
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub)) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
/* allocate buffer for concatenation of KEM key and ECDH shared key */
/* the buffer will be hashed and the result is the shared secret */
@@ -133,25 +121,27 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
goto out;
}
/* generate and encrypt KEM key with client key */
arc4random_buf(rnd, sizeof(rnd));
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
if (crypto_kem_mlkem768_enc(ct, shared_secret, client_pub) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
/* generate ECDH key pair, store server pubkey after ciphertext */
kexc25519_keygen(server_key, server_pub);
if ((r = sshbuf_put(buf, enc.snd, sizeof(enc.snd))) != 0 ||
(r = sshbuf_put(server_blob, enc.fst.value, sizeof(enc.fst.value))) != 0 ||
if ((r = sshbuf_put(buf, shared_secret, sizeof(shared_secret))) != 0 ||
(r = sshbuf_put(server_blob, ct, sizeof(ct))) != 0 ||
(r = sshbuf_put(server_blob, server_pub, sizeof(server_pub))) != 0)
goto out;
/* append ECDH shared key */
client_pub += crypto_kem_mlkem768_PUBLICKEYBYTES;
client_pub += MLKEM768_PUBLICKEYBYTES;
if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0)
goto out;
if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
if ((r = ssh_digest_buffer(kex->hash_alg, buf,
hash, sizeof(hash))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
dump_digest("server cipher text:",
enc.fst.value, sizeof(enc.fst.value));
dump_digest("server kem key:", enc.snd, sizeof(enc.snd));
dump_digest("server cipher text:", ct, sizeof(ct));
dump_digest("server kem key:", shared_secret, sizeof(shared_secret));
dump_digest("concatenation of KEM key and ECDH shared key:",
sshbuf_ptr(buf), sshbuf_len(buf));
#endif
@@ -172,8 +162,7 @@ kex_kem_mlkem768x25519_enc(struct kex *kex,
out:
explicit_bzero(hash, sizeof(hash));
explicit_bzero(server_key, sizeof(server_key));
explicit_bzero(rnd, sizeof(rnd));
explicit_bzero(&enc, sizeof(enc));
explicit_bzero(shared_secret, sizeof(shared_secret));
sshbuf_free(server_blob);
sshbuf_free(buf);
return r;
@@ -184,42 +173,37 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
{
struct sshbuf *buf = NULL;
u_char mlkem_key[crypto_kem_mlkem768_BYTES];
u_char shared_secret[MLKEM768_BYTES];
const u_char *ciphertext, *server_pub;
u_char hash[SSH_DIGEST_MAX_LENGTH];
size_t need;
int r;
struct libcrux_mlkem768_sk mlkem_priv;
struct libcrux_mlkem768_ciphertext mlkem_ciphertext;
*shared_secretp = NULL;
memset(&mlkem_priv, 0, sizeof(mlkem_priv));
memset(&mlkem_ciphertext, 0, sizeof(mlkem_ciphertext));
need = crypto_kem_mlkem768_CIPHERTEXTBYTES + CURVE25519_SIZE;
need = MLKEM768_CIPHERTEXTBYTES + CURVE25519_SIZE;
if (sshbuf_len(server_blob) != need) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
ciphertext = sshbuf_ptr(server_blob);
server_pub = ciphertext + crypto_kem_mlkem768_CIPHERTEXTBYTES;
server_pub = ciphertext + MLKEM768_CIPHERTEXTBYTES;
/* hash concatenation of KEM key and ECDH shared key */
if ((buf = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
memcpy(mlkem_priv.value, kex->mlkem768_client_key,
sizeof(kex->mlkem768_client_key));
memcpy(mlkem_ciphertext.value, ciphertext,
sizeof(mlkem_ciphertext.value));
#ifdef DEBUG_KEXECDH
dump_digest("server cipher text:", mlkem_ciphertext.value,
sizeof(mlkem_ciphertext.value));
dump_digest("server cipher text:", ciphertext,
MLKEM768_CIPHERTEXTBYTES);
dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
#endif
libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
&mlkem_ciphertext, mlkem_key);
if ((r = sshbuf_put(buf, mlkem_key, sizeof(mlkem_key))) != 0)
if (crypto_kem_mlkem768_dec(shared_secret, ciphertext,
kex->mlkem768_client_key) != 0) {
r = SSH_ERR_INTERNAL_ERROR;
goto out;
}
if ((r = sshbuf_put(buf, shared_secret, sizeof(shared_secret))) != 0)
goto out;
if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
buf, 1)) < 0)
@@ -228,7 +212,7 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
hash, sizeof(hash))) != 0)
goto out;
#ifdef DEBUG_KEXECDH
dump_digest("client kem key:", mlkem_key, sizeof(mlkem_key));
dump_digest("client kem key:", shared_secret, sizeof(shared_secret));
dump_digest("concatenation of KEM key and ECDH shared key:",
sshbuf_ptr(buf), sshbuf_len(buf));
#endif
@@ -245,9 +229,7 @@ kex_kem_mlkem768x25519_dec(struct kex *kex,
buf = NULL;
out:
explicit_bzero(hash, sizeof(hash));
explicit_bzero(&mlkem_priv, sizeof(mlkem_priv));
explicit_bzero(&mlkem_ciphertext, sizeof(mlkem_ciphertext));
explicit_bzero(mlkem_key, sizeof(mlkem_key));
explicit_bzero(shared_secret, sizeof(shared_secret));
sshbuf_free(buf);
return r;
}
+426
View File
@@ -0,0 +1,426 @@
/* $OpenBSD: libcrux-mlkem-mldsa.c,v 1.1 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2026 Damien Miller <djm@mindrot.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "log.h"
#include "crypto_api.h"
#include "libcrux_internal.h"
/* ML-KEM 768 */
int
crypto_kem_mlkem768_keypair(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES])
{
uint8_t rnd[crypto_kem_mlkem768_KEYPAIRSEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_kem_mlkem768_keypair_seeded(pk, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_kem_mlkem768_keypair_seeded(uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES],
const uint8_t seed[crypto_kem_mlkem768_KEYPAIRSEEDBYTES])
{
libcrux_mlkem768_keypair keypair;
libcrux_mlkem768_keypair_rnd rnd;
memcpy(rnd.data, seed, sizeof(rnd.data));
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
memcpy(pk, keypair.pk.data, crypto_kem_mlkem768_PUBLICKEYBYTES);
memcpy(sk, keypair.sk.data, crypto_kem_mlkem768_SECRETKEYBYTES);
explicit_bzero(&keypair, sizeof(keypair));
explicit_bzero(&rnd, sizeof(rnd));
return 0;
}
int
crypto_kem_mlkem768_enc(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES])
{
uint8_t rnd[crypto_kem_mlkem768_ENCSEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_kem_mlkem768_enc_seeded(ct, shared_secret, pk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_kem_mlkem768_enc_seeded(uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t pk[crypto_kem_mlkem768_PUBLICKEYBYTES],
const uint8_t seed[crypto_kem_mlkem768_ENCSEEDBYTES])
{
libcrux_mlkem768_enc_result enc;
libcrux_mlkem768_pk pk_internal;
libcrux_mlkem768_enc_rnd rnd;
memcpy(pk_internal.data, pk, crypto_kem_mlkem768_PUBLICKEYBYTES);
if (!libcrux_ml_kem_mlkem768_portable_validate_public_key(&pk_internal))
return -1;
memcpy(rnd.data, seed, sizeof(rnd.data));
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&pk_internal, rnd);
memcpy(ct, enc.fst.data, crypto_kem_mlkem768_CIPHERTEXTBYTES);
memcpy(shared_secret, enc.snd.data, crypto_kem_mlkem768_BYTES);
explicit_bzero(&enc, sizeof(enc));
explicit_bzero(&rnd, sizeof(rnd));
return 0;
}
int
crypto_kem_mlkem768_dec(uint8_t shared_secret[crypto_kem_mlkem768_BYTES],
const uint8_t ct[crypto_kem_mlkem768_CIPHERTEXTBYTES],
const uint8_t sk[crypto_kem_mlkem768_SECRETKEYBYTES])
{
libcrux_mlkem768_sk sk_internal;
libcrux_mlkem768_ciphertext ct_internal;
libcrux_mlkem768_dec_result shared_secret_internal;
memcpy(sk_internal.data, sk, crypto_kem_mlkem768_SECRETKEYBYTES);
memcpy(ct_internal.data, ct, crypto_kem_mlkem768_CIPHERTEXTBYTES);
shared_secret_internal = libcrux_ml_kem_mlkem768_portable_decapsulate(
&sk_internal, &ct_internal);
memcpy(shared_secret, shared_secret_internal.data,
crypto_kem_mlkem768_BYTES);
explicit_bzero(&sk_internal, sizeof(sk_internal));
explicit_bzero(&shared_secret_internal, sizeof(shared_secret_internal));
return 0;
}
/* ML-DSA 44 */
int
crypto_sign_mldsa44_keypair(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
uint8_t sk[MLDSA44_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA44_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa44_keypair_seeded(pk, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa44_keypair_seeded(uint8_t pk[MLDSA44_PUBLICKEYBYTES],
uint8_t sk[MLDSA44_SECRETKEYBYTES], const uint8_t seed[MLDSA44_SEEDBYTES])
{
libcrux_mldsa44_keypair_rnd rnd;
libcrux_mldsa44_keypair keypair;
memcpy(rnd.data, seed, sizeof(rnd.data));
keypair = libcrux_ml_dsa_ml_dsa_44_portable_generate_key_pair(rnd);
memcpy(pk, keypair.verification_key.data, MLDSA44_PUBLICKEYBYTES);
memcpy(sk, keypair.signing_key.data, MLDSA44_SECRETKEYBYTES);
explicit_bzero(&keypair, sizeof(keypair));
explicit_bzero(&rnd, sizeof(rnd));
return 0;
}
int
crypto_sign_mldsa44(uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA44_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa44_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa44_seeded(uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_SECRETKEYBYTES],
const uint8_t seed[MLDSA44_SEEDBYTES])
{
libcrux_mldsa44_sign_rnd rnd;
libcrux_mldsa44_sk sk_internal;
libcrux_mldsa44_message message = { msg, msglen };
libcrux_mldsa44_message context = { ctx, ctxlen };
libcrux_mldsa44_sign_result res;
int r = -1;
memcpy(sk_internal.data, sk, MLDSA44_SECRETKEYBYTES);
memcpy(rnd.data, seed, sizeof(rnd.data));
res = libcrux_ml_dsa_ml_dsa_44_portable_sign(&sk_internal,
message, context, rnd);
if (res.tag == LIBCRUX_RESULT_OK) {
memcpy(sig, res.val.case_Ok.data, MLDSA44_SIGBYTES);
r = 0;
}
explicit_bzero(&sk_internal, sizeof(sk_internal));
explicit_bzero(&res, sizeof(res));
explicit_bzero(&rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa44_verify(const uint8_t sig[MLDSA44_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA44_PUBLICKEYBYTES])
{
libcrux_mldsa44_pk pk_internal;
libcrux_mldsa44_signature sig_internal;
libcrux_mldsa44_message message = { msg, msglen };
libcrux_mldsa44_message context = { ctx, ctxlen };
libcrux_mldsa44_verify_result res;
memcpy(pk_internal.data, pk, MLDSA44_PUBLICKEYBYTES);
memcpy(sig_internal.data, sig, MLDSA44_SIGBYTES);
res = libcrux_ml_dsa_ml_dsa_44_portable_verify(&pk_internal,
message, context, &sig_internal);
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
}
/* ML-DSA 65 */
#if 0
int
crypto_sign_mldsa65_keypair(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
uint8_t sk[MLDSA65_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA65_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa65_keypair_seeded(pk, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa65_keypair_seeded(uint8_t pk[MLDSA65_PUBLICKEYBYTES],
uint8_t sk[MLDSA65_SECRETKEYBYTES], const uint8_t seed[MLDSA65_SEEDBYTES])
{
libcrux_mldsa65_keypair_rnd rnd;
libcrux_mldsa65_keypair keypair;
memcpy(rnd.data, seed, sizeof(rnd.data));
keypair = libcrux_ml_dsa_ml_dsa_65_portable_generate_key_pair(rnd);
memcpy(pk, keypair.verification_key.data, MLDSA65_PUBLICKEYBYTES);
memcpy(sk, keypair.signing_key.data, MLDSA65_SECRETKEYBYTES);
explicit_bzero(&keypair, sizeof(keypair));
explicit_bzero(&rnd, sizeof(rnd));
return 0;
}
int
crypto_sign_mldsa65(uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA65_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA65_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa65_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa65_seeded(uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA65_SECRETKEYBYTES],
const uint8_t seed[MLDSA65_SEEDBYTES])
{
libcrux_mldsa65_sign_rnd rnd;
libcrux_mldsa65_sk sk_internal;
libcrux_mldsa65_message message = { msg, msglen };
libcrux_mldsa65_message context = { ctx, ctxlen };
libcrux_mldsa65_sign_result res;
int r = -1;
memcpy(sk_internal.data, sk, MLDSA65_SECRETKEYBYTES);
memcpy(rnd.data, seed, sizeof(rnd.data));
res = libcrux_ml_dsa_ml_dsa_65_portable_sign(&sk_internal,
message, context, rnd);
if (res.tag == LIBCRUX_RESULT_OK) {
memcpy(sig, res.val.case_Ok.data, MLDSA65_SIGBYTES);
r = 0;
}
explicit_bzero(&sk_internal, sizeof(sk_internal));
explicit_bzero(&res, sizeof(res));
explicit_bzero(&rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa65_verify(const uint8_t sig[MLDSA65_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA65_PUBLICKEYBYTES])
{
libcrux_mldsa65_pk pk_internal;
libcrux_mldsa65_signature sig_internal;
libcrux_mldsa65_message message = { msg, msglen };
libcrux_mldsa65_message context = { ctx, ctxlen };
libcrux_mldsa65_verify_result res;
memcpy(pk_internal.data, pk, MLDSA65_PUBLICKEYBYTES);
memcpy(sig_internal.data, sig, MLDSA65_SIGBYTES);
res = libcrux_ml_dsa_ml_dsa_65_portable_verify(&pk_internal,
message, context, &sig_internal);
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
}
#endif
/* ML-DSA 87 */
#if 0
int
crypto_sign_mldsa87_keypair(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
uint8_t sk[MLDSA87_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA87_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa87_keypair_seeded(pk, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa87_keypair_seeded(uint8_t pk[MLDSA87_PUBLICKEYBYTES],
uint8_t sk[MLDSA87_SECRETKEYBYTES], const uint8_t seed[MLDSA87_SEEDBYTES])
{
libcrux_mldsa87_keypair_rnd rnd;
libcrux_mldsa87_keypair keypair;
memcpy(rnd.data, seed, sizeof(rnd.data));
keypair = libcrux_ml_dsa_ml_dsa_87_portable_generate_key_pair(rnd);
memcpy(pk, keypair.verification_key.data, MLDSA87_PUBLICKEYBYTES);
memcpy(sk, keypair.signing_key.data, MLDSA87_SECRETKEYBYTES);
explicit_bzero(&keypair, sizeof(keypair));
explicit_bzero(&rnd, sizeof(rnd));
return 0;
}
int
crypto_sign_mldsa87(uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA87_SECRETKEYBYTES])
{
uint8_t rnd[MLDSA87_SEEDBYTES];
int r;
arc4random_buf(rnd, sizeof(rnd));
r = crypto_sign_mldsa87_seeded(sig, msg, msglen, ctx, ctxlen, sk, rnd);
explicit_bzero(rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa87_seeded(uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA87_SECRETKEYBYTES],
const uint8_t seed[MLDSA87_SEEDBYTES])
{
libcrux_mldsa87_sign_rnd rnd;
libcrux_mldsa87_sk sk_internal;
libcrux_mldsa87_message message = { msg, msglen };
libcrux_mldsa87_message context = { ctx, ctxlen };
libcrux_mldsa87_sign_result res;
int r = -1;
memcpy(sk_internal.data, sk, MLDSA87_SECRETKEYBYTES);
memcpy(rnd.data, seed, sizeof(rnd.data));
res = libcrux_ml_dsa_ml_dsa_87_portable_sign(&sk_internal,
message, context, rnd);
if (res.tag == LIBCRUX_RESULT_OK) {
memcpy(sig, res.val.case_Ok.data, MLDSA87_SIGBYTES);
r = 0;
}
explicit_bzero(&sk_internal, sizeof(sk_internal));
explicit_bzero(&res, sizeof(res));
explicit_bzero(&rnd, sizeof(rnd));
return r;
}
int
crypto_sign_mldsa87_verify(const uint8_t sig[MLDSA87_SIGBYTES],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA87_PUBLICKEYBYTES])
{
libcrux_mldsa87_pk pk_internal;
libcrux_mldsa87_signature sig_internal;
libcrux_mldsa87_message message = { msg, msglen };
libcrux_mldsa87_message context = { ctx, ctxlen };
libcrux_mldsa87_verify_result res;
memcpy(pk_internal.data, pk, MLDSA87_PUBLICKEYBYTES);
memcpy(sig_internal.data, sig, MLDSA87_SIGBYTES);
res = libcrux_ml_dsa_ml_dsa_87_portable_verify(&pk_internal,
message, context, &sig_internal);
return (res.tag == LIBCRUX_RESULT_OK) ? 0 : -1;
}
#endif
void
sha3_256(uint8_t digest[32], const uint8_t *data, size_t len)
{
Eurydice_borrow_slice_u8 input = { data, len };
Eurydice_mut_borrow_slice_u8 output = { digest, 32 };
libcrux_sha3_portable_sha256(output, input);
}
void
sha3_512(uint8_t digest[64], const uint8_t *data, size_t len)
{
Eurydice_borrow_slice_u8 input = { data, len };
Eurydice_mut_borrow_slice_u8 output = { digest, 64 };
libcrux_sha3_portable_sha512(output, input);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-219
View File
@@ -1,219 +0,0 @@
#!/bin/sh
# $OpenBSD: mlkem768.sh,v 1.5 2025/11/13 05:13:06 djm Exp $
# Placed in the Public Domain.
#
#WANT_LIBCRUX_REVISION="origin/main"
WANT_LIBCRUX_REVISION="core-models-v0.0.4"
BASE="libcrux/libcrux-ml-kem/extracts/c_header_only/generated"
FILES="
$BASE/eurydice_glue.h
$BASE/libcrux_mlkem_core.h
$BASE/libcrux_ct_ops.h
$BASE/libcrux_sha3_portable.h
$BASE/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
LSHIFT="<<"
cat << _EOF
#if !defined(__GNUC__) || (__GNUC__ < 2)
# define __attribute__(x)
#endif
#define KRML_MUSTINLINE inline
#define KRML_NOINLINE __attribute__((noinline, unused))
#define KRML_HOST_EPRINTF(...)
#define KRML_HOST_EXIT(x) fatal_f("internal error")
static inline void
store64_le(uint8_t dst[8], uint64_t src)
{
dst[0] = src & 0xff;
dst[1] = (src >> 8) & 0xff;
dst[2] = (src >> 16) & 0xff;
dst[3] = (src >> 24) & 0xff;
dst[4] = (src >> 32) & 0xff;
dst[5] = (src >> 40) & 0xff;
dst[6] = (src >> 48) & 0xff;
dst[7] = (src >> 56) & 0xff;
}
static inline void
store32_le(uint8_t dst[4], uint32_t src)
{
dst[0] = src & 0xff;
dst[1] = (src >> 8) & 0xff;
dst[2] = (src >> 16) & 0xff;
dst[3] = (src >> 24) & 0xff;
}
static inline void
store32_be(uint8_t dst[4], uint32_t src)
{
dst[0] = (src >> 24) & 0xff;
dst[1] = (src >> 16) & 0xff;
dst[2] = (src >> 8) & 0xff;
dst[3] = src & 0xff;
}
static inline uint64_t
load64_le(uint8_t src[8])
{
return (uint64_t)(src[0]) |
((uint64_t)(src[1]) $LSHIFT 8) |
((uint64_t)(src[2]) $LSHIFT 16) |
((uint64_t)(src[3]) $LSHIFT 24) |
((uint64_t)(src[4]) $LSHIFT 32) |
((uint64_t)(src[5]) $LSHIFT 40) |
((uint64_t)(src[6]) $LSHIFT 48) |
((uint64_t)(src[7]) $LSHIFT 56);
}
static inline uint32_t
load32_le(uint8_t src[4])
{
return (uint32_t)(src[0]) |
((uint32_t)(src[1]) $LSHIFT 8) |
((uint32_t)(src[2]) $LSHIFT 16) |
((uint32_t)(src[3]) $LSHIFT 24);
}
#ifdef MISSING_BUILTIN_POPCOUNT
static inline unsigned int
__builtin_popcount(unsigned int num)
{
const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
return v[num & 0xf] + v[(num >> 4) & 0xf];
}
#endif
_EOF
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
*/eurydice_glue.h)
# Replace endian function for consistency.
perl -0777 -pe 's/(static inline void core_num__u32__to_be_bytes.*\n)([^}]*\n)/\1 store32_be(dst, src);\n/'
;;
# 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 libcrux_ml_kem_types_MlKemPublicKey_30_s'
echo '#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s'
echo '#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s'
echo '#define libcrux_mlkem768_enc_result tuple_c2_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 -I . -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=`grep '^libcrux_ml_kem_mlkem768_portable_generate_key_pair.*randomness' libcrux_mlkem768_sha3.h_new | sed 's/.*randomness[[]//;s/\].*//'`
enc_rng_len=`sed -e '/^static inline tuple_c2 libcrux_ml_kem_mlkem768_portable_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
+341
View File
@@ -0,0 +1,341 @@
#!/bin/sh
# $OpenBSD: mlkem_mldsa.sh,v 1.1 2026/06/14 03:59:34 djm Exp $
# Placed in the Public Domain.
#
WANT_LIBCRUX_REVISION="origin/jonas/combined-extraction-mldsa"
BASE="libcrux/combined_extraction/generated"
FILES="
$BASE/eurydice_glue.h
$BASE/combined_core.h
$BASE/libcrux_sha3_portable.h
$BASE/libcrux_mlkem_core.h
$BASE/libcrux_mldsa_core.h
$BASE/libcrux_ct_ops.h
$BASE/libcrux_mldsa_portable.h
$BASE/libcrux_mldsa44_portable.h
$BASE/libcrux_mldsa65_portable.h
$BASE/libcrux_mldsa87_portable.h
$BASE/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
LSHIFT="<<"
cat << _EOF
#if !defined(__GNUC__) || (__GNUC__ < 2)
# define __attribute__(x)
#endif
#define KRML_MUSTINLINE inline
#define KRML_NOINLINE __attribute__((noinline, unused))
#define KRML_HOST_EPRINTF(...)
#define KRML_HOST_EXIT(x) fatal_f("internal error")
#define KRML_UNION_CONSTRUCTOR(T)
static inline void
store64_le(uint8_t dst[8], uint64_t src)
{
dst[0] = src & 0xff;
dst[1] = (src >> 8) & 0xff;
dst[2] = (src >> 16) & 0xff;
dst[3] = (src >> 24) & 0xff;
dst[4] = (src >> 32) & 0xff;
dst[5] = (src >> 40) & 0xff;
dst[6] = (src >> 48) & 0xff;
dst[7] = (src >> 56) & 0xff;
}
static inline void
store32_le(uint8_t dst[4], uint32_t src)
{
dst[0] = src & 0xff;
dst[1] = (src >> 8) & 0xff;
dst[2] = (src >> 16) & 0xff;
dst[3] = (src >> 24) & 0xff;
}
static inline void
store16_le(uint8_t dst[2], uint16_t src)
{
dst[0] = src & 0xff;
dst[1] = (src >> 8) & 0xff;
}
static inline void
store32_be(uint8_t dst[4], uint32_t src)
{
dst[0] = (src >> 24) & 0xff;
dst[1] = (src >> 16) & 0xff;
dst[2] = (src >> 8) & 0xff;
dst[3] = src & 0xff;
}
static inline uint64_t
load64_le(uint8_t src[8])
{
return (uint64_t)(src[0]) |
((uint64_t)(src[1]) $LSHIFT 8) |
((uint64_t)(src[2]) $LSHIFT 16) |
((uint64_t)(src[3]) $LSHIFT 24) |
((uint64_t)(src[4]) $LSHIFT 32) |
((uint64_t)(src[5]) $LSHIFT 40) |
((uint64_t)(src[6]) $LSHIFT 48) |
((uint64_t)(src[7]) $LSHIFT 56);
}
static inline uint32_t
load32_le(uint8_t src[4])
{
return (uint32_t)(src[0]) |
((uint32_t)(src[1]) $LSHIFT 8) |
((uint32_t)(src[2]) $LSHIFT 16) |
((uint32_t)(src[3]) $LSHIFT 24);
}
static inline uint16_t
load16_le(uint8_t src[4])
{
return (uint16_t)(src[0]) |
((uint16_t)(src[1]) $LSHIFT 8);
}
#ifdef MISSING_BUILTIN_POPCOUNT
static inline unsigned int
__builtin_popcount(unsigned int num)
{
const int v[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
return v[num & 0xf] + v[(num >> 4) & 0xf];
}
#endif
_EOF
for i in $FILES; do
echo "/* from $i */"
# Changes to all files:
# - remove all includes, we inline everything required.
# - cleanup whitespace
# - convert C++-style constructors to C-style compound literals
# - convert Result constructors to C initializers
# - use anonymous unions to avoid "union U" redefinition errors
sed -e "/#include/d" \
-e 's/[ ]*$//' \
$i | \
case "$i" in
*/eurydice_glue.h)
# Replace endian function for consistency.
perl -0777 -pe 's/(static inline void core_num__u32__to_be_bytes.*\n)([^}]*\n)/\1 store32_be(dst, src);\n/'
;;
# Default: pass through.
*)
cat
;;
esac | \
perl -0777 -pe 's/ <<\n\s+\(uint32_t\)\(int32_t\)0//g'
echo
done
cat << _EOF
/* rename some types to be a bit more ergonomic */
/* ML-KEM 768 */
typedef Eurydice_arr_c7 libcrux_mlkem768_keypair_rnd;
typedef Eurydice_arr_ec libcrux_mlkem768_enc_rnd;
typedef libcrux_ml_kem_mlkem768_MlKem768KeyPair libcrux_mlkem768_keypair;
typedef Eurydice_arr_5f libcrux_mlkem768_pk;
typedef Eurydice_arr_7d libcrux_mlkem768_sk;
typedef Eurydice_arr_2b libcrux_mlkem768_ciphertext;
typedef tuple_f4 libcrux_mlkem768_enc_result;
typedef Eurydice_arr_ec libcrux_mlkem768_dec_result;
/* ML-DSA 44 */
typedef Eurydice_arr_ec libcrux_mldsa44_keypair_rnd;
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_44_MLDSA44KeyPair
libcrux_mldsa44_keypair;
typedef Eurydice_arr_10 libcrux_mldsa44_sk;
typedef Eurydice_arr_02 libcrux_mldsa44_pk;
typedef Eurydice_borrow_slice_u8 libcrux_mldsa44_message;
typedef Eurydice_arr_ec libcrux_mldsa44_sign_rnd;
typedef core_result_Result_48 libcrux_mldsa44_sign_result;
typedef core_result_Result_41 libcrux_mldsa44_verify_result;
typedef Eurydice_arr_85 libcrux_mldsa44_signature;
/* ML-DSA 65 */
typedef Eurydice_arr_ec libcrux_mldsa65_keypair_rnd;
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_65_MLDSA65KeyPair
libcrux_mldsa65_keypair;
typedef Eurydice_arr_24 libcrux_mldsa65_sk;
typedef Eurydice_arr_29 libcrux_mldsa65_pk;
typedef Eurydice_borrow_slice_u8 libcrux_mldsa65_message;
typedef Eurydice_arr_ec libcrux_mldsa65_sign_rnd;
typedef core_result_Result_8c libcrux_mldsa65_sign_result;
typedef core_result_Result_41 libcrux_mldsa65_verify_result;
typedef Eurydice_arr_0c libcrux_mldsa65_signature;
/* ML-DSA 87 */
typedef Eurydice_arr_ec libcrux_mldsa87_keypair_rnd;
typedef libcrux_ml_dsa_ml_dsa_generic_ml_dsa_87_MLDSA87KeyPair
libcrux_mldsa87_keypair;
typedef Eurydice_arr_e2 libcrux_mldsa87_sk;
typedef Eurydice_arr_43 libcrux_mldsa87_pk;
typedef Eurydice_borrow_slice_u8 libcrux_mldsa87_message;
typedef Eurydice_arr_ec libcrux_mldsa87_sign_rnd;
typedef core_result_Result_8b libcrux_mldsa87_sign_result;
typedef core_result_Result_41 libcrux_mldsa87_verify_result;
typedef Eurydice_arr_93 libcrux_mldsa87_signature;
#define LIBCRUX_RESULT_OK core_result_Ok
_EOF
) > libcrux_internal.h_new
# Do some checks on the resultant file
cat > libcrux_internal_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_internal.h_new"
#define TEST_MLDSA(L) do { \
libcrux_mldsa##L##_keypair_rnd kpseed##L = {0}; \
libcrux_mldsa##L##_keypair kp##L = {0}; \
libcrux_mldsa##L##_sk sk##L = {0}; \
libcrux_mldsa##L##_pk pk##L = {0}; \
const uint8_t msgdata##L[6] = { 0x4a, 0x75, 0x6c, 0x69, 0x75, 0x73 }; \
libcrux_mldsa##L##_message msg##L = { \
msgdata##L, sizeof(msgdata##L) \
}; \
const uint8_t ctxdata##L[4] = { 0x48, 0x75, 0x67, 0x6f }; \
libcrux_mldsa##L##_message ctx##L = { \
ctxdata##L, sizeof(ctxdata##L) \
}; \
libcrux_mldsa##L##_sign_rnd signseed##L = {0}; \
libcrux_mldsa##L##_sign_result signresult##L = {0}; \
libcrux_mldsa##L##_verify_result verifyresult##L = {0}; \
libcrux_mldsa##L##_signature sig##L = {0}; \
\
if (sizeof(kpseed##L.data) != MLDSA##L##_SEEDBYTES) \
errx(1, "libcrux_mldsa%d_keypair_rnd bad", L); \
if (sizeof(kp##L.verification_key.data) != MLDSA##L##_PUBLICKEYBYTES) \
errx(1, "libcrux_mldsa%d_keypair verification_key bad", L); \
if (sizeof(kp##L.signing_key.data) != MLDSA##L##_SECRETKEYBYTES) \
errx(1, "libcrux_mldsa%d_keypair signing_key bad", L); \
if (sizeof(pk##L.data) != MLDSA##L##_PUBLICKEYBYTES) \
errx(1, "libcrux_mldsa%d_pk bad", L); \
if (sizeof(sk##L.data) != MLDSA##L##_SECRETKEYBYTES) \
errx(1, "libcrux_mldsa%d_sk bad", L); \
if (sizeof(signresult##L.val.case_Ok.data) != MLDSA##L##_SIGBYTES) \
errx(1, "libcrux_mldsa%d_sign_result bad", L); \
if (sizeof(sig##L.data) != MLDSA##L##_SIGBYTES) \
errx(1, "libcrux_mldsa%d_signature bad", L); \
\
kp##L = libcrux_ml_dsa_ml_dsa_##L##_portable_generate_key_pair( \
kpseed##L); \
sk##L = kp##L.signing_key; \
pk##L = kp##L.verification_key; \
signresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_sign(&sk##L, \
msg##L, ctx##L, signseed##L); \
if (signresult##L.tag != LIBCRUX_RESULT_OK) \
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_sign failed", L); \
sig##L = signresult##L.val.case_Ok; \
verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
msg##L, ctx##L, &sig##L); \
if (verifyresult##L.tag != LIBCRUX_RESULT_OK) \
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify failed", L); \
sig##L.data[10] ^= 0x10; /* corrupt the signature */ \
verifyresult##L = libcrux_ml_dsa_ml_dsa_##L##_portable_verify(&pk##L, \
msg##L, ctx##L, &sig##L); \
if (verifyresult##L.tag == LIBCRUX_RESULT_OK) \
errx(1, "libcrux_ml_dsa_ml_dsa_%d_portable_verify fail2", L); \
} while (0)
int main(void) {
libcrux_mlkem768_keypair keypair = {0};
libcrux_mlkem768_pk pk = {0};
libcrux_mlkem768_sk sk = {0};
libcrux_mlkem768_ciphertext ct = {0};
libcrux_mlkem768_enc_result enc_result = {0};
libcrux_mlkem768_keypair_rnd kp_seed = {0};
libcrux_mlkem768_enc_rnd enc_seed = {0};
libcrux_mlkem768_dec_result shared_secret = {0};
uint8_t shared_key[crypto_kem_mlkem768_BYTES];
if (sizeof(keypair.pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
errx(1, "keypair.pk bad");
if (sizeof(keypair.sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
errx(1, "keypair.sk bad");
if (sizeof(pk.data) != crypto_kem_mlkem768_PUBLICKEYBYTES)
errx(1, "pk bad");
if (sizeof(sk.data) != crypto_kem_mlkem768_SECRETKEYBYTES)
errx(1, "sk bad");
if (sizeof(ct.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
errx(1, "ct bad");
if (sizeof(enc_result.fst.data) != crypto_kem_mlkem768_CIPHERTEXTBYTES)
errx(1, "enc_result ct bad");
if (sizeof(enc_result.snd.data) != crypto_kem_mlkem768_BYTES)
errx(1, "enc_result shared key bad");
if (sizeof(kp_seed.data) != crypto_kem_mlkem768_KEYPAIRSEEDBYTES)
errx(1, "keypair rnd bad");
if (sizeof(enc_seed.data) != crypto_kem_mlkem768_ENCSEEDBYTES)
errx(1, "keypair rnd 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);
shared_secret = libcrux_ml_kem_mlkem768_portable_decapsulate(
&keypair.sk, &enc_result.fst);
memcpy(shared_key, shared_secret.data, sizeof(shared_key));
if (memcmp(shared_key, enc_result.snd.data, sizeof(shared_key)) != 0)
errx(1, "smoke failed");
TEST_MLDSA(44);
TEST_MLDSA(65);
TEST_MLDSA(87);
return 0;
}
_EOF
cc -Wall -Wextra -Wno-unused-parameter -I . -o libcrux_internal_check \
libcrux_internal_check.c
./libcrux_internal_check
mv libcrux_internal.h_new libcrux_internal.h
rm libcrux_internal_check libcrux_internal_check.c
echo 1>&2
echo "libcrux_internal.h OK" 1>&2
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: pathnames.h,v 1.36 2025/08/29 03:50:38 djm Exp $ */
/* $OpenBSD: pathnames.h,v 1.37 2026/06/14 03:59:34 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -33,6 +33,7 @@
#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key"
#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key"
#define _PATH_HOST_MLDSA44_ED25519_KEY_FILE SSHDIR "/ssh_host_mldsa44_ed25519_key"
#define _PATH_DH_MODULI ETCDIR "/moduli"
#define _PATH_SSH_PROGRAM "/usr/bin/ssh"
@@ -78,6 +79,7 @@
#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519"
#define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk"
#define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk"
#define _PATH_SSH_CLIENT_ID_MLDSA44_ED25519 _PATH_SSH_USER_DIR "/id_mldsa44_ed25519"
/*
* Configuration file in user's home directory. This file need not be
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.448 2026/05/31 13:12:07 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.449 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -302,6 +302,8 @@ fill_default_server_options(ServerOptions *options)
_PATH_HOST_ECDSA_KEY_FILE, 0);
servconf_add_hostkey("[default]", 0, options,
_PATH_HOST_ED25519_KEY_FILE, 0);
servconf_add_hostkey("[default]", 0, options,
_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 0);
}
/* No certificates by default */
if (options->num_ports == 0)
+7 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.490 2026/03/03 09:57:25 dtucker Exp $ */
/* $OpenBSD: ssh-keygen.c,v 1.491 2026/06/14 03:59:34 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -263,6 +263,10 @@ ask_filename(struct passwd *pw, const char *prompt)
case KEY_ED25519_SK_CERT:
name = _PATH_SSH_CLIENT_ID_ED25519_SK;
break;
case KEY_MLDSA44_ED25519:
case KEY_MLDSA44_ED25519_CERT:
name = _PATH_SSH_CLIENT_ID_MLDSA44_ED25519;
break;
default:
fatal("bad key type");
}
@@ -995,6 +999,8 @@ do_gen_all_hostkeys(struct passwd *pw)
{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
#endif /* WITH_OPENSSL */
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
{ "mldsa44-ed25519", "MLDSA44-ED25519",
_PATH_HOST_MLDSA44_ED25519_KEY_FILE },
{ NULL, NULL, NULL }
};
+12 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keyscan.c,v 1.167 2025/08/29 03:50:38 djm Exp $ */
/* $OpenBSD: ssh-keyscan.c,v 1.168 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
@@ -59,12 +59,13 @@ int ssh_port = SSH_DEFAULT_PORT;
#define KT_ED25519 (1<<2)
#define KT_ECDSA_SK (1<<4)
#define KT_ED25519_SK (1<<5)
#define KT_MLDSA44_ED25519 (1<<6)
#define KT_MIN KT_RSA
#define KT_MAX KT_ED25519_SK
#define KT_MAX KT_MLDSA44_ED25519
int get_cert = 0;
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK|KT_MLDSA44_ED25519;
int hash_hosts = 0; /* Hash hostname on output */
@@ -236,6 +237,11 @@ keygrab_ssh2(con *c)
"ecdsa-sha2-nistp384,"
"ecdsa-sha2-nistp521";
break;
case KT_MLDSA44_ED25519:
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
"ssh-mldsa44-ed25519-cert-v01@openssh.com" :
"ssh-mldsa44-ed25519@openssh.com";
break;
case KT_ECDSA_SK:
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
@@ -720,6 +726,9 @@ main(int argc, char **argv)
case KEY_ECDSA_SK:
get_keytypes |= KT_ECDSA_SK;
break;
case KEY_MLDSA44_ED25519:
get_keytypes |= KT_MLDSA44_ED25519;
break;
case KEY_UNSPEC:
default:
fatal("Unknown key type \"%s\"", tname);
+2 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keysign.c,v 1.80 2026/03/19 02:36:28 djm Exp $ */
/* $OpenBSD: ssh-keysign.c,v 1.81 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2002 Markus Friedl. All rights reserved.
*
@@ -200,6 +200,7 @@ main(int argc, char **argv)
key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, O_RDONLY);
if ((pw = getpwuid(getuid())) == NULL)
fatal("getpwuid failed");
+501
View File
@@ -0,0 +1,501 @@
/* $OpenBSD: ssh-mldsa-eddsa.c,v 1.1 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2026 Damien Miller <djm@mindrot.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* draft-miller-sshm-mldsa44-ed25519-composite-sigs-00 */
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "crypto_api.h"
#include "sshbuf.h"
#include "ssherr.h"
#include "digest.h"
#define SSHKEY_INTERNAL
#include "sshkey.h"
#include "log.h"
#define COMPOSITE_PREFIX "CompositeAlgorithmSignatures2025"
#define COMPOSITE_LABEL "COMPSIG-MLDSA44-Ed25519-SHA512"
#define SSH_MLDSA44_ED25519_ALG_NAME "ssh-mldsa44-ed25519@openssh.com"
/*
* raw_* functions implement the draft-ietf-lamps-pq-composite-sigs-18
* composite signature scheme. These are exposed (i.e. not static) so
* we can test them separately in unittests/crypto.
*/
int
crypto_sign_mldsa44_ed25519_keygen(uint8_t pk[MLDSA44_ED25519_PK_SZ],
uint8_t sk[MLDSA44_ED25519_SK_SZ])
{
uint8_t mldsa_seed[32], ed25519_seed[32];
int r;
arc4random_buf(mldsa_seed, sizeof(mldsa_seed));
arc4random_buf(ed25519_seed, sizeof(ed25519_seed));
r = crypto_sign_mldsa44_ed25519_keygen_seeded(pk, sk, mldsa_seed,
ed25519_seed);
explicit_bzero(mldsa_seed, sizeof(mldsa_seed));
explicit_bzero(ed25519_seed, sizeof(ed25519_seed));
return r;
}
int
crypto_sign_mldsa44_ed25519_keygen_seeded(uint8_t pk[MLDSA44_ED25519_PK_SZ],
uint8_t sk[MLDSA44_ED25519_SK_SZ], const uint8_t mldsa_seed[32],
const uint8_t ed25519_seed[32])
{
uint8_t ed25519_pk[32], ed25519_sk[64];
uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES];
int ret = -1;
if (crypto_sign_mldsa44_keypair_seeded(pk, mldsa_sk, mldsa_seed) != 0)
goto out;
if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk,
ed25519_seed) != 0)
goto out;
/* Serialize PK: mldsaPK || ed25519PK */
memcpy(pk + MLDSA44_PUBLICKEYBYTES, ed25519_pk, 32);
/* Serialize SK: mldsaSeed || ed25519Seed */
memcpy(sk, mldsa_seed, 32);
memcpy(sk + 32, ed25519_seed, 32);
/* success */
ret = 0;
out:
explicit_bzero(mldsa_sk, sizeof(mldsa_sk));
explicit_bzero(ed25519_sk, sizeof(ed25519_sk));
return ret;
}
static int
construct_m_prime(uint8_t **m_primep, size_t *m_prime_lenp,
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen)
{
int r;
uint8_t hash[64];
struct sshbuf *m_prime;
*m_primep = NULL;
*m_prime_lenp = 0;
if (ctxlen > 255)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = ssh_digest_memory(SSH_DIGEST_SHA512, msg, msglen,
hash, sizeof(hash))) != 0)
return r;
if ((m_prime = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((r = sshbuf_put(m_prime, COMPOSITE_PREFIX,
sizeof(COMPOSITE_PREFIX) - 1)) != 0 ||
(r = sshbuf_put(m_prime, COMPOSITE_LABEL,
sizeof(COMPOSITE_LABEL) - 1)) != 0 ||
(r = sshbuf_put_u8(m_prime, (uint8_t)ctxlen)) != 0 ||
(r = sshbuf_put(m_prime, ctx, ctxlen)) != 0 ||
(r = sshbuf_put(m_prime, hash, sizeof(hash))) != 0) {
sshbuf_free(m_prime);
return r;
}
if ((*m_primep = malloc(sshbuf_len(m_prime))) == NULL) {
sshbuf_free(m_prime);
return SSH_ERR_ALLOC_FAIL;
}
memcpy(*m_primep, sshbuf_ptr(m_prime), sshbuf_len(m_prime));
*m_prime_lenp = sshbuf_len(m_prime);
/* success */
sshbuf_free(m_prime);
return 0;
}
int
crypto_sign_mldsa44_ed25519_sign(uint8_t sig[MLDSA44_ED25519_SIG_SZ],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t sk[MLDSA44_ED25519_SK_SZ])
{
uint8_t *m_prime = NULL;
size_t m_prime_len = 0;
uint8_t mldsa_sk[MLDSA44_SECRETKEYBYTES];
uint8_t mldsa_pk_dummy[MLDSA44_PUBLICKEYBYTES];
uint8_t ed25519_pk[32], ed25519_sk[64];
uint8_t mldsa_rnd[32];
unsigned long long smlen;
int r = -1;
if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen,
ctx, ctxlen) != 0)
return -1;
/* Expand ML-DSA key from seed */
if (crypto_sign_mldsa44_keypair_seeded(mldsa_pk_dummy, mldsa_sk, sk) != 0)
goto out;
/* Sign with ML-DSA */
arc4random_buf(mldsa_rnd, sizeof(mldsa_rnd));
if (crypto_sign_mldsa44_seeded(sig, m_prime, m_prime_len,
(const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1,
mldsa_sk, mldsa_rnd) != 0)
goto out;
/* Expand Ed25519 key from seed */
if (crypto_sign_ed25519_keypair_from_seed(ed25519_pk, ed25519_sk,
sk + 32) != 0)
goto out;
/* Sign with Ed25519 */
uint8_t *sm = malloc(m_prime_len + 64);
if (sm == NULL)
goto out;
if (crypto_sign_ed25519(sm, &smlen, m_prime, m_prime_len,
ed25519_sk) != 0) {
free(sm);
goto out;
}
memcpy(sig + MLDSA44_SIGBYTES, sm, 64);
free(sm);
r = 0;
out:
free(m_prime);
explicit_bzero(mldsa_rnd, sizeof(mldsa_rnd));
explicit_bzero(mldsa_sk, sizeof(mldsa_sk));
explicit_bzero(ed25519_sk, sizeof(ed25519_sk));
return r;
}
int
crypto_sign_mldsa44_ed25519_verify(const uint8_t sig[MLDSA44_ED25519_SIG_SZ],
const uint8_t *msg, size_t msglen,
const uint8_t *ctx, size_t ctxlen,
const uint8_t pk[MLDSA44_ED25519_PK_SZ])
{
uint8_t *m_prime = NULL;
size_t m_prime_len = 0;
uint8_t *sm = NULL, *m = NULL;
unsigned long long smlen, mlen;
int r = -1;
if (construct_m_prime(&m_prime, &m_prime_len, msg, msglen,
ctx, ctxlen) != 0)
return -1;
/* Verify ML-DSA */
if (crypto_sign_mldsa44_verify(sig, m_prime, m_prime_len,
(const uint8_t *)COMPOSITE_LABEL, sizeof(COMPOSITE_LABEL) - 1,
pk) != 0)
goto out;
/* Verify Ed25519 */
smlen = m_prime_len + 64;
mlen = smlen;
if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL)
goto out;
memcpy(sm, sig + MLDSA44_SIGBYTES, 64);
memcpy(sm + 64, m_prime, m_prime_len);
if (crypto_sign_ed25519_open(m, &mlen, sm, smlen,
pk + MLDSA44_PUBLICKEYBYTES) != 0)
goto out;
if (mlen != m_prime_len)
goto out;
r = 0;
out:
free(m_prime);
free(sm);
free(m);
return r;
}
/* sshkey integration */
static void
ssh_mldsa44_ed25519_cleanup(struct sshkey *k)
{
freezero(k->mldsa_ed25519_pk, MLDSA44_ED25519_PK_SZ);
freezero(k->mldsa_ed25519_sk, MLDSA44_ED25519_SK_SZ);
k->mldsa_ed25519_pk = NULL;
k->mldsa_ed25519_sk = NULL;
}
static int
ssh_mldsa44_ed25519_equal(const struct sshkey *a, const struct sshkey *b)
{
if (a->mldsa_ed25519_pk == NULL || b->mldsa_ed25519_pk == NULL)
return 0;
if (memcmp(a->mldsa_ed25519_pk, b->mldsa_ed25519_pk,
MLDSA44_ED25519_PK_SZ) != 0)
return 0;
return 1;
}
static int
ssh_mldsa44_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
enum sshkey_serialize_rep opts)
{
int r;
if (key->mldsa_ed25519_pk == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = sshbuf_put_string(b, key->mldsa_ed25519_pk,
MLDSA44_ED25519_PK_SZ)) != 0)
return r;
return 0;
}
static int
ssh_mldsa44_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b,
enum sshkey_serialize_rep opts)
{
int r;
if (key->mldsa_ed25519_sk == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if (!sshkey_is_cert(key)) {
if ((r = ssh_mldsa44_ed25519_serialize_public(key,
b, opts)) != 0)
return r;
}
if ((r = sshbuf_put_string(b, key->mldsa_ed25519_sk,
MLDSA44_ED25519_SK_SZ)) != 0)
return r;
return 0;
}
static int
ssh_mldsa44_ed25519_deserialize_public(const char *ktype, struct sshbuf *b,
struct sshkey *key)
{
u_char *pk = NULL;
size_t len = 0;
int r;
if ((r = sshbuf_get_string(b, &pk, &len)) != 0)
return r;
if (len != MLDSA44_ED25519_PK_SZ) {
freezero(pk, len);
return SSH_ERR_INVALID_FORMAT;
}
key->mldsa_ed25519_pk = pk;
return 0;
}
static int
ssh_mldsa44_ed25519_deserialize_private(const char *ktype, struct sshbuf *b,
struct sshkey *key)
{
int r;
size_t sklen = 0;
u_char *sk = NULL;
if (!sshkey_is_cert(key)) {
if ((r = ssh_mldsa44_ed25519_deserialize_public(ktype,
b, key)) != 0)
return r;
}
if ((r = sshbuf_get_string(b, &sk, &sklen)) != 0)
goto out;
if (sklen != MLDSA44_ED25519_SK_SZ) {
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
key->mldsa_ed25519_sk = sk;
sk = NULL; /* transferred */
r = 0;
out:
freezero(sk, sklen);
return r;
}
static int
ssh_mldsa44_ed25519_generate(struct sshkey *k, int bits)
{
free(k->mldsa_ed25519_pk);
free(k->mldsa_ed25519_sk);
k->mldsa_ed25519_pk = NULL;
k->mldsa_ed25519_sk = NULL;
if ((k->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL ||
(k->mldsa_ed25519_sk = malloc(MLDSA44_ED25519_SK_SZ)) == NULL) {
free(k->mldsa_ed25519_pk);
return SSH_ERR_ALLOC_FAIL;
}
if (crypto_sign_mldsa44_ed25519_keygen(k->mldsa_ed25519_pk,
k->mldsa_ed25519_sk) != 0) {
free(k->mldsa_ed25519_pk);
free(k->mldsa_ed25519_sk);
return SSH_ERR_CRYPTO_ERROR;
}
return 0;
}
static int
ssh_mldsa44_ed25519_copy_public(const struct sshkey *from, struct sshkey *to)
{
if (from->mldsa_ed25519_pk == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((to->mldsa_ed25519_pk = malloc(MLDSA44_ED25519_PK_SZ)) == NULL)
return SSH_ERR_ALLOC_FAIL;
memcpy(to->mldsa_ed25519_pk, from->mldsa_ed25519_pk,
MLDSA44_ED25519_PK_SZ);
return 0;
}
static int
ssh_mldsa44_ed25519_sign(struct sshkey *key,
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
const char *alg, const char *sk_provider, const char *sk_pin,
u_int compat)
{
u_char sig[MLDSA44_ED25519_SIG_SZ];
struct sshbuf *b = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
if (lenp != NULL)
*lenp = 0;
if (sigp != NULL)
*sigp = NULL;
if (key == NULL ||
sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 ||
key->mldsa_ed25519_sk == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if (crypto_sign_mldsa44_ed25519_sign(sig, data, datalen, NULL, 0,
key->mldsa_ed25519_sk) != 0) {
r = SSH_ERR_CRYPTO_ERROR;
goto out;
}
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_put_cstring(b, SSH_MLDSA44_ED25519_ALG_NAME)) != 0 ||
(r = sshbuf_put_string(b, sig, sizeof(sig))) != 0)
goto out;
if (sigp != NULL) {
if ((*sigp = malloc(sshbuf_len(b))) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
memcpy(*sigp, sshbuf_ptr(b), sshbuf_len(b));
}
if (lenp != NULL)
*lenp = sshbuf_len(b);
r = 0;
out:
sshbuf_free(b);
explicit_bzero(sig, sizeof(sig));
return r;
}
static int
ssh_mldsa44_ed25519_verify(const struct sshkey *key,
const u_char *sig, size_t siglen, const u_char *data, size_t dlen,
const char *alg, u_int compat, struct sshkey_sig_details **detailsp)
{
struct sshbuf *b = NULL;
char *ktype = NULL;
const u_char *sigblob;
size_t len;
int r;
if (key == NULL ||
sshkey_type_plain(key->type) != KEY_MLDSA44_ED25519 ||
key->mldsa_ed25519_pk == NULL ||
sig == NULL || siglen == 0)
return SSH_ERR_INVALID_ARGUMENT;
if ((b = sshbuf_from(sig, siglen)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
(r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
goto out;
if (strcmp(SSH_MLDSA44_ED25519_ALG_NAME, ktype) != 0) {
r = SSH_ERR_KEY_TYPE_MISMATCH;
goto out;
}
if (sshbuf_len(b) != 0) {
r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
goto out;
}
if (len != MLDSA44_ED25519_SIG_SZ) {
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (crypto_sign_mldsa44_ed25519_verify(sigblob, data, dlen, NULL, 0,
key->mldsa_ed25519_pk) != 0) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
r = 0;
out:
sshbuf_free(b);
free(ktype);
return r;
}
const struct sshkey_impl_funcs sshkey_mldsa44_ed25519_funcs = {
/* .size = */ NULL,
/* .alloc = */ NULL,
/* .cleanup = */ ssh_mldsa44_ed25519_cleanup,
/* .equal = */ ssh_mldsa44_ed25519_equal,
/* .ssh_serialize_public = */ ssh_mldsa44_ed25519_serialize_public,
/* .ssh_deserialize_public = */ ssh_mldsa44_ed25519_deserialize_public,
/* .ssh_serialize_private = */ ssh_mldsa44_ed25519_serialize_private,
/* .ssh_deserialize_private = */ ssh_mldsa44_ed25519_deserialize_private,
/* .generate = */ ssh_mldsa44_ed25519_generate,
/* .copy_public = */ ssh_mldsa44_ed25519_copy_public,
/* .sign = */ ssh_mldsa44_ed25519_sign,
/* .verify = */ ssh_mldsa44_ed25519_verify,
};
const struct sshkey_impl sshkey_mldsa44_ed25519_impl = {
/* .name = */ "ssh-mldsa44-ed25519@openssh.com",
/* .shortname = */ "MLDSA44-ED25519",
/* .sigalg = */ NULL,
/* .type = */ KEY_MLDSA44_ED25519,
/* .nid = */ 0,
/* .cert = */ 0,
/* .sigonly = */ 0,
/* .keybits = */ 256,
/* .funcs = */ &sshkey_mldsa44_ed25519_funcs,
};
const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl = {
/* .name = */ "ssh-mldsa44-ed25519-cert-v01@openssh.com",
/* .shortname = */ "MLDSA44-ED25519-CERT",
/* .sigalg = */ NULL,
/* .type = */ KEY_MLDSA44_ED25519_CERT,
/* .nid = */ 0,
/* .cert = */ 1,
/* .sigonly = */ 0,
/* .keybits = */ 256,
/* .funcs = */ &sshkey_mldsa44_ed25519_funcs,
};
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.632 2026/05/31 05:55:21 djm Exp $ */
/* $OpenBSD: ssh.c,v 1.633 2026/06/14 03:59:34 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1719,9 +1719,11 @@ main(int ac, char **av)
L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0);
L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1);
L_CERT(_PATH_HOST_RSA_KEY_FILE, 2);
L_CERT(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 3);
L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4);
L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5);
L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6);
L_PUBKEY(_PATH_HOST_MLDSA44_ED25519_KEY_FILE, 7);
if (loaded == 0)
debug("HostbasedAuthentication enabled but no "
"local public host keys could be loaded.");
+2 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect.c,v 1.382 2026/02/16 00:45:41 dtucker Exp $ */
/* $OpenBSD: sshconnect.c,v 1.383 2026/06/14 03:59:34 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1604,6 +1604,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
KEY_RSA,
KEY_ECDSA,
KEY_ED25519,
KEY_MLDSA44_ED25519,
-1
};
int i, ret = 0;
+4 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshd-auth.c,v 1.15 2026/05/31 11:30:50 djm Exp $ */
/* $OpenBSD: sshd-auth.c,v 1.16 2026/06/14 03:59:34 djm Exp $ */
/*
* SSH2 implementation:
* Privilege Separation:
@@ -236,6 +236,7 @@ list_hostkey_types(void)
/* FALLTHROUGH */
case KEY_ECDSA:
case KEY_ED25519:
case KEY_MLDSA44_ED25519:
case KEY_ECDSA_SK:
case KEY_ED25519_SK:
append_hostkey_type(b, sshkey_ssh_name(key));
@@ -255,6 +256,7 @@ list_hostkey_types(void)
/* FALLTHROUGH */
case KEY_ECDSA_CERT:
case KEY_ED25519_CERT:
case KEY_MLDSA44_ED25519_CERT:
case KEY_ECDSA_SK_CERT:
case KEY_ED25519_SK_CERT:
append_hostkey_type(b, sshkey_ssh_name(key));
@@ -279,6 +281,7 @@ get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
case KEY_RSA_CERT:
case KEY_ECDSA_CERT:
case KEY_ED25519_CERT:
case KEY_MLDSA44_ED25519_CERT:
case KEY_ECDSA_SK_CERT:
case KEY_ED25519_SK_CERT:
key = host_certificates[i];
+2 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshd-session.c,v 1.23 2026/03/11 09:10:59 dtucker Exp $ */
/* $OpenBSD: sshd-session.c,v 1.24 2026/06/14 03:59:34 djm Exp $ */
/*
* SSH2 implementation:
* Privilege Separation:
@@ -405,6 +405,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
case KEY_ED25519_CERT:
case KEY_ECDSA_SK_CERT:
case KEY_ED25519_SK_CERT:
case KEY_MLDSA44_ED25519_CERT:
key = sensitive_data.host_certificates[i];
break;
default:
+2 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.626 2026/02/09 21:21:39 dtucker Exp $ */
/* $OpenBSD: sshd.c,v 1.627 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
* Copyright (c) 2002 Niels Provos. All rights reserved.
@@ -1598,6 +1598,7 @@ main(int ac, char **av)
case KEY_RSA:
case KEY_ECDSA:
case KEY_ED25519:
case KEY_MLDSA44_ED25519:
case KEY_ECDSA_SK:
case KEY_ED25519_SK:
if (have_agent || key != NULL)
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.c,v 1.11 2026/02/06 23:31:29 dtucker Exp $ */
/* $OpenBSD: ssherr.c,v 1.12 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@@ -72,6 +72,8 @@ ssh_err(int n)
case SSH_ERR_LIBCRYPTO_ERROR:
msg = ssherr_libcrypto();
return msg != NULL ? msg : "error in libcrypto";
case SSH_ERR_INTERNAL_CRYPTO_ERROR:
return "cryptographic operation failed";
case SSH_ERR_UNEXPECTED_TRAILING_DATA:
return "unexpected bytes remain after decoding";
case SSH_ERR_SYSTEM_ERROR:
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.h,v 1.9 2026/02/06 23:31:29 dtucker Exp $ */
/* $OpenBSD: ssherr.h,v 1.10 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@@ -82,6 +82,8 @@
#define SSH_ERR_SIGN_ALG_UNSUPPORTED -58
#define SSH_ERR_FEATURE_UNSUPPORTED -59
#define SSH_ERR_DEVICE_NOT_FOUND -60
#define SSH_ERR_CRYPTO_ERROR -61
#define SSH_ERR_INTERNAL_CRYPTO_ERROR -62
/* Translate a numeric error code to a human-readable error string */
const char *ssh_err(int n);
+10 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.161 2026/02/06 22:59:18 dtucker Exp $ */
/* $OpenBSD: sshkey.c,v 1.162 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@@ -87,6 +87,8 @@ extern const struct sshkey_impl sshkey_ed25519_impl;
extern const struct sshkey_impl sshkey_ed25519_cert_impl;
extern const struct sshkey_impl sshkey_ed25519_sk_impl;
extern const struct sshkey_impl sshkey_ed25519_sk_cert_impl;
extern const struct sshkey_impl sshkey_mldsa44_ed25519_impl;
extern const struct sshkey_impl sshkey_mldsa44_ed25519_cert_impl;
#ifdef WITH_OPENSSL
extern const struct sshkey_impl sshkey_ecdsa_sk_impl;
extern const struct sshkey_impl sshkey_ecdsa_sk_cert_impl;
@@ -111,6 +113,8 @@ const struct sshkey_impl * const keyimpls[] = {
&sshkey_ed25519_cert_impl,
&sshkey_ed25519_sk_impl,
&sshkey_ed25519_sk_cert_impl,
&sshkey_mldsa44_ed25519_impl,
&sshkey_mldsa44_ed25519_cert_impl,
#ifdef WITH_OPENSSL
&sshkey_ecdsa_nistp256_impl,
&sshkey_ecdsa_nistp256_cert_impl,
@@ -422,6 +426,8 @@ sshkey_type_plain(int type)
return KEY_ECDSA_SK;
case KEY_ED25519_CERT:
return KEY_ED25519;
case KEY_MLDSA44_ED25519_CERT:
return KEY_MLDSA44_ED25519;
case KEY_ED25519_SK_CERT:
return KEY_ED25519_SK;
default:
@@ -442,6 +448,8 @@ sshkey_type_certified(int type)
return KEY_ECDSA_SK_CERT;
case KEY_ED25519:
return KEY_ED25519_CERT;
case KEY_MLDSA44_ED25519:
return KEY_MLDSA44_ED25519_CERT;
case KEY_ED25519_SK:
return KEY_ED25519_SK_CERT;
default:
@@ -3342,6 +3350,7 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
#ifdef WITH_OPENSSL
case KEY_ECDSA_SK:
#endif /* WITH_OPENSSL */
case KEY_MLDSA44_ED25519:
return sshkey_private_to_blob2(key, blob, passphrase,
comment, openssh_format_cipher, openssh_format_rounds);
default:
+6 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.73 2026/03/03 09:57:26 dtucker Exp $ */
/* $OpenBSD: sshkey.h,v 1.74 2026/06/14 03:59:34 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -61,6 +61,8 @@ enum sshkey_types {
KEY_ECDSA_SK_CERT,
KEY_ED25519_SK,
KEY_ED25519_SK_CERT,
KEY_MLDSA44_ED25519,
KEY_MLDSA44_ED25519_CERT,
KEY_UNSPEC
};
@@ -118,6 +120,9 @@ struct sshkey {
/* KEY_ED25519 and KEY_ED25519_SK */
u_char *ed25519_sk;
u_char *ed25519_pk;
/* KEY_MLDSA44_ED25519 */
u_char *mldsa_ed25519_sk;
u_char *mldsa_ed25519_pk;
/* KEY_ECDSA_SK and KEY_ED25519_SK */
char *sk_application;
uint8_t sk_flags;