xref: /openbsd/usr.bin/ssh/ssh-ed25519.c (revision 3bef86f7)
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 
32 static void
33 ssh_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 
41 static int
42 ssh_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 
51 static int
52 ssh_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 
65 static int
66 ssh_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 
78 static int
79 ssh_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 
88 static int
89 ssh_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 
99 static int
100 ssh_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 
117 static int
118 ssh_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 
142 static int
143 ssh_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 
201 static int
202 ssh_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 */
273 const 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 
288 const 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 
300 const 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