1/* $NetBSD: kexecdh.c,v 1.8 2019/04/20 17:16:40 christos Exp $ */ 2/* $OpenBSD: kexecdh.c,v 1.10 2019/01/21 10:40:11 djm Exp $ */ 3/* 4 * Copyright (c) 2010 Damien Miller. All rights reserved. 5 * Copyright (c) 2019 Markus Friedl. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "includes.h" 29__RCSID("$NetBSD: kexecdh.c,v 1.8 2019/04/20 17:16:40 christos Exp $"); 30#include <sys/types.h> 31 32#include <stdio.h> 33#include <string.h> 34#include <signal.h> 35 36#include <openssl/ecdh.h> 37 38#include "sshkey.h" 39#include "kex.h" 40#include "sshbuf.h" 41#include "digest.h" 42#include "ssherr.h" 43 44static int 45kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key, 46 const EC_GROUP *, struct sshbuf **); 47 48int 49kex_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 90int 91kex_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 136static int 137kex_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 198int 199kex_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