Home | History | Annotate | Line # | Download | only in dist
      1 /*	$NetBSD: kexecdh.c,v 1.10 2026/04/08 18:58:40 christos Exp $	*/
      2 /* $OpenBSD: kexecdh.c,v 1.12 2026/02/14 00:18:34 jsg Exp $ */
      3 
      4 /*
      5  * Copyright (c) 2010 Damien Miller.  All rights reserved.
      6  * Copyright (c) 2019 Markus Friedl.  All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "includes.h"
     30 __RCSID("$NetBSD: kexecdh.c,v 1.10 2026/04/08 18:58:40 christos Exp $");
     31 #include <sys/types.h>
     32 
     33 #include <stdio.h>
     34 #include <signal.h>
     35 
     36 #include <openssl/bn.h>
     37 #include <openssl/ecdh.h>
     38 
     39 #include "sshkey.h"
     40 #include "kex.h"
     41 #include "sshbuf.h"
     42 #include "ssherr.h"
     43 
     44 static int
     45 kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key,
     46     const EC_GROUP *, struct sshbuf **);
     47 
     48 int
     49 kex_ecdh_keypair(struct kex *kex)
     50 {
     51 	EC_KEY *client_key = NULL;
     52 	const EC_GROUP *group;
     53 	const EC_POINT *public_key;
     54 	struct sshbuf *buf = NULL;
     55 	int r;
     56 
     57 	if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
     58 		r = SSH_ERR_ALLOC_FAIL;
     59 		goto out;
     60 	}
     61 	if (EC_KEY_generate_key(client_key) != 1) {
     62 		r = SSH_ERR_LIBCRYPTO_ERROR;
     63 		goto out;
     64 	}
     65 	group = EC_KEY_get0_group(client_key);
     66 	public_key = EC_KEY_get0_public_key(client_key);
     67 
     68 	if ((buf = sshbuf_new()) == NULL) {
     69 		r = SSH_ERR_ALLOC_FAIL;
     70 		goto out;
     71 	}
     72 	if ((r = sshbuf_put_ec(buf, public_key, group)) != 0 ||
     73 	    (r = sshbuf_get_u32(buf, NULL)) != 0)
     74 		goto out;
     75 #ifdef DEBUG_KEXECDH
     76 	fputs("client private key:\n", stderr);
     77 	sshkey_dump_ec_key(client_key);
     78 #endif
     79 	kex->ec_client_key = client_key;
     80 	kex->ec_group = group;
     81 	client_key = NULL;	/* owned by the kex */
     82 	kex->client_pub = buf;
     83 	buf = NULL;
     84  out:
     85 	EC_KEY_free(client_key);
     86 	sshbuf_free(buf);
     87 	return r;
     88 }
     89 
     90 int
     91 kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob,
     92     struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
     93 {
     94 	const EC_GROUP *group;
     95 	const EC_POINT *pub_key;
     96 	EC_KEY *server_key = NULL;
     97 	struct sshbuf *server_blob = NULL;
     98 	int r;
     99 
    100 	*server_blobp = NULL;
    101 	*shared_secretp = NULL;
    102 
    103 	if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
    104 		r = SSH_ERR_ALLOC_FAIL;
    105 		goto out;
    106 	}
    107 	if (EC_KEY_generate_key(server_key) != 1) {
    108 		r = SSH_ERR_LIBCRYPTO_ERROR;
    109 		goto out;
    110 	}
    111 	group = EC_KEY_get0_group(server_key);
    112 
    113 #ifdef DEBUG_KEXECDH
    114 	fputs("server private key:\n", stderr);
    115 	sshkey_dump_ec_key(server_key);
    116 #endif
    117 	pub_key = EC_KEY_get0_public_key(server_key);
    118 	if ((server_blob = sshbuf_new()) == NULL) {
    119 		r = SSH_ERR_ALLOC_FAIL;
    120 		goto out;
    121 	}
    122 	if ((r = sshbuf_put_ec(server_blob, pub_key, group)) != 0 ||
    123 	    (r = sshbuf_get_u32(server_blob, NULL)) != 0)
    124 		goto out;
    125 	if ((r = kex_ecdh_dec_key_group(kex, client_blob, server_key, group,
    126 	    shared_secretp)) != 0)
    127 		goto out;
    128 	*server_blobp = server_blob;
    129 	server_blob = NULL;
    130  out:
    131 	EC_KEY_free(server_key);
    132 	sshbuf_free(server_blob);
    133 	return r;
    134 }
    135 
    136 static int
    137 kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob,
    138     EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp)
    139 {
    140 	struct sshbuf *buf = NULL;
    141 	BIGNUM *shared_secret = NULL;
    142 	EC_POINT *dh_pub = NULL;
    143 	u_char *kbuf = NULL;
    144 	size_t klen = 0;
    145 	int r;
    146 
    147 	*shared_secretp = NULL;
    148 
    149 	if ((buf = sshbuf_new()) == NULL) {
    150 		r = SSH_ERR_ALLOC_FAIL;
    151 		goto out;
    152 	}
    153 	if ((r = sshbuf_put_stringb(buf, ec_blob)) != 0)
    154 		goto out;
    155 	if ((dh_pub = EC_POINT_new(group)) == NULL) {
    156 		r = SSH_ERR_ALLOC_FAIL;
    157 		goto out;
    158 	}
    159 	if ((r = sshbuf_get_ec(buf, dh_pub, group)) != 0) {
    160 		goto out;
    161 	}
    162 	sshbuf_reset(buf);
    163 
    164 #ifdef DEBUG_KEXECDH
    165 	fputs("public key:\n", stderr);
    166 	sshkey_dump_ec_point(group, dh_pub);
    167 #endif
    168 	if (sshkey_ec_validate_public(group, dh_pub) != 0) {
    169 		r = SSH_ERR_MESSAGE_INCOMPLETE;
    170 		goto out;
    171 	}
    172 	klen = (EC_GROUP_get_degree(group) + 7) / 8;
    173 	if ((kbuf = malloc(klen)) == NULL ||
    174 	    (shared_secret = BN_new()) == NULL) {
    175 		r = SSH_ERR_ALLOC_FAIL;
    176 		goto out;
    177 	}
    178 	if (ECDH_compute_key(kbuf, klen, dh_pub, key, NULL) != (int)klen ||
    179 	    BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
    180 		r = SSH_ERR_LIBCRYPTO_ERROR;
    181 		goto out;
    182 	}
    183 #ifdef DEBUG_KEXECDH
    184 	dump_digest("shared secret", kbuf, klen);
    185 #endif
    186 	if ((r = sshbuf_put_bignum2(buf, shared_secret)) != 0)
    187 		goto out;
    188 	*shared_secretp = buf;
    189 	buf = NULL;
    190  out:
    191 	EC_POINT_clear_free(dh_pub);
    192 	BN_clear_free(shared_secret);
    193 	freezero(kbuf, klen);
    194 	sshbuf_free(buf);
    195 	return r;
    196 }
    197 
    198 int
    199 kex_ecdh_dec(struct kex *kex, const struct sshbuf *server_blob,
    200     struct sshbuf **shared_secretp)
    201 {
    202 	int r;
    203 
    204 	r = kex_ecdh_dec_key_group(kex, server_blob, kex->ec_client_key,
    205 	    kex->ec_group, shared_secretp);
    206 	EC_KEY_free(kex->ec_client_key);
    207 	kex->ec_client_key = NULL;
    208 	return r;
    209 }
    210