1*4e5f03cbSdjm /* $OpenBSD: kexgen.c,v 1.10 2024/09/09 02:39:57 djm Exp $ */ 2d84d4014Sdjm /* 3d84d4014Sdjm * Copyright (c) 2019 Markus Friedl. All rights reserved. 4d84d4014Sdjm * 5d84d4014Sdjm * Redistribution and use in source and binary forms, with or without 6d84d4014Sdjm * modification, are permitted provided that the following conditions 7d84d4014Sdjm * are met: 8d84d4014Sdjm * 1. Redistributions of source code must retain the above copyright 9d84d4014Sdjm * notice, this list of conditions and the following disclaimer. 10d84d4014Sdjm * 2. Redistributions in binary form must reproduce the above copyright 11d84d4014Sdjm * notice, this list of conditions and the following disclaimer in the 12d84d4014Sdjm * documentation and/or other materials provided with the distribution. 13d84d4014Sdjm * 14d84d4014Sdjm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15d84d4014Sdjm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16d84d4014Sdjm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17d84d4014Sdjm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18d84d4014Sdjm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19d84d4014Sdjm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20d84d4014Sdjm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21d84d4014Sdjm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22d84d4014Sdjm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23d84d4014Sdjm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24d84d4014Sdjm */ 25d84d4014Sdjm 26d84d4014Sdjm #include <sys/types.h> 27d84d4014Sdjm 28d84d4014Sdjm #include <stdio.h> 29d84d4014Sdjm #include <string.h> 30d84d4014Sdjm #include <signal.h> 31d84d4014Sdjm 32d84d4014Sdjm #include "sshkey.h" 33d84d4014Sdjm #include "kex.h" 34d84d4014Sdjm #include "log.h" 35d84d4014Sdjm #include "packet.h" 36d84d4014Sdjm #include "ssh2.h" 37d84d4014Sdjm #include "sshbuf.h" 38d84d4014Sdjm #include "digest.h" 39d84d4014Sdjm #include "ssherr.h" 40d84d4014Sdjm 41d84d4014Sdjm static int input_kex_gen_init(int, u_int32_t, struct ssh *); 42d84d4014Sdjm static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); 43d84d4014Sdjm 44d84d4014Sdjm static int 45d84d4014Sdjm kex_gen_hash( 46d84d4014Sdjm int hash_alg, 47d84d4014Sdjm const struct sshbuf *client_version, 48d84d4014Sdjm const struct sshbuf *server_version, 4921d29470Sdjm const struct sshbuf *client_kexinit, 5021d29470Sdjm const struct sshbuf *server_kexinit, 5121d29470Sdjm const struct sshbuf *server_host_key_blob, 52d84d4014Sdjm const struct sshbuf *client_pub, 53d84d4014Sdjm const struct sshbuf *server_pub, 54d84d4014Sdjm const struct sshbuf *shared_secret, 55d84d4014Sdjm u_char *hash, size_t *hashlen) 56d84d4014Sdjm { 57d84d4014Sdjm struct sshbuf *b; 58d84d4014Sdjm int r; 59d84d4014Sdjm 60d84d4014Sdjm if (*hashlen < ssh_digest_bytes(hash_alg)) 61d84d4014Sdjm return SSH_ERR_INVALID_ARGUMENT; 62d84d4014Sdjm if ((b = sshbuf_new()) == NULL) 63d84d4014Sdjm return SSH_ERR_ALLOC_FAIL; 64d84d4014Sdjm if ((r = sshbuf_put_stringb(b, client_version)) != 0 || 65d84d4014Sdjm (r = sshbuf_put_stringb(b, server_version)) != 0 || 66d84d4014Sdjm /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ 6721d29470Sdjm (r = sshbuf_put_u32(b, sshbuf_len(client_kexinit) + 1)) != 0 || 68d84d4014Sdjm (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || 6921d29470Sdjm (r = sshbuf_putb(b, client_kexinit)) != 0 || 7021d29470Sdjm (r = sshbuf_put_u32(b, sshbuf_len(server_kexinit) + 1)) != 0 || 71d84d4014Sdjm (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || 7221d29470Sdjm (r = sshbuf_putb(b, server_kexinit)) != 0 || 7321d29470Sdjm (r = sshbuf_put_stringb(b, server_host_key_blob)) != 0 || 74d84d4014Sdjm (r = sshbuf_put_stringb(b, client_pub)) != 0 || 75d84d4014Sdjm (r = sshbuf_put_stringb(b, server_pub)) != 0 || 76d84d4014Sdjm (r = sshbuf_putb(b, shared_secret)) != 0) { 77d84d4014Sdjm sshbuf_free(b); 78d84d4014Sdjm return r; 79d84d4014Sdjm } 80d84d4014Sdjm #ifdef DEBUG_KEX 81d84d4014Sdjm sshbuf_dump(b, stderr); 82d84d4014Sdjm #endif 83d84d4014Sdjm if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { 84d84d4014Sdjm sshbuf_free(b); 85d84d4014Sdjm return SSH_ERR_LIBCRYPTO_ERROR; 86d84d4014Sdjm } 87d84d4014Sdjm sshbuf_free(b); 88d84d4014Sdjm *hashlen = ssh_digest_bytes(hash_alg); 89d84d4014Sdjm #ifdef DEBUG_KEX 90d84d4014Sdjm dump_digest("hash", hash, *hashlen); 91d84d4014Sdjm #endif 92d84d4014Sdjm return 0; 93d84d4014Sdjm } 94d84d4014Sdjm 95d84d4014Sdjm int 96d84d4014Sdjm kex_gen_client(struct ssh *ssh) 97d84d4014Sdjm { 98d84d4014Sdjm struct kex *kex = ssh->kex; 99d84d4014Sdjm int r; 100d84d4014Sdjm 101d84d4014Sdjm switch (kex->kex_type) { 1021f96526fSdjm #ifdef WITH_OPENSSL 103d84d4014Sdjm case KEX_DH_GRP1_SHA1: 104d84d4014Sdjm case KEX_DH_GRP14_SHA1: 105d84d4014Sdjm case KEX_DH_GRP14_SHA256: 106d84d4014Sdjm case KEX_DH_GRP16_SHA512: 107d84d4014Sdjm case KEX_DH_GRP18_SHA512: 108d84d4014Sdjm r = kex_dh_keypair(kex); 109d84d4014Sdjm break; 110d84d4014Sdjm case KEX_ECDH_SHA2: 111d84d4014Sdjm r = kex_ecdh_keypair(kex); 112d84d4014Sdjm break; 1131f96526fSdjm #endif /* WITH_OPENSSL */ 114d84d4014Sdjm case KEX_C25519_SHA256: 115d84d4014Sdjm r = kex_c25519_keypair(kex); 116d84d4014Sdjm break; 1173e284e19Sdjm case KEX_KEM_SNTRUP761X25519_SHA512: 1183e284e19Sdjm r = kex_kem_sntrup761x25519_keypair(kex); 119d84d4014Sdjm break; 1209dc26a4eSdjm case KEX_KEM_MLKEM768X25519_SHA256: 1219dc26a4eSdjm r = kex_kem_mlkem768x25519_keypair(kex); 1229dc26a4eSdjm break; 123d84d4014Sdjm default: 124d84d4014Sdjm r = SSH_ERR_INVALID_ARGUMENT; 125d84d4014Sdjm break; 126d84d4014Sdjm } 127d84d4014Sdjm if (r != 0) 128d84d4014Sdjm return r; 129d84d4014Sdjm if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || 130d84d4014Sdjm (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0 || 131d84d4014Sdjm (r = sshpkt_send(ssh)) != 0) 132d84d4014Sdjm return r; 133d84d4014Sdjm debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); 134d84d4014Sdjm ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_gen_reply); 135d84d4014Sdjm return 0; 136d84d4014Sdjm } 137d84d4014Sdjm 138d84d4014Sdjm static int 139d84d4014Sdjm input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh) 140d84d4014Sdjm { 141d84d4014Sdjm struct kex *kex = ssh->kex; 142d84d4014Sdjm struct sshkey *server_host_key = NULL; 143d84d4014Sdjm struct sshbuf *shared_secret = NULL; 144d84d4014Sdjm struct sshbuf *server_blob = NULL; 14521d29470Sdjm struct sshbuf *tmp = NULL, *server_host_key_blob = NULL; 14621d29470Sdjm u_char *signature = NULL; 147d84d4014Sdjm u_char hash[SSH_DIGEST_MAX_LENGTH]; 14821d29470Sdjm size_t slen, hashlen; 149d84d4014Sdjm int r; 150d84d4014Sdjm 1511c4876f5Sdjm debug("SSH2_MSG_KEX_ECDH_REPLY received"); 1521c4876f5Sdjm ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &kex_protocol_error); 1531c4876f5Sdjm 154d84d4014Sdjm /* hostkey */ 15521d29470Sdjm if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) 15621d29470Sdjm goto out; 15721d29470Sdjm /* sshkey_fromb() consumes its buffer, so make a copy */ 15821d29470Sdjm if ((tmp = sshbuf_fromb(server_host_key_blob)) == NULL) { 15921d29470Sdjm r = SSH_ERR_ALLOC_FAIL; 16021d29470Sdjm goto out; 16121d29470Sdjm } 16221d29470Sdjm if ((r = sshkey_fromb(tmp, &server_host_key)) != 0) 163d84d4014Sdjm goto out; 164d84d4014Sdjm if ((r = kex_verify_host_key(ssh, server_host_key)) != 0) 165d84d4014Sdjm goto out; 166d84d4014Sdjm 167d84d4014Sdjm /* Q_S, server public key */ 168d84d4014Sdjm /* signed H */ 169d84d4014Sdjm if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || 170d84d4014Sdjm (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || 171d84d4014Sdjm (r = sshpkt_get_end(ssh)) != 0) 172d84d4014Sdjm goto out; 173d84d4014Sdjm 174d84d4014Sdjm /* compute shared secret */ 175d84d4014Sdjm switch (kex->kex_type) { 1761f96526fSdjm #ifdef WITH_OPENSSL 177d84d4014Sdjm case KEX_DH_GRP1_SHA1: 178d84d4014Sdjm case KEX_DH_GRP14_SHA1: 179d84d4014Sdjm case KEX_DH_GRP14_SHA256: 180d84d4014Sdjm case KEX_DH_GRP16_SHA512: 181d84d4014Sdjm case KEX_DH_GRP18_SHA512: 182d84d4014Sdjm r = kex_dh_dec(kex, server_blob, &shared_secret); 183d84d4014Sdjm break; 184d84d4014Sdjm case KEX_ECDH_SHA2: 185d84d4014Sdjm r = kex_ecdh_dec(kex, server_blob, &shared_secret); 186d84d4014Sdjm break; 1871f96526fSdjm #endif /* WITH_OPENSSL */ 188d84d4014Sdjm case KEX_C25519_SHA256: 189d84d4014Sdjm r = kex_c25519_dec(kex, server_blob, &shared_secret); 190d84d4014Sdjm break; 1913e284e19Sdjm case KEX_KEM_SNTRUP761X25519_SHA512: 1923e284e19Sdjm r = kex_kem_sntrup761x25519_dec(kex, server_blob, 193d84d4014Sdjm &shared_secret); 194d84d4014Sdjm break; 1959dc26a4eSdjm case KEX_KEM_MLKEM768X25519_SHA256: 1969dc26a4eSdjm r = kex_kem_mlkem768x25519_dec(kex, server_blob, 1979dc26a4eSdjm &shared_secret); 1989dc26a4eSdjm break; 199d84d4014Sdjm default: 200d84d4014Sdjm r = SSH_ERR_INVALID_ARGUMENT; 201d84d4014Sdjm break; 202d84d4014Sdjm } 203d84d4014Sdjm if (r !=0 ) 204d84d4014Sdjm goto out; 205d84d4014Sdjm 206d84d4014Sdjm /* calc and verify H */ 207d84d4014Sdjm hashlen = sizeof(hash); 208d84d4014Sdjm if ((r = kex_gen_hash( 209d84d4014Sdjm kex->hash_alg, 210d84d4014Sdjm kex->client_version, 211d84d4014Sdjm kex->server_version, 21221d29470Sdjm kex->my, 21321d29470Sdjm kex->peer, 21421d29470Sdjm server_host_key_blob, 215d84d4014Sdjm kex->client_pub, 216d84d4014Sdjm server_blob, 217d84d4014Sdjm shared_secret, 218d84d4014Sdjm hash, &hashlen)) != 0) 219d84d4014Sdjm goto out; 220d84d4014Sdjm 221d84d4014Sdjm if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, 222493ad5b0Sdjm kex->hostkey_alg, ssh->compat, NULL)) != 0) 223d84d4014Sdjm goto out; 224d84d4014Sdjm 225b2c330ecSdjm if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || 226b2c330ecSdjm (r = kex_send_newkeys(ssh)) != 0) 227b2c330ecSdjm goto out; 228b2c330ecSdjm 229b2c330ecSdjm /* save initial signature and hostkey */ 230b2c330ecSdjm if ((kex->flags & KEX_INITIAL) != 0) { 231b2c330ecSdjm if (kex->initial_hostkey != NULL || kex->initial_sig != NULL) { 232b2c330ecSdjm r = SSH_ERR_INTERNAL_ERROR; 233b2c330ecSdjm goto out; 234b2c330ecSdjm } 235b2c330ecSdjm if ((kex->initial_sig = sshbuf_new()) == NULL) { 236b2c330ecSdjm r = SSH_ERR_ALLOC_FAIL; 237b2c330ecSdjm goto out; 238b2c330ecSdjm } 239b2c330ecSdjm if ((r = sshbuf_put(kex->initial_sig, signature, slen)) != 0) 240b2c330ecSdjm goto out; 241b2c330ecSdjm kex->initial_hostkey = server_host_key; 242b2c330ecSdjm server_host_key = NULL; 243b2c330ecSdjm } 244b2c330ecSdjm /* success */ 245d84d4014Sdjm out: 246d84d4014Sdjm explicit_bzero(hash, sizeof(hash)); 247d84d4014Sdjm explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); 2483e284e19Sdjm explicit_bzero(kex->sntrup761_client_key, 2493e284e19Sdjm sizeof(kex->sntrup761_client_key)); 2509dc26a4eSdjm explicit_bzero(kex->mlkem768_client_key, 2519dc26a4eSdjm sizeof(kex->mlkem768_client_key)); 25221d29470Sdjm sshbuf_free(server_host_key_blob); 253d84d4014Sdjm free(signature); 25421d29470Sdjm sshbuf_free(tmp); 255d84d4014Sdjm sshkey_free(server_host_key); 256d84d4014Sdjm sshbuf_free(server_blob); 257d84d4014Sdjm sshbuf_free(shared_secret); 258d84d4014Sdjm sshbuf_free(kex->client_pub); 259d84d4014Sdjm kex->client_pub = NULL; 260d84d4014Sdjm return r; 261d84d4014Sdjm } 262d84d4014Sdjm 263d84d4014Sdjm int 264d84d4014Sdjm kex_gen_server(struct ssh *ssh) 265d84d4014Sdjm { 266d84d4014Sdjm debug("expecting SSH2_MSG_KEX_ECDH_INIT"); 267d84d4014Sdjm ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &input_kex_gen_init); 268d84d4014Sdjm return 0; 269d84d4014Sdjm } 270d84d4014Sdjm 271d84d4014Sdjm static int 272d84d4014Sdjm input_kex_gen_init(int type, u_int32_t seq, struct ssh *ssh) 273d84d4014Sdjm { 274d84d4014Sdjm struct kex *kex = ssh->kex; 275d84d4014Sdjm struct sshkey *server_host_private, *server_host_public; 276d84d4014Sdjm struct sshbuf *shared_secret = NULL; 277d84d4014Sdjm struct sshbuf *server_pubkey = NULL; 278d84d4014Sdjm struct sshbuf *client_pubkey = NULL; 27921d29470Sdjm struct sshbuf *server_host_key_blob = NULL; 28021d29470Sdjm u_char *signature = NULL, hash[SSH_DIGEST_MAX_LENGTH]; 28121d29470Sdjm size_t slen, hashlen; 282d84d4014Sdjm int r; 283d84d4014Sdjm 2841c4876f5Sdjm debug("SSH2_MSG_KEX_ECDH_INIT received"); 2851c4876f5Sdjm ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &kex_protocol_error); 2861c4876f5Sdjm 287d84d4014Sdjm if ((r = kex_load_hostkey(ssh, &server_host_private, 288d84d4014Sdjm &server_host_public)) != 0) 289d84d4014Sdjm goto out; 290d84d4014Sdjm 291d84d4014Sdjm if ((r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 || 292d84d4014Sdjm (r = sshpkt_get_end(ssh)) != 0) 293d84d4014Sdjm goto out; 294d84d4014Sdjm 295d84d4014Sdjm /* compute shared secret */ 296d84d4014Sdjm switch (kex->kex_type) { 2971f96526fSdjm #ifdef WITH_OPENSSL 298d84d4014Sdjm case KEX_DH_GRP1_SHA1: 299d84d4014Sdjm case KEX_DH_GRP14_SHA1: 300d84d4014Sdjm case KEX_DH_GRP14_SHA256: 301d84d4014Sdjm case KEX_DH_GRP16_SHA512: 302d84d4014Sdjm case KEX_DH_GRP18_SHA512: 303d84d4014Sdjm r = kex_dh_enc(kex, client_pubkey, &server_pubkey, 304d84d4014Sdjm &shared_secret); 305d84d4014Sdjm break; 306d84d4014Sdjm case KEX_ECDH_SHA2: 307d84d4014Sdjm r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey, 308d84d4014Sdjm &shared_secret); 309d84d4014Sdjm break; 3101f96526fSdjm #endif /* WITH_OPENSSL */ 311d84d4014Sdjm case KEX_C25519_SHA256: 312d84d4014Sdjm r = kex_c25519_enc(kex, client_pubkey, &server_pubkey, 313d84d4014Sdjm &shared_secret); 314d84d4014Sdjm break; 3153e284e19Sdjm case KEX_KEM_SNTRUP761X25519_SHA512: 3163e284e19Sdjm r = kex_kem_sntrup761x25519_enc(kex, client_pubkey, 317d84d4014Sdjm &server_pubkey, &shared_secret); 318d84d4014Sdjm break; 3199dc26a4eSdjm case KEX_KEM_MLKEM768X25519_SHA256: 3209dc26a4eSdjm r = kex_kem_mlkem768x25519_enc(kex, client_pubkey, 3219dc26a4eSdjm &server_pubkey, &shared_secret); 3229dc26a4eSdjm break; 323d84d4014Sdjm default: 324d84d4014Sdjm r = SSH_ERR_INVALID_ARGUMENT; 325d84d4014Sdjm break; 326d84d4014Sdjm } 327d84d4014Sdjm if (r !=0 ) 328d84d4014Sdjm goto out; 329d84d4014Sdjm 330d84d4014Sdjm /* calc H */ 33121d29470Sdjm if ((server_host_key_blob = sshbuf_new()) == NULL) { 33221d29470Sdjm r = SSH_ERR_ALLOC_FAIL; 33321d29470Sdjm goto out; 33421d29470Sdjm } 33521d29470Sdjm if ((r = sshkey_putb(server_host_public, server_host_key_blob)) != 0) 336d84d4014Sdjm goto out; 337d84d4014Sdjm hashlen = sizeof(hash); 338d84d4014Sdjm if ((r = kex_gen_hash( 339d84d4014Sdjm kex->hash_alg, 340d84d4014Sdjm kex->client_version, 341d84d4014Sdjm kex->server_version, 34221d29470Sdjm kex->peer, 34321d29470Sdjm kex->my, 34421d29470Sdjm server_host_key_blob, 345d84d4014Sdjm client_pubkey, 346d84d4014Sdjm server_pubkey, 347d84d4014Sdjm shared_secret, 348d84d4014Sdjm hash, &hashlen)) != 0) 349d84d4014Sdjm goto out; 350d84d4014Sdjm 351d84d4014Sdjm /* sign H */ 352d84d4014Sdjm if ((r = kex->sign(ssh, server_host_private, server_host_public, 353d84d4014Sdjm &signature, &slen, hash, hashlen, kex->hostkey_alg)) != 0) 354d84d4014Sdjm goto out; 355d84d4014Sdjm 356d84d4014Sdjm /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ 357d84d4014Sdjm if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || 35821d29470Sdjm (r = sshpkt_put_stringb(ssh, server_host_key_blob)) != 0 || 359d84d4014Sdjm (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 || 360d84d4014Sdjm (r = sshpkt_put_string(ssh, signature, slen)) != 0 || 361d84d4014Sdjm (r = sshpkt_send(ssh)) != 0) 362d84d4014Sdjm goto out; 363d84d4014Sdjm 364b2c330ecSdjm if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || 365b2c330ecSdjm (r = kex_send_newkeys(ssh)) != 0) 366b2c330ecSdjm goto out; 367b2c330ecSdjm /* retain copy of hostkey used at initial KEX */ 368b2c330ecSdjm if (kex->initial_hostkey == NULL && 369b2c330ecSdjm (r = sshkey_from_private(server_host_public, 370b2c330ecSdjm &kex->initial_hostkey)) != 0) 371b2c330ecSdjm goto out; 372b2c330ecSdjm /* success */ 373d84d4014Sdjm out: 374d84d4014Sdjm explicit_bzero(hash, sizeof(hash)); 37521d29470Sdjm sshbuf_free(server_host_key_blob); 376d84d4014Sdjm free(signature); 377d84d4014Sdjm sshbuf_free(shared_secret); 378d84d4014Sdjm sshbuf_free(client_pubkey); 379d84d4014Sdjm sshbuf_free(server_pubkey); 380d84d4014Sdjm return r; 381d84d4014Sdjm } 382