[openssh-commits] [openssh] 02/06: upstream: ssh: implement mlkem768nistp256-sha256; ok djm@

git+noreply at mindrot.org git+noreply at mindrot.org
Thu Jul 30 13:49:49 AEST 2026


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 786cb6a14cede4c1bfcf7a1989b53c9a28c8bee4
Author: markus at openbsd.org <markus at openbsd.org>
AuthorDate: Mon Jul 27 12:28:52 2026 +0000

    upstream: ssh: implement mlkem768nistp256-sha256; ok djm@
    
    OpenBSD-Commit-ID: f5daafce2fcb44684606197873dab4cd189524a9
---
 Makefile.in       |   2 +-
 kex-names.c       |   4 +-
 kex.h             |  13 ++-
 kexecdh.c         |   8 +-
 kexgen.c          |  13 ++-
 kexmlkem768ecdh.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 monitor.c         |   3 +-
 ssh-keyscan.c     |   3 +-
 ssh_api.c         |   4 +-
 sshconnect2.c     |   3 +-
 sshd-auth.c       |   3 +-
 11 files changed, 344 insertions(+), 15 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index da59bcc10..e9503e7b7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -105,7 +105,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
 	libcrux-mlkem-mldsa.o ssh-mldsa-eddsa.o \
 	hmac.o ed25519.o ed25519-openssl.o \
 	kex.o kex-names.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
-	kexgexc.o kexgexs.o \
+	kexgexc.o kexgexs.o kexmlkem768ecdh.o \
 	kexsntrup761x25519.o kexmlkem768x25519.o sntrup761.o kexgen.o \
 	sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
 	sshbuf-io.o misc-agent.o ssherr-libcrypto.o
diff --git a/kex-names.c b/kex-names.c
index 751f06cea..cb189431d 100644
--- a/kex-names.c
+++ b/kex-names.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex-names.c,v 1.7 2026/02/14 00:18:34 jsg Exp $ */
+/* $OpenBSD: kex-names.c,v 1.8 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
@@ -70,6 +70,8 @@ static const struct kexalg kexalgs[] = {
 	{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
 	    SSH_DIGEST_SHA512, KEX_NOT_PQ },
 # endif /* OPENSSL_HAS_NISTP521 */
+	{ KEX_MLKEM768NISTP256_SHA256, KEX_KEM_MLKEM768ECDH_SHA256,
+	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256, KEX_IS_PQ },
 #endif /* OPENSSL_HAS_ECC */
 #endif /* WITH_OPENSSL */
 #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
diff --git a/kex.h b/kex.h
index a4d50f1a3..8677f2709 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.130 2026/05/31 04:44:38 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.131 2026/07/27 12:28:52 markus Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -65,6 +65,7 @@
 #define	KEX_SNTRUP761X25519_SHA512	"sntrup761x25519-sha512"
 #define	KEX_SNTRUP761X25519_SHA512_OLD	"sntrup761x25519-sha512 at openssh.com"
 #define	KEX_MLKEM768X25519_SHA256	"mlkem768x25519-sha256"
+#define	KEX_MLKEM768NISTP256_SHA256	"mlkem768nistp256-sha256"
 
 #define COMP_NONE	0
 #define COMP_DELAYED	2
@@ -103,6 +104,7 @@ enum kex_exchange {
 	KEX_C25519_SHA256,
 	KEX_KEM_SNTRUP761X25519_SHA512,
 	KEX_KEM_MLKEM768X25519_SHA256,
+	KEX_KEM_MLKEM768ECDH_SHA256,
 	KEX_MAX
 };
 
@@ -239,6 +241,9 @@ int	 kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
     struct sshbuf **);
 int	 kex_dh_dec(struct kex *, const struct sshbuf *, struct sshbuf **);
 
+int	 kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key,
+	    const EC_GROUP *, struct sshbuf **);
+
 int	 kex_ecdh_keypair(struct kex *);
 int	 kex_ecdh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
     struct sshbuf **);
@@ -261,6 +266,12 @@ int	 kex_kem_mlkem768x25519_enc(struct kex *, const struct sshbuf *,
 int	 kex_kem_mlkem768x25519_dec(struct kex *, const struct sshbuf *,
     struct sshbuf **);
 
+int	 kex_kem_mlkem768ecdh_keypair(struct kex *);
+int	 kex_kem_mlkem768ecdh_enc(struct kex *, const struct sshbuf *,
+    struct sshbuf **, struct sshbuf **);
+int	 kex_kem_mlkem768ecdh_dec(struct kex *, const struct sshbuf *,
+    struct sshbuf **);
+
 int	 kex_dh_keygen(struct kex *);
 int	 kex_dh_compute_key(struct kex *, BIGNUM *, struct sshbuf *);
 
diff --git a/kexecdh.c b/kexecdh.c
index 6a9058cdc..beded1255 100644
--- a/kexecdh.c
+++ b/kexecdh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexecdh.c,v 1.12 2026/02/14 00:18:34 jsg Exp $ */
+/* $OpenBSD: kexecdh.c,v 1.13 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright (c) 2010 Damien Miller.  All rights reserved.
  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
@@ -41,10 +41,6 @@
 #include "sshbuf.h"
 #include "ssherr.h"
 
-static int
-kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key,
-    const EC_GROUP *, struct sshbuf **);
-
 int
 kex_ecdh_keypair(struct kex *kex)
 {
@@ -133,7 +129,7 @@ kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob,
 	return r;
 }
 
-static int
+int
 kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob,
     EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp)
 {
diff --git a/kexgen.c b/kexgen.c
index 5643bc831..b6bd438b5 100644
--- a/kexgen.c
+++ b/kexgen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexgen.c,v 1.12 2026/03/03 09:57:25 dtucker Exp $ */
+/* $OpenBSD: kexgen.c,v 1.13 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
  *
@@ -123,6 +123,9 @@ kex_gen_client(struct ssh *ssh)
 	case KEX_KEM_MLKEM768X25519_SHA256:
 		r = kex_kem_mlkem768x25519_keypair(kex);
 		break;
+	case KEX_KEM_MLKEM768ECDH_SHA256:
+		r = kex_kem_mlkem768ecdh_keypair(kex);
+		break;
 	default:
 		r = SSH_ERR_INVALID_ARGUMENT;
 		break;
@@ -199,6 +202,10 @@ input_kex_gen_reply(int type, uint32_t seq, struct ssh *ssh)
 		r = kex_kem_mlkem768x25519_dec(kex, server_blob,
 		    &shared_secret);
 		break;
+	case KEX_KEM_MLKEM768ECDH_SHA256:
+		r = kex_kem_mlkem768ecdh_dec(kex, server_blob,
+		    &shared_secret);
+		break;
 	default:
 		r = SSH_ERR_INVALID_ARGUMENT;
 		break;
@@ -323,6 +330,10 @@ input_kex_gen_init(int type, uint32_t seq, struct ssh *ssh)
 		r = kex_kem_mlkem768x25519_enc(kex, client_pubkey,
 		    &server_pubkey, &shared_secret);
 		break;
+	case KEX_KEM_MLKEM768ECDH_SHA256:
+		r = kex_kem_mlkem768ecdh_enc(kex, client_pubkey,
+		    &server_pubkey, &shared_secret);
+		break;
 	default:
 		r = SSH_ERR_INVALID_ARGUMENT;
 		break;
diff --git a/kexmlkem768ecdh.c b/kexmlkem768ecdh.c
new file mode 100644
index 000000000..94c2aeae4
--- /dev/null
+++ b/kexmlkem768ecdh.c
@@ -0,0 +1,303 @@
+/* $OpenBSD: kexmlkem768ecdh.c,v */
+/*
+ * Copyright (c) 2025 Markus Friedl.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <signal.h>
+#include <endian.h>
+
+#include "sshkey.h"
+#include "kex.h"
+#include "sshbuf.h"
+#include "digest.h"
+#include "ssherr.h"
+
+int
+kex_kem_mlkem768ecdh_keypair(struct kex *kex)
+{
+	struct sshbuf *buf = NULL;
+	struct sshbuf *ec_blob = NULL;
+	EC_KEY *client_key = NULL;
+	const EC_GROUP *group;
+	const EC_POINT *public_key;
+	u_char *cp = NULL;
+	size_t need;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	if ((buf = sshbuf_new()) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	need = MLKEM768_PUBLICKEYBYTES;
+	if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
+		goto out;
+	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,
+	    MLKEM768_PUBLICKEYBYTES);
+#endif
+	if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if (EC_KEY_generate_key(client_key) != 1) {
+		r = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+	group = EC_KEY_get0_group(client_key);
+	public_key = EC_KEY_get0_public_key(client_key);
+
+	if ((ec_blob = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((r = sshbuf_put_ec(ec_blob, public_key, group)) != 0 ||
+	    (r = sshbuf_get_u32(ec_blob, NULL)) != 0 ||
+	    (r = sshbuf_putb(buf, ec_blob)) != 0)
+		goto out;
+#ifdef DEBUG_KEXECDH
+	fputs("client private key EC:\n", stderr);
+	sshkey_dump_ec_key(client_key);
+#endif
+	/* success */
+	r = 0;
+	kex->ec_client_key = client_key;
+	kex->ec_group = group;
+	client_key = NULL;	/* owned by the kex */
+	kex->client_pub = buf;
+	buf = NULL;
+ out:
+	sshbuf_free(buf);
+	sshbuf_free(ec_blob);
+	EC_KEY_free(client_key);
+	return r;
+}
+
+int
+kex_kem_mlkem768ecdh_enc(struct kex *kex,
+   const struct sshbuf *client_blob, struct sshbuf **server_blobp,
+   struct sshbuf **shared_secretp)
+{
+	const EC_GROUP *group;
+	const EC_POINT *pub_key;
+	EC_KEY *server_key = NULL;
+	struct sshbuf *ec_pub = NULL;
+	struct sshbuf *ec_blob = NULL;
+	struct sshbuf *ec_shared = NULL;
+	struct sshbuf *server_blob = NULL;
+	struct sshbuf *buf = NULL;
+	const u_char *client_pub;
+	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;
+
+	*server_blobp = NULL;
+	*shared_secretp = NULL;
+
+	/* client_blob contains both KEM and ECDH client pubkeys */
+	need = MLKEM768_PUBLICKEYBYTES;
+	if (sshbuf_len(client_blob) <= need) {
+		r = SSH_ERR_SIGNATURE_INVALID;
+		goto out;
+	}
+	client_pub = sshbuf_ptr(client_blob);
+#ifdef DEBUG_KEXECDH
+	dump_digest("client public key mlkem768:", client_pub,
+	    MLKEM768_PUBLICKEYBYTES);
+#endif
+
+	/* allocate buffer for concatenation of KEM key and ECDH shared key */
+	/* the buffer will be hashed and the result is the shared secret */
+	if ((buf = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	/* allocate space for encrypted KEM key and ECDH pub key */
+	if ((server_blob = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	/* generate and encrypt KEM key with client key */
+	if (crypto_kem_mlkem768_enc(ct, shared_secret, client_pub) != 0) {
+		r = SSH_ERR_INTERNAL_ERROR;
+		goto out;
+	}
+	if ((r = sshbuf_put(buf, shared_secret, sizeof(shared_secret))) != 0 ||
+	    (r = sshbuf_put(server_blob, ct, sizeof(ct))) != 0)
+		goto out;
+
+	client_pub += MLKEM768_PUBLICKEYBYTES;
+	if ((ec_pub = sshbuf_from(client_pub, sshbuf_len(client_blob) -
+	    MLKEM768_PUBLICKEYBYTES)) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	/* generate ECDH key pair */
+	if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if (EC_KEY_generate_key(server_key) != 1) {
+		r = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+	group = EC_KEY_get0_group(server_key);
+#ifdef DEBUG_KEXECDH
+	fputs("server private key EC:\n", stderr);
+	sshkey_dump_ec_key(server_key);
+#endif
+	/* store server pubkey after ciphertext */
+	pub_key = EC_KEY_get0_public_key(server_key);
+	if ((ec_blob = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((r = sshbuf_put_ec(ec_blob, pub_key, group)) != 0 ||
+	    (r = sshbuf_get_u32(ec_blob, NULL)) != 0 ||
+	    (r = sshbuf_putb(server_blob, ec_blob)) != 0)
+		goto out;
+
+	/* append ECDH shared key */
+	if ((r = kex_ecdh_dec_key_group(kex, ec_pub, server_key, group,
+	    &ec_shared)) != 0 ||
+	    (r = sshbuf_putb(buf, ec_shared)) != 0)
+		goto out;
+
+	if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
+		goto out;
+#ifdef DEBUG_KEXECDH
+	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
+	/* string-encoded hash is resulting shared secret */
+	sshbuf_reset(buf);
+	if ((r = sshbuf_put_string(buf, hash,
+	    ssh_digest_bytes(kex->hash_alg))) != 0)
+		goto out;
+#ifdef DEBUG_KEXECDH
+	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+	/* success */
+	r = 0;
+	*server_blobp = server_blob;
+	*shared_secretp = buf;
+	server_blob = NULL;
+	buf = NULL;
+ out:
+	explicit_bzero(hash, sizeof(hash));
+	explicit_bzero(shared_secret, sizeof(shared_secret));
+	EC_KEY_free(server_key);
+	sshbuf_free(ec_pub);
+	sshbuf_free(ec_blob);
+	sshbuf_free(ec_shared);
+	sshbuf_free(server_blob);
+	sshbuf_free(buf);
+	return r;
+}
+
+int
+kex_kem_mlkem768ecdh_dec(struct kex *kex,
+    const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
+{
+	struct sshbuf *buf = NULL;
+	struct sshbuf *ec_pub = NULL;
+	struct sshbuf *ec_shared = NULL;
+	u_char shared_secret[MLKEM768_BYTES];
+	const u_char *ciphertext, *server_pub;
+	u_char hash[SSH_DIGEST_MAX_LENGTH];
+	size_t need;
+	int r;
+
+	*shared_secretp = NULL;
+
+	need = MLKEM768_CIPHERTEXTBYTES;
+	if (sshbuf_len(server_blob) <= need) {
+		r = SSH_ERR_SIGNATURE_INVALID;
+		goto out;
+	}
+	ciphertext = sshbuf_ptr(server_blob);
+	server_pub = ciphertext + MLKEM768_CIPHERTEXTBYTES;
+#ifdef DEBUG_KEXECDH
+	dump_digest("server cipher text (dec):", ciphertext,
+	    MLKEM768_CIPHERTEXTBYTES);
+#endif
+	/* hash concatenation of KEM key and ECDH shared key */
+	if ((buf = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	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 ((ec_pub = sshbuf_from(server_pub, sshbuf_len(server_blob) - need))
+	    == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((r = kex_ecdh_dec(kex, ec_pub, &ec_shared)) != 0 ||
+	    (r = sshbuf_putb(buf, ec_shared)) != 0)
+		goto out;
+	if ((r = ssh_digest_buffer(kex->hash_alg, buf,
+	    hash, sizeof(hash))) != 0)
+		goto out;
+#ifdef DEBUG_KEXECDH
+	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
+	sshbuf_reset(buf);
+	if ((r = sshbuf_put_string(buf, hash,
+	    ssh_digest_bytes(kex->hash_alg))) != 0)
+		goto out;
+#ifdef DEBUG_KEXECDH
+	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
+#endif
+	/* success */
+	r = 0;
+	*shared_secretp = buf;
+	buf = NULL;
+ out:
+	explicit_bzero(hash, sizeof(hash));
+	explicit_bzero(shared_secret, sizeof(shared_secret));
+	EC_KEY_free(kex->ec_client_key);
+	kex->ec_client_key = NULL;
+	sshbuf_free(ec_pub);
+	sshbuf_free(ec_shared);
+	sshbuf_free(buf);
+	return r;
+}
diff --git a/monitor.c b/monitor.c
index 0b865fd68..013deae5f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.257 2026/07/09 02:22:10 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.258 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos at citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus at openbsd.org>
@@ -1876,6 +1876,7 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
 # ifdef OPENSSL_HAS_ECC
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
+	kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_server;
 # endif
 #endif /* WITH_OPENSSL */
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;
diff --git a/ssh-keyscan.c b/ssh-keyscan.c
index ddaf8dfb1..1c473913d 100644
--- a/ssh-keyscan.c
+++ b/ssh-keyscan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keyscan.c,v 1.168 2026/06/14 03:59:34 djm Exp $ */
+/* $OpenBSD: ssh-keyscan.c,v 1.169 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright 1995, 1996 by David Mazieres <dm at lcs.mit.edu>.
  *
@@ -297,6 +297,7 @@ keygrab_ssh2(con *c)
 	c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
 	c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
 	c->c_ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+	c->c_ssh->kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_client;
 	ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper);
 	/*
 	 * do the key-exchange until an error occurs or until
diff --git a/ssh_api.c b/ssh_api.c
index 38ac17da1..4d1e2cd1a 100644
--- a/ssh_api.c
+++ b/ssh_api.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh_api.c,v 1.34 2026/03/03 09:57:25 dtucker Exp $ */
+/* $OpenBSD: ssh_api.c,v 1.35 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright (c) 2012 Markus Friedl.  All rights reserved.
  *
@@ -135,6 +135,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
 		ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
 		ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
 		ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_server;
+		ssh->kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_server;
 		ssh->kex->load_host_public_key=&_ssh_host_public_key;
 		ssh->kex->load_host_private_key=&_ssh_host_private_key;
 		ssh->kex->sign=&_ssh_host_key_sign;
@@ -154,6 +155,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
 		ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
 		ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
 		ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+		ssh->kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_client;
 		ssh->kex->verify_host_key =&_ssh_verify_host_key;
 	}
 	*sshp = ssh;
diff --git a/sshconnect2.c b/sshconnect2.c
index ef2c23da8..f46f29937 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.389 2026/07/21 06:17:42 djm Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.390 2026/07/27 12:28:52 markus Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
@@ -277,6 +277,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr_storage *hostaddr,
 	ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
 	ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
 	ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client;
+	ssh->kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_client;
 	ssh->kex->verify_host_key=&verify_host_key_callback;
 
 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
diff --git a/sshd-auth.c b/sshd-auth.c
index 83178e868..0005e4cf9 100644
--- a/sshd-auth.c
+++ b/sshd-auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd-auth.c,v 1.17 2026/07/21 06:17:42 djm Exp $ */
+/* $OpenBSD: sshd-auth.c,v 1.18 2026/07/27 12:28:52 markus Exp $ */
 /*
  * SSH2 implementation:
  * Privilege Separation:
@@ -808,6 +808,7 @@ do_ssh2_kex(struct ssh *ssh)
 	kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
 # ifdef OPENSSL_HAS_ECC
 	kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
+	kex->kex[KEX_KEM_MLKEM768ECDH_SHA256] = kex_gen_server;
 # endif /* OPENSSL_HAS_ECC */
 #endif /* WITH_OPENSSL */
 	kex->kex[KEX_C25519_SHA256] = kex_gen_server;

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list