1/* $OpenBSD: ssh-ed25519.c,v 1.19 2022/10/28 00:44:44 djm Exp $ */
2/*
3 * Copyright (c) 2013 Markus Friedl <markus@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#define SSHKEY_INTERNAL
18#include <sys/types.h>
19#include <limits.h>
20
21#include "crypto_api.h"
22
23#include <string.h>
24#include <stdarg.h>
25
26#include "log.h"
27#include "sshbuf.h"
28#include "sshkey.h"
29#include "ssherr.h"
30#include "ssh.h"
31
32static void
33ssh_ed25519_cleanup(struct sshkey *k)
34{
35	freezero(k->ed25519_pk, ED25519_PK_SZ);
36	freezero(k->ed25519_sk, ED25519_SK_SZ);
37	k->ed25519_pk = NULL;
38	k->ed25519_sk = NULL;
39}
40
41static int
42ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b)
43{
44	if (a->ed25519_pk == NULL || b->ed25519_pk == NULL)
45		return 0;
46	if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0)
47		return 0;
48	return 1;
49}
50
51static int
52ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b,
53    enum sshkey_serialize_rep opts)
54{
55	int r;
56
57	if (key->ed25519_pk == NULL)
58		return SSH_ERR_INVALID_ARGUMENT;
59	if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0)
60		return r;
61
62	return 0;
63}
64
65static int
66ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b,
67    enum sshkey_serialize_rep opts)
68{
69	int r;
70
71	if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 ||
72	    (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0)
73		return r;
74
75	return 0;
76}
77
78static int
79ssh_ed25519_generate(struct sshkey *k, int bits)
80{
81	if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
82	    (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL)
83		return SSH_ERR_ALLOC_FAIL;
84	crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
85	return 0;
86}
87
88static int
89ssh_ed25519_copy_public(const struct sshkey *from, struct sshkey *to)
90{
91	if (from->ed25519_pk == NULL)
92		return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */
93	if ((to->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL)
94		return SSH_ERR_ALLOC_FAIL;
95	memcpy(to->ed25519_pk, from->ed25519_pk, ED25519_PK_SZ);
96	return 0;
97}
98
99static int
100ssh_ed25519_deserialize_public(const char *ktype, struct sshbuf *b,
101    struct sshkey *key)
102{
103	u_char *pk = NULL;
104	size_t len = 0;
105	int r;
106
107	if ((r = sshbuf_get_string(b, &pk, &len)) != 0)
108		return r;
109	if (len != ED25519_PK_SZ) {
110		freezero(pk, len);
111		return SSH_ERR_INVALID_FORMAT;
112	}
113	key->ed25519_pk = pk;
114	return 0;
115}
116
117static int
118ssh_ed25519_deserialize_private(const char *ktype, struct sshbuf *b,
119    struct sshkey *key)
120{
121	int r;
122	size_t sklen = 0;
123	u_char *ed25519_sk = NULL;
124
125	if ((r = ssh_ed25519_deserialize_public(NULL, b, key)) != 0)
126		goto out;
127	if ((r = sshbuf_get_string(b, &ed25519_sk, &sklen)) != 0)
128		goto out;
129	if (sklen != ED25519_SK_SZ) {
130		r = SSH_ERR_INVALID_FORMAT;
131		goto out;
132	}
133	key->ed25519_sk = ed25519_sk;
134	ed25519_sk = NULL; /* transferred */
135	/* success */
136	r = 0;
137 out:
138	freezero(ed25519_sk, sklen);
139	return r;
140}
141
142static int
143ssh_ed25519_sign(struct sshkey *key,
144    u_char **sigp, size_t *lenp,
145    const u_char *data, size_t datalen,
146    const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
147{
148	u_char *sig = NULL;
149	size_t slen = 0, len;
150	unsigned long long smlen;
151	int r, ret;
152	struct sshbuf *b = NULL;
153
154	if (lenp != NULL)
155		*lenp = 0;
156	if (sigp != NULL)
157		*sigp = NULL;
158
159	if (key == NULL ||
160	    sshkey_type_plain(key->type) != KEY_ED25519 ||
161	    key->ed25519_sk == NULL ||
162	    datalen >= INT_MAX - crypto_sign_ed25519_BYTES)
163		return SSH_ERR_INVALID_ARGUMENT;
164	smlen = slen = datalen + crypto_sign_ed25519_BYTES;
165	if ((sig = malloc(slen)) == NULL)
166		return SSH_ERR_ALLOC_FAIL;
167
168	if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen,
169	    key->ed25519_sk)) != 0 || smlen <= datalen) {
170		r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
171		goto out;
172	}
173	/* encode signature */
174	if ((b = sshbuf_new()) == NULL) {
175		r = SSH_ERR_ALLOC_FAIL;
176		goto out;
177	}
178	if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 ||
179	    (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0)
180		goto out;
181	len = sshbuf_len(b);
182	if (sigp != NULL) {
183		if ((*sigp = malloc(len)) == NULL) {
184			r = SSH_ERR_ALLOC_FAIL;
185			goto out;
186		}
187		memcpy(*sigp, sshbuf_ptr(b), len);
188	}
189	if (lenp != NULL)
190		*lenp = len;
191	/* success */
192	r = 0;
193 out:
194	sshbuf_free(b);
195	if (sig != NULL)
196		freezero(sig, slen);
197
198	return r;
199}
200
201static int
202ssh_ed25519_verify(const struct sshkey *key,
203    const u_char *sig, size_t siglen,
204    const u_char *data, size_t dlen, const char *alg, u_int compat,
205    struct sshkey_sig_details **detailsp)
206{
207	struct sshbuf *b = NULL;
208	char *ktype = NULL;
209	const u_char *sigblob;
210	u_char *sm = NULL, *m = NULL;
211	size_t len;
212	unsigned long long smlen = 0, mlen = 0;
213	int r, ret;
214
215	if (key == NULL ||
216	    sshkey_type_plain(key->type) != KEY_ED25519 ||
217	    key->ed25519_pk == NULL ||
218	    dlen >= INT_MAX - crypto_sign_ed25519_BYTES ||
219	    sig == NULL || siglen == 0)
220		return SSH_ERR_INVALID_ARGUMENT;
221
222	if ((b = sshbuf_from(sig, siglen)) == NULL)
223		return SSH_ERR_ALLOC_FAIL;
224	if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
225	    (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
226		goto out;
227	if (strcmp("ssh-ed25519", ktype) != 0) {
228		r = SSH_ERR_KEY_TYPE_MISMATCH;
229		goto out;
230	}
231	if (sshbuf_len(b) != 0) {
232		r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
233		goto out;
234	}
235	if (len > crypto_sign_ed25519_BYTES) {
236		r = SSH_ERR_INVALID_FORMAT;
237		goto out;
238	}
239	if (dlen >= SIZE_MAX - len) {
240		r = SSH_ERR_INVALID_ARGUMENT;
241		goto out;
242	}
243	smlen = len + dlen;
244	mlen = smlen;
245	if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
246		r = SSH_ERR_ALLOC_FAIL;
247		goto out;
248	}
249	memcpy(sm, sigblob, len);
250	memcpy(sm+len, data, dlen);
251	if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
252	    key->ed25519_pk)) != 0) {
253		debug2_f("crypto_sign_ed25519_open failed: %d", ret);
254	}
255	if (ret != 0 || mlen != dlen) {
256		r = SSH_ERR_SIGNATURE_INVALID;
257		goto out;
258	}
259	/* XXX compare 'm' and 'data' ? */
260	/* success */
261	r = 0;
262 out:
263	if (sm != NULL)
264		freezero(sm, smlen);
265	if (m != NULL)
266		freezero(m, smlen); /* NB mlen may be invalid if r != 0 */
267	sshbuf_free(b);
268	free(ktype);
269	return r;
270}
271
272/* NB. not static; used by ED25519-SK */
273const struct sshkey_impl_funcs sshkey_ed25519_funcs = {
274	/* .size = */		NULL,
275	/* .alloc = */		NULL,
276	/* .cleanup = */	ssh_ed25519_cleanup,
277	/* .equal = */		ssh_ed25519_equal,
278	/* .ssh_serialize_public = */ ssh_ed25519_serialize_public,
279	/* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public,
280	/* .ssh_serialize_private = */ ssh_ed25519_serialize_private,
281	/* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private,
282	/* .generate = */	ssh_ed25519_generate,
283	/* .copy_public = */	ssh_ed25519_copy_public,
284	/* .sign = */		ssh_ed25519_sign,
285	/* .verify = */		ssh_ed25519_verify,
286};
287
288const struct sshkey_impl sshkey_ed25519_impl = {
289	/* .name = */		"ssh-ed25519",
290	/* .shortname = */	"ED25519",
291	/* .sigalg = */		NULL,
292	/* .type = */		KEY_ED25519,
293	/* .nid = */		0,
294	/* .cert = */		0,
295	/* .sigonly = */	0,
296	/* .keybits = */	256,
297	/* .funcs = */		&sshkey_ed25519_funcs,
298};
299
300const struct sshkey_impl sshkey_ed25519_cert_impl = {
301	/* .name = */		"ssh-ed25519-cert-v01@openssh.com",
302	/* .shortname = */	"ED25519-CERT",
303	/* .sigalg = */		NULL,
304	/* .type = */		KEY_ED25519_CERT,
305	/* .nid = */		0,
306	/* .cert = */		1,
307	/* .sigonly = */	0,
308	/* .keybits = */	256,
309	/* .funcs = */		&sshkey_ed25519_funcs,
310};
311