1166124Srafan/* $OpenBSD: kexc25519s.c,v 1.10 2015/12/04 16:41:28 markus Exp $ */
250276Speter/*
3166124Srafan * Copyright (c) 2001 Markus Friedl.  All rights reserved.
450276Speter * Copyright (c) 2010 Damien Miller.  All rights reserved.
550276Speter * Copyright (c) 2013 Aris Adamantiadis.  All rights reserved.
650276Speter *
750276Speter * modification, are permitted provided that the following conditions
850276Speter * are met:
950276Speter * 1. Redistributions of source code must retain the above copyright
1050276Speter *    notice, this list of conditions and the following disclaimer.
1150276Speter * 2. Redistributions in binary form must reproduce the above copyright
1250276Speter *    notice, this list of conditions and the following disclaimer in the
1350276Speter *    documentation and/or other materials provided with the distribution.
1450276Speter *
1550276Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1650276Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1750276Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1850276Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1950276Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2050276Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2150276Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2250276Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2350276Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2450276Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2550276Speter */
2650276Speter
2750276Speter#include "includes.h"
2850276Speter
2950276Speter#include <sys/types.h>
30166124Srafan#include <stdio.h>
3150276Speter#include <string.h>
3250276Speter#include <signal.h>
3350276Speter
3450276Speter#include "sshkey.h"
3550276Speter#include "cipher.h"
3650276Speter#include "digest.h"
3750276Speter#include "kex.h"
3850276Speter#include "log.h"
39166124Srafan#include "packet.h"
4050276Speter#include "ssh2.h"
4150276Speter#include "sshbuf.h"
4250276Speter#include "ssherr.h"
43166124Srafan
44166124Srafanstatic int input_kex_c25519_init(int, u_int32_t, void *);
45166124Srafan
46166124Srafanint
4750276Speterkexc25519_server(struct ssh *ssh)
4850276Speter{
4950276Speter	debug("expecting SSH2_MSG_KEX_ECDH_INIT");
5050276Speter	ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &input_kex_c25519_init);
5150276Speter	return 0;
5250276Speter}
5350276Speter
5450276Speterstatic int
5550276Speterinput_kex_c25519_init(int type, u_int32_t seq, void *ctxt)
5650276Speter{
5750276Speter	struct ssh *ssh = ctxt;
5850276Speter	struct kex *kex = ssh->kex;
5950276Speter	struct sshkey *server_host_private, *server_host_public;
6050276Speter	struct sshbuf *shared_secret = NULL;
6150276Speter	u_char *server_host_key_blob = NULL, *signature = NULL;
6250276Speter	u_char server_key[CURVE25519_SIZE];
6350276Speter	u_char *client_pubkey = NULL;
6450276Speter	u_char server_pubkey[CURVE25519_SIZE];
6550276Speter	u_char hash[SSH_DIGEST_MAX_LENGTH];
6650276Speter	size_t slen, pklen, sbloblen, hashlen;
6750276Speter	int r;
68166124Srafan
69166124Srafan	/* generate private key */
70166124Srafan	kexc25519_keygen(server_key, server_pubkey);
71166124Srafan#ifdef DEBUG_KEXECDH
72166124Srafan	dump_digest("server private key:", server_key, sizeof(server_key));
73166124Srafan#endif
74166124Srafan	if (kex->load_host_public_key == NULL ||
75166124Srafan	    kex->load_host_private_key == NULL) {
7650276Speter		r = SSH_ERR_INVALID_ARGUMENT;
7750276Speter		goto out;
7850276Speter	}
7950276Speter	server_host_public = kex->load_host_public_key(kex->hostkey_type,
8050276Speter	    kex->hostkey_nid, ssh);
8162449Speter	server_host_private = kex->load_host_private_key(kex->hostkey_type,
8262449Speter	    kex->hostkey_nid, ssh);
8362449Speter	if (server_host_public == NULL) {
8462449Speter		r = SSH_ERR_NO_HOSTKEY_LOADED;
8562449Speter		goto out;
8662449Speter	}
8762449Speter
8862449Speter	if ((r = sshpkt_get_string(ssh, &client_pubkey, &pklen)) != 0 ||
8950276Speter	    (r = sshpkt_get_end(ssh)) != 0)
9050276Speter		goto out;
9150276Speter	if (pklen != CURVE25519_SIZE) {
9250276Speter		r = SSH_ERR_SIGNATURE_INVALID;
9350276Speter		goto out;
9450276Speter	}
9550276Speter#ifdef DEBUG_KEXECDH
9650276Speter	dump_digest("client public key:", client_pubkey, CURVE25519_SIZE);
97166124Srafan#endif
9850276Speter
9950276Speter	if ((shared_secret = sshbuf_new()) == NULL) {
10050276Speter		r = SSH_ERR_ALLOC_FAIL;
101166124Srafan		goto out;
10250276Speter	}
103166124Srafan	if ((r = kexc25519_shared_key(server_key, client_pubkey,
10450276Speter	    shared_secret)) < 0)
10550276Speter		goto out;
10650276Speter
10750276Speter	/* calc H */
10850276Speter	if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
10950276Speter	    &sbloblen)) != 0)
11050276Speter		goto out;
11150276Speter	hashlen = sizeof(hash);
112166124Srafan	if ((r = kex_c25519_hash(
113166124Srafan	    kex->hash_alg,
114166124Srafan	    kex->client_version_string,
115166124Srafan	    kex->server_version_string,
116166124Srafan	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
117166124Srafan	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
118166124Srafan	    server_host_key_blob, sbloblen,
119166124Srafan	    client_pubkey,
120166124Srafan	    server_pubkey,
121166124Srafan	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
122166124Srafan	    hash, &hashlen)) < 0)
123166124Srafan		goto out;
124166124Srafan
125166124Srafan	/* save session id := H */
12650276Speter	if (kex->session_id == NULL) {
12750276Speter		kex->session_id_len = hashlen;
12850276Speter		kex->session_id = malloc(kex->session_id_len);
12950276Speter		if (kex->session_id == NULL) {
130166124Srafan			r = SSH_ERR_ALLOC_FAIL;
131166124Srafan			goto out;
132166124Srafan		}
133166124Srafan		memcpy(kex->session_id, hash, kex->session_id_len);
134166124Srafan	}
135166124Srafan
136166124Srafan	/* sign H */
13750276Speter	if ((r = kex->sign(server_host_private, server_host_public, &signature,
13850276Speter	     &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0)
13950276Speter		goto out;
14050276Speter
14150276Speter	/* send server hostkey, ECDH pubkey 'Q_S' and signed H */
14250276Speter	if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 ||
14350276Speter	    (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
14450276Speter	    (r = sshpkt_put_string(ssh, server_pubkey, sizeof(server_pubkey))) != 0 ||
14550276Speter	    (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
14650276Speter	    (r = sshpkt_send(ssh)) != 0)
147		goto out;
148
149	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
150		r = kex_send_newkeys(ssh);
151out:
152	explicit_bzero(hash, sizeof(hash));
153	explicit_bzero(server_key, sizeof(server_key));
154	free(server_host_key_blob);
155	free(signature);
156	free(client_pubkey);
157	sshbuf_free(shared_secret);
158	return r;
159}
160