1/* $OpenBSD: kexsntrup761x25519.c,v 1.2 2021/12/05 12:28:27 jsg Exp $ */
2/*
3 * Copyright (c) 2019 Markus Friedl.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
27
28#ifdef USE_SNTRUP761X25519
29
30#include <sys/types.h>
31
32#include <stdio.h>
33#include <string.h>
34#include <signal.h>
35
36#include "sshkey.h"
37#include "kex.h"
38#include "sshbuf.h"
39#include "digest.h"
40#include "ssherr.h"
41
42int
43kex_kem_sntrup761x25519_keypair(struct kex *kex)
44{
45	struct sshbuf *buf = NULL;
46	u_char *cp = NULL;
47	size_t need;
48	int r;
49
50	if ((buf = sshbuf_new()) == NULL)
51		return SSH_ERR_ALLOC_FAIL;
52	need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE;
53	if ((r = sshbuf_reserve(buf, need, &cp)) != 0)
54		goto out;
55	crypto_kem_sntrup761_keypair(cp, kex->sntrup761_client_key);
56#ifdef DEBUG_KEXECDH
57	dump_digest("client public key sntrup761:", cp,
58	    crypto_kem_sntrup761_PUBLICKEYBYTES);
59#endif
60	cp += crypto_kem_sntrup761_PUBLICKEYBYTES;
61	kexc25519_keygen(kex->c25519_client_key, cp);
62#ifdef DEBUG_KEXECDH
63	dump_digest("client public key c25519:", cp, CURVE25519_SIZE);
64#endif
65	kex->client_pub = buf;
66	buf = NULL;
67 out:
68	sshbuf_free(buf);
69	return r;
70}
71
72int
73kex_kem_sntrup761x25519_enc(struct kex *kex,
74   const struct sshbuf *client_blob, struct sshbuf **server_blobp,
75   struct sshbuf **shared_secretp)
76{
77	struct sshbuf *server_blob = NULL;
78	struct sshbuf *buf = NULL;
79	const u_char *client_pub;
80	u_char *kem_key, *ciphertext, *server_pub;
81	u_char server_key[CURVE25519_SIZE];
82	u_char hash[SSH_DIGEST_MAX_LENGTH];
83	size_t need;
84	int r;
85
86	*server_blobp = NULL;
87	*shared_secretp = NULL;
88
89	/* client_blob contains both KEM and ECDH client pubkeys */
90	need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE;
91	if (sshbuf_len(client_blob) != need) {
92		r = SSH_ERR_SIGNATURE_INVALID;
93		goto out;
94	}
95	client_pub = sshbuf_ptr(client_blob);
96#ifdef DEBUG_KEXECDH
97	dump_digest("client public key sntrup761:", client_pub,
98	    crypto_kem_sntrup761_PUBLICKEYBYTES);
99	dump_digest("client public key 25519:",
100	    client_pub + crypto_kem_sntrup761_PUBLICKEYBYTES,
101	    CURVE25519_SIZE);
102#endif
103	/* allocate buffer for concatenation of KEM key and ECDH shared key */
104	/* the buffer will be hashed and the result is the shared secret */
105	if ((buf = sshbuf_new()) == NULL) {
106		r = SSH_ERR_ALLOC_FAIL;
107		goto out;
108	}
109	if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES,
110	    &kem_key)) != 0)
111		goto out;
112	/* allocate space for encrypted KEM key and ECDH pub key */
113	if ((server_blob = sshbuf_new()) == NULL) {
114		r = SSH_ERR_ALLOC_FAIL;
115		goto out;
116	}
117	need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE;
118	if ((r = sshbuf_reserve(server_blob, need, &ciphertext)) != 0)
119		goto out;
120	/* generate and encrypt KEM key with client key */
121	crypto_kem_sntrup761_enc(ciphertext, kem_key, client_pub);
122	/* generate ECDH key pair, store server pubkey after ciphertext */
123	server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES;
124	kexc25519_keygen(server_key, server_pub);
125	/* append ECDH shared key */
126	client_pub += crypto_kem_sntrup761_PUBLICKEYBYTES;
127	if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0)
128		goto out;
129	if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
130		goto out;
131#ifdef DEBUG_KEXECDH
132	dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE);
133	dump_digest("server cipher text:", ciphertext,
134	    crypto_kem_sntrup761_CIPHERTEXTBYTES);
135	dump_digest("server kem key:", kem_key, crypto_kem_sntrup761_BYTES);
136	dump_digest("concatenation of KEM key and ECDH shared key:",
137	    sshbuf_ptr(buf), sshbuf_len(buf));
138#endif
139	/* string-encoded hash is resulting shared secret */
140	sshbuf_reset(buf);
141	if ((r = sshbuf_put_string(buf, hash,
142	    ssh_digest_bytes(kex->hash_alg))) != 0)
143		goto out;
144#ifdef DEBUG_KEXECDH
145	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
146#endif
147	*server_blobp = server_blob;
148	*shared_secretp = buf;
149	server_blob = NULL;
150	buf = NULL;
151 out:
152	explicit_bzero(hash, sizeof(hash));
153	explicit_bzero(server_key, sizeof(server_key));
154	sshbuf_free(server_blob);
155	sshbuf_free(buf);
156	return r;
157}
158
159int
160kex_kem_sntrup761x25519_dec(struct kex *kex,
161    const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
162{
163	struct sshbuf *buf = NULL;
164	u_char *kem_key = NULL;
165	const u_char *ciphertext, *server_pub;
166	u_char hash[SSH_DIGEST_MAX_LENGTH];
167	size_t need;
168	int r, decoded;
169
170	*shared_secretp = NULL;
171
172	need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE;
173	if (sshbuf_len(server_blob) != need) {
174		r = SSH_ERR_SIGNATURE_INVALID;
175		goto out;
176	}
177	ciphertext = sshbuf_ptr(server_blob);
178	server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES;
179#ifdef DEBUG_KEXECDH
180	dump_digest("server cipher text:", ciphertext,
181	    crypto_kem_sntrup761_CIPHERTEXTBYTES);
182	dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE);
183#endif
184	/* hash concatenation of KEM key and ECDH shared key */
185	if ((buf = sshbuf_new()) == NULL) {
186		r = SSH_ERR_ALLOC_FAIL;
187		goto out;
188	}
189	if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES,
190	    &kem_key)) != 0)
191		goto out;
192	decoded = crypto_kem_sntrup761_dec(kem_key, ciphertext,
193	    kex->sntrup761_client_key);
194	if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub,
195	    buf, 1)) < 0)
196		goto out;
197	if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0)
198		goto out;
199#ifdef DEBUG_KEXECDH
200	dump_digest("client kem key:", kem_key, crypto_kem_sntrup761_BYTES);
201	dump_digest("concatenation of KEM key and ECDH shared key:",
202	    sshbuf_ptr(buf), sshbuf_len(buf));
203#endif
204	sshbuf_reset(buf);
205	if ((r = sshbuf_put_string(buf, hash,
206	    ssh_digest_bytes(kex->hash_alg))) != 0)
207		goto out;
208#ifdef DEBUG_KEXECDH
209	dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf));
210#endif
211	if (decoded != 0) {
212		r = SSH_ERR_SIGNATURE_INVALID;
213		goto out;
214	}
215	*shared_secretp = buf;
216	buf = NULL;
217 out:
218	explicit_bzero(hash, sizeof(hash));
219	sshbuf_free(buf);
220	return r;
221}
222
223#else
224
225#include "ssherr.h"
226
227struct kex;
228struct sshbuf;
229struct sshkey;
230
231int
232kex_kem_sntrup761x25519_keypair(struct kex *kex)
233{
234	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
235}
236
237int
238kex_kem_sntrup761x25519_enc(struct kex *kex,
239   const struct sshbuf *client_blob, struct sshbuf **server_blobp,
240   struct sshbuf **shared_secretp)
241{
242	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
243}
244
245int
246kex_kem_sntrup761x25519_dec(struct kex *kex,
247    const struct sshbuf *server_blob, struct sshbuf **shared_secretp)
248{
249	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
250}
251#endif /* USE_SNTRUP761X25519 */
252