1*87445a97Sdjm /* $OpenBSD: kexecdh.c,v 1.10 2019/01/21 10:40:11 djm Exp $ */
2f6c05033Sdjm /*
3f6c05033Sdjm * Copyright (c) 2010 Damien Miller. All rights reserved.
432573a67Sdjm * Copyright (c) 2019 Markus Friedl. All rights reserved.
5f6c05033Sdjm *
6f6c05033Sdjm * Redistribution and use in source and binary forms, with or without
7f6c05033Sdjm * modification, are permitted provided that the following conditions
8f6c05033Sdjm * are met:
9f6c05033Sdjm * 1. Redistributions of source code must retain the above copyright
10f6c05033Sdjm * notice, this list of conditions and the following disclaimer.
11f6c05033Sdjm * 2. Redistributions in binary form must reproduce the above copyright
12f6c05033Sdjm * notice, this list of conditions and the following disclaimer in the
13f6c05033Sdjm * documentation and/or other materials provided with the distribution.
14f6c05033Sdjm *
15f6c05033Sdjm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16f6c05033Sdjm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17f6c05033Sdjm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18f6c05033Sdjm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19f6c05033Sdjm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20f6c05033Sdjm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21f6c05033Sdjm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22f6c05033Sdjm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23f6c05033Sdjm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24f6c05033Sdjm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25f6c05033Sdjm */
26f6c05033Sdjm
27f6c05033Sdjm #include <sys/types.h>
28f6c05033Sdjm
2932573a67Sdjm #include <stdio.h>
30f6c05033Sdjm #include <string.h>
3132573a67Sdjm #include <signal.h>
32f6c05033Sdjm
33f6c05033Sdjm #include <openssl/ecdh.h>
34f6c05033Sdjm
3513202d0aSmarkus #include "sshkey.h"
36f6c05033Sdjm #include "kex.h"
3713202d0aSmarkus #include "sshbuf.h"
388df5df93Sdjm #include "digest.h"
3913202d0aSmarkus #include "ssherr.h"
40f6c05033Sdjm
4132573a67Sdjm static int
42b115be19Sdjm kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key,
4332573a67Sdjm const EC_GROUP *, struct sshbuf **);
4432573a67Sdjm
4513202d0aSmarkus int
kex_ecdh_keypair(struct kex * kex)4632573a67Sdjm kex_ecdh_keypair(struct kex *kex)
47f6c05033Sdjm {
4832573a67Sdjm EC_KEY *client_key = NULL;
4932573a67Sdjm const EC_GROUP *group;
5032573a67Sdjm const EC_POINT *public_key;
5132573a67Sdjm struct sshbuf *buf = NULL;
5213202d0aSmarkus int r;
53f6c05033Sdjm
5432573a67Sdjm if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
5532573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
5632573a67Sdjm goto out;
5732573a67Sdjm }
5832573a67Sdjm if (EC_KEY_generate_key(client_key) != 1) {
5932573a67Sdjm r = SSH_ERR_LIBCRYPTO_ERROR;
6032573a67Sdjm goto out;
6132573a67Sdjm }
6232573a67Sdjm group = EC_KEY_get0_group(client_key);
6332573a67Sdjm public_key = EC_KEY_get0_public_key(client_key);
6432573a67Sdjm
6532573a67Sdjm if ((buf = sshbuf_new()) == NULL) {
6632573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
6732573a67Sdjm goto out;
6832573a67Sdjm }
6932573a67Sdjm if ((r = sshbuf_put_ec(buf, public_key, group)) != 0 ||
7032573a67Sdjm (r = sshbuf_get_u32(buf, NULL)) != 0)
7132573a67Sdjm goto out;
7232573a67Sdjm #ifdef DEBUG_KEXECDH
7332573a67Sdjm fputs("client private key:\n", stderr);
7432573a67Sdjm sshkey_dump_ec_key(client_key);
7532573a67Sdjm #endif
7632573a67Sdjm kex->ec_client_key = client_key;
7732573a67Sdjm kex->ec_group = group;
7832573a67Sdjm client_key = NULL; /* owned by the kex */
79*87445a97Sdjm kex->client_pub = buf;
8032573a67Sdjm buf = NULL;
8132573a67Sdjm out:
8232573a67Sdjm EC_KEY_free(client_key);
8332573a67Sdjm sshbuf_free(buf);
8413202d0aSmarkus return r;
8513202d0aSmarkus }
8632573a67Sdjm
8732573a67Sdjm int
kex_ecdh_enc(struct kex * kex,const struct sshbuf * client_blob,struct sshbuf ** server_blobp,struct sshbuf ** shared_secretp)88b115be19Sdjm kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob,
8932573a67Sdjm struct sshbuf **server_blobp, struct sshbuf **shared_secretp)
9032573a67Sdjm {
9132573a67Sdjm const EC_GROUP *group;
9232573a67Sdjm const EC_POINT *pub_key;
9332573a67Sdjm EC_KEY *server_key = NULL;
9432573a67Sdjm struct sshbuf *server_blob = NULL;
9532573a67Sdjm int r;
9632573a67Sdjm
9732573a67Sdjm *server_blobp = NULL;
9832573a67Sdjm *shared_secretp = NULL;
9932573a67Sdjm
10032573a67Sdjm if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
10132573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
10232573a67Sdjm goto out;
10313202d0aSmarkus }
10432573a67Sdjm if (EC_KEY_generate_key(server_key) != 1) {
10532573a67Sdjm r = SSH_ERR_LIBCRYPTO_ERROR;
10632573a67Sdjm goto out;
10732573a67Sdjm }
10832573a67Sdjm group = EC_KEY_get0_group(server_key);
10932573a67Sdjm
11032573a67Sdjm #ifdef DEBUG_KEXECDH
11132573a67Sdjm fputs("server private key:\n", stderr);
11232573a67Sdjm sshkey_dump_ec_key(server_key);
11313202d0aSmarkus #endif
11432573a67Sdjm pub_key = EC_KEY_get0_public_key(server_key);
11532573a67Sdjm if ((server_blob = sshbuf_new()) == NULL) {
11632573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
11732573a67Sdjm goto out;
11832573a67Sdjm }
11932573a67Sdjm if ((r = sshbuf_put_ec(server_blob, pub_key, group)) != 0 ||
12032573a67Sdjm (r = sshbuf_get_u32(server_blob, NULL)) != 0)
12132573a67Sdjm goto out;
122b115be19Sdjm if ((r = kex_ecdh_dec_key_group(kex, client_blob, server_key, group,
12332573a67Sdjm shared_secretp)) != 0)
12432573a67Sdjm goto out;
12532573a67Sdjm *server_blobp = server_blob;
12632573a67Sdjm server_blob = NULL;
12732573a67Sdjm out:
12832573a67Sdjm EC_KEY_free(server_key);
12932573a67Sdjm sshbuf_free(server_blob);
13032573a67Sdjm return r;
13132573a67Sdjm }
13232573a67Sdjm
13332573a67Sdjm static 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)134b115be19Sdjm kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob,
13532573a67Sdjm EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp)
13632573a67Sdjm {
13732573a67Sdjm struct sshbuf *buf = NULL;
13832573a67Sdjm BIGNUM *shared_secret = NULL;
13932573a67Sdjm EC_POINT *dh_pub = NULL;
14032573a67Sdjm u_char *kbuf = NULL;
14132573a67Sdjm size_t klen = 0;
14232573a67Sdjm int r;
14332573a67Sdjm
14432573a67Sdjm *shared_secretp = NULL;
14532573a67Sdjm
14632573a67Sdjm if ((buf = sshbuf_new()) == NULL) {
14732573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
14832573a67Sdjm goto out;
14932573a67Sdjm }
150b115be19Sdjm if ((r = sshbuf_put_stringb(buf, ec_blob)) != 0)
15132573a67Sdjm goto out;
15232573a67Sdjm if ((dh_pub = EC_POINT_new(group)) == NULL) {
15332573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
15432573a67Sdjm goto out;
15532573a67Sdjm }
15632573a67Sdjm if ((r = sshbuf_get_ec(buf, dh_pub, group)) != 0) {
15732573a67Sdjm goto out;
15832573a67Sdjm }
15932573a67Sdjm sshbuf_reset(buf);
16032573a67Sdjm
16132573a67Sdjm #ifdef DEBUG_KEXECDH
16232573a67Sdjm fputs("public key:\n", stderr);
16332573a67Sdjm sshkey_dump_ec_point(group, dh_pub);
16432573a67Sdjm #endif
16532573a67Sdjm if (sshkey_ec_validate_public(group, dh_pub) != 0) {
16632573a67Sdjm r = SSH_ERR_MESSAGE_INCOMPLETE;
16732573a67Sdjm goto out;
16832573a67Sdjm }
16932573a67Sdjm klen = (EC_GROUP_get_degree(group) + 7) / 8;
17032573a67Sdjm if ((kbuf = malloc(klen)) == NULL ||
17132573a67Sdjm (shared_secret = BN_new()) == NULL) {
17232573a67Sdjm r = SSH_ERR_ALLOC_FAIL;
17332573a67Sdjm goto out;
17432573a67Sdjm }
17532573a67Sdjm if (ECDH_compute_key(kbuf, klen, dh_pub, key, NULL) != (int)klen ||
17632573a67Sdjm BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
17732573a67Sdjm r = SSH_ERR_LIBCRYPTO_ERROR;
17832573a67Sdjm goto out;
17932573a67Sdjm }
18032573a67Sdjm #ifdef DEBUG_KEXECDH
18132573a67Sdjm dump_digest("shared secret", kbuf, klen);
18232573a67Sdjm #endif
18332573a67Sdjm if ((r = sshbuf_put_bignum2(buf, shared_secret)) != 0)
18432573a67Sdjm goto out;
18532573a67Sdjm *shared_secretp = buf;
18632573a67Sdjm buf = NULL;
18732573a67Sdjm out:
18832573a67Sdjm EC_POINT_clear_free(dh_pub);
18932573a67Sdjm BN_clear_free(shared_secret);
19032573a67Sdjm freezero(kbuf, klen);
19132573a67Sdjm sshbuf_free(buf);
19232573a67Sdjm return r;
19332573a67Sdjm }
19432573a67Sdjm
19532573a67Sdjm int
kex_ecdh_dec(struct kex * kex,const struct sshbuf * server_blob,struct sshbuf ** shared_secretp)196b115be19Sdjm kex_ecdh_dec(struct kex *kex, const struct sshbuf *server_blob,
19732573a67Sdjm struct sshbuf **shared_secretp)
19832573a67Sdjm {
19932573a67Sdjm int r;
20032573a67Sdjm
201b115be19Sdjm r = kex_ecdh_dec_key_group(kex, server_blob, kex->ec_client_key,
20232573a67Sdjm kex->ec_group, shared_secretp);
20332573a67Sdjm EC_KEY_free(kex->ec_client_key);
20432573a67Sdjm kex->ec_client_key = NULL;
20532573a67Sdjm return r;
206f6c05033Sdjm }
207