]>
Commit | Line | Data |
---|---|---|
27267642 | 1 | /* $OpenBSD: ssh-ed25519.c,v 1.19 2022/10/28 00:44:44 djm Exp $ */ |
5be9d9e3 DM |
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 | ||
f104da26 DM |
18 | #include "includes.h" |
19 | ||
5be9d9e3 | 20 | #include <sys/types.h> |
8668706d | 21 | #include <limits.h> |
5be9d9e3 DM |
22 | |
23 | #include "crypto_api.h" | |
24 | ||
25 | #include <string.h> | |
26 | #include <stdarg.h> | |
27 | ||
5be9d9e3 | 28 | #include "log.h" |
3c4726f4 | 29 | #include "sshbuf.h" |
8668706d DM |
30 | #define SSHKEY_INTERNAL |
31 | #include "sshkey.h" | |
32 | #include "ssherr.h" | |
5be9d9e3 DM |
33 | #include "ssh.h" |
34 | ||
25de1c01 | 35 | static void |
36 | ssh_ed25519_cleanup(struct sshkey *k) | |
37 | { | |
38 | freezero(k->ed25519_pk, ED25519_PK_SZ); | |
39 | freezero(k->ed25519_sk, ED25519_SK_SZ); | |
40 | k->ed25519_pk = NULL; | |
41 | k->ed25519_sk = NULL; | |
42 | } | |
43 | ||
1e78844a | 44 | static int |
45 | ssh_ed25519_equal(const struct sshkey *a, const struct sshkey *b) | |
46 | { | |
47 | if (a->ed25519_pk == NULL || b->ed25519_pk == NULL) | |
48 | return 0; | |
49 | if (memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) != 0) | |
50 | return 0; | |
51 | return 1; | |
52 | } | |
53 | ||
591fed94 | 54 | static int |
55 | ssh_ed25519_serialize_public(const struct sshkey *key, struct sshbuf *b, | |
a1deb6cd | 56 | enum sshkey_serialize_rep opts) |
591fed94 | 57 | { |
58 | int r; | |
59 | ||
60 | if (key->ed25519_pk == NULL) | |
61 | return SSH_ERR_INVALID_ARGUMENT; | |
a1deb6cd | 62 | if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0) |
591fed94 | 63 | return r; |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
2519a707 | 68 | static int |
69 | ssh_ed25519_serialize_private(const struct sshkey *key, struct sshbuf *b, | |
70 | enum sshkey_serialize_rep opts) | |
71 | { | |
72 | int r; | |
73 | ||
74 | if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 || | |
75 | (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0) | |
76 | return r; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
262647c2 | 81 | static int |
82 | ssh_ed25519_generate(struct sshkey *k, int bits) | |
83 | { | |
84 | if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || | |
85 | (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) | |
86 | return SSH_ERR_ALLOC_FAIL; | |
87 | crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); | |
88 | return 0; | |
89 | } | |
90 | ||
7d00799c | 91 | static int |
92 | ssh_ed25519_copy_public(const struct sshkey *from, struct sshkey *to) | |
93 | { | |
94 | if (from->ed25519_pk == NULL) | |
95 | return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */ | |
96 | if ((to->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) | |
97 | return SSH_ERR_ALLOC_FAIL; | |
98 | memcpy(to->ed25519_pk, from->ed25519_pk, ED25519_PK_SZ); | |
99 | return 0; | |
100 | } | |
101 | ||
a1deb6cd | 102 | static int |
103 | ssh_ed25519_deserialize_public(const char *ktype, struct sshbuf *b, | |
104 | struct sshkey *key) | |
105 | { | |
106 | u_char *pk = NULL; | |
107 | size_t len = 0; | |
108 | int r; | |
109 | ||
110 | if ((r = sshbuf_get_string(b, &pk, &len)) != 0) | |
111 | return r; | |
112 | if (len != ED25519_PK_SZ) { | |
113 | freezero(pk, len); | |
114 | return SSH_ERR_INVALID_FORMAT; | |
115 | } | |
116 | key->ed25519_pk = pk; | |
117 | return 0; | |
118 | } | |
119 | ||
27267642 | 120 | static int |
121 | ssh_ed25519_deserialize_private(const char *ktype, struct sshbuf *b, | |
122 | struct sshkey *key) | |
123 | { | |
124 | int r; | |
125 | size_t sklen = 0; | |
126 | u_char *ed25519_sk = NULL; | |
127 | ||
128 | if ((r = ssh_ed25519_deserialize_public(NULL, b, key)) != 0) | |
129 | goto out; | |
130 | if ((r = sshbuf_get_string(b, &ed25519_sk, &sklen)) != 0) | |
131 | goto out; | |
132 | if (sklen != ED25519_SK_SZ) { | |
133 | r = SSH_ERR_INVALID_FORMAT; | |
134 | goto out; | |
135 | } | |
136 | key->ed25519_sk = ed25519_sk; | |
137 | ed25519_sk = NULL; /* transferred */ | |
138 | /* success */ | |
139 | r = 0; | |
140 | out: | |
141 | freezero(ed25519_sk, sklen); | |
142 | return r; | |
143 | } | |
144 | ||
3fbc58bb | 145 | static int |
146 | ssh_ed25519_sign(struct sshkey *key, | |
147 | u_char **sigp, size_t *lenp, | |
148 | const u_char *data, size_t datalen, | |
149 | const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) | |
5be9d9e3 | 150 | { |
8668706d DM |
151 | u_char *sig = NULL; |
152 | size_t slen = 0, len; | |
5be9d9e3 | 153 | unsigned long long smlen; |
8668706d DM |
154 | int r, ret; |
155 | struct sshbuf *b = NULL; | |
5be9d9e3 | 156 | |
8668706d DM |
157 | if (lenp != NULL) |
158 | *lenp = 0; | |
159 | if (sigp != NULL) | |
160 | *sigp = NULL; | |
bee3a234 | 161 | |
8668706d DM |
162 | if (key == NULL || |
163 | sshkey_type_plain(key->type) != KEY_ED25519 || | |
164 | key->ed25519_sk == NULL || | |
165 | datalen >= INT_MAX - crypto_sign_ed25519_BYTES) | |
166 | return SSH_ERR_INVALID_ARGUMENT; | |
5be9d9e3 | 167 | smlen = slen = datalen + crypto_sign_ed25519_BYTES; |
8668706d DM |
168 | if ((sig = malloc(slen)) == NULL) |
169 | return SSH_ERR_ALLOC_FAIL; | |
5be9d9e3 DM |
170 | |
171 | if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, | |
172 | key->ed25519_sk)) != 0 || smlen <= datalen) { | |
8668706d DM |
173 | r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ |
174 | goto out; | |
5be9d9e3 DM |
175 | } |
176 | /* encode signature */ | |
8668706d DM |
177 | if ((b = sshbuf_new()) == NULL) { |
178 | r = SSH_ERR_ALLOC_FAIL; | |
179 | goto out; | |
180 | } | |
181 | if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || | |
182 | (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) | |
183 | goto out; | |
184 | len = sshbuf_len(b); | |
185 | if (sigp != NULL) { | |
186 | if ((*sigp = malloc(len)) == NULL) { | |
187 | r = SSH_ERR_ALLOC_FAIL; | |
188 | goto out; | |
189 | } | |
190 | memcpy(*sigp, sshbuf_ptr(b), len); | |
191 | } | |
5be9d9e3 DM |
192 | if (lenp != NULL) |
193 | *lenp = len; | |
8668706d DM |
194 | /* success */ |
195 | r = 0; | |
196 | out: | |
197 | sshbuf_free(b); | |
70a5de0a | 198 | if (sig != NULL) |
d5ba1c03 | 199 | freezero(sig, slen); |
5be9d9e3 | 200 | |
8668706d | 201 | return r; |
5be9d9e3 DM |
202 | } |
203 | ||
3fbc58bb | 204 | static int |
8668706d | 205 | ssh_ed25519_verify(const struct sshkey *key, |
3fbc58bb | 206 | const u_char *sig, size_t siglen, |
207 | const u_char *data, size_t dlen, const char *alg, u_int compat, | |
208 | struct sshkey_sig_details **detailsp) | |
5be9d9e3 | 209 | { |
8668706d DM |
210 | struct sshbuf *b = NULL; |
211 | char *ktype = NULL; | |
212 | const u_char *sigblob; | |
213 | u_char *sm = NULL, *m = NULL; | |
214 | size_t len; | |
215 | unsigned long long smlen = 0, mlen = 0; | |
216 | int r, ret; | |
5be9d9e3 | 217 | |
8668706d DM |
218 | if (key == NULL || |
219 | sshkey_type_plain(key->type) != KEY_ED25519 || | |
220 | key->ed25519_pk == NULL || | |
3fbc58bb | 221 | dlen >= INT_MAX - crypto_sign_ed25519_BYTES || |
222 | sig == NULL || siglen == 0) | |
8668706d DM |
223 | return SSH_ERR_INVALID_ARGUMENT; |
224 | ||
3fbc58bb | 225 | if ((b = sshbuf_from(sig, siglen)) == NULL) |
8668706d DM |
226 | return SSH_ERR_ALLOC_FAIL; |
227 | if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || | |
228 | (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) | |
229 | goto out; | |
5be9d9e3 | 230 | if (strcmp("ssh-ed25519", ktype) != 0) { |
8668706d DM |
231 | r = SSH_ERR_KEY_TYPE_MISMATCH; |
232 | goto out; | |
5be9d9e3 | 233 | } |
8668706d DM |
234 | if (sshbuf_len(b) != 0) { |
235 | r = SSH_ERR_UNEXPECTED_TRAILING_DATA; | |
236 | goto out; | |
5be9d9e3 DM |
237 | } |
238 | if (len > crypto_sign_ed25519_BYTES) { | |
8668706d DM |
239 | r = SSH_ERR_INVALID_FORMAT; |
240 | goto out; | |
5be9d9e3 | 241 | } |
3fbc58bb | 242 | if (dlen >= SIZE_MAX - len) { |
165bc878 | 243 | r = SSH_ERR_INVALID_ARGUMENT; |
244 | goto out; | |
245 | } | |
3fbc58bb | 246 | smlen = len + dlen; |
8668706d | 247 | mlen = smlen; |
3c4726f4 | 248 | if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { |
8668706d DM |
249 | r = SSH_ERR_ALLOC_FAIL; |
250 | goto out; | |
251 | } | |
5be9d9e3 | 252 | memcpy(sm, sigblob, len); |
3fbc58bb | 253 | memcpy(sm+len, data, dlen); |
5be9d9e3 DM |
254 | if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, |
255 | key->ed25519_pk)) != 0) { | |
816036f1 | 256 | debug2_f("crypto_sign_ed25519_open failed: %d", ret); |
5be9d9e3 | 257 | } |
3fbc58bb | 258 | if (ret != 0 || mlen != dlen) { |
8668706d DM |
259 | r = SSH_ERR_SIGNATURE_INVALID; |
260 | goto out; | |
5be9d9e3 DM |
261 | } |
262 | /* XXX compare 'm' and 'data' ? */ | |
8668706d DM |
263 | /* success */ |
264 | r = 0; | |
265 | out: | |
70a5de0a | 266 | if (sm != NULL) |
d5ba1c03 | 267 | freezero(sm, smlen); |
70a5de0a | 268 | if (m != NULL) |
d5ba1c03 | 269 | freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ |
8668706d DM |
270 | sshbuf_free(b); |
271 | free(ktype); | |
272 | return r; | |
5be9d9e3 | 273 | } |
25de1c01 | 274 | |
1e78844a | 275 | /* NB. not static; used by ED25519-SK */ |
276 | const struct sshkey_impl_funcs sshkey_ed25519_funcs = { | |
25de1c01 | 277 | /* .size = */ NULL, |
278 | /* .alloc = */ NULL, | |
279 | /* .cleanup = */ ssh_ed25519_cleanup, | |
1e78844a | 280 | /* .equal = */ ssh_ed25519_equal, |
591fed94 | 281 | /* .ssh_serialize_public = */ ssh_ed25519_serialize_public, |
a1deb6cd | 282 | /* .ssh_deserialize_public = */ ssh_ed25519_deserialize_public, |
2519a707 | 283 | /* .ssh_serialize_private = */ ssh_ed25519_serialize_private, |
27267642 | 284 | /* .ssh_deserialize_private = */ ssh_ed25519_deserialize_private, |
262647c2 | 285 | /* .generate = */ ssh_ed25519_generate, |
7d00799c | 286 | /* .copy_public = */ ssh_ed25519_copy_public, |
3fbc58bb | 287 | /* .sign = */ ssh_ed25519_sign, |
288 | /* .verify = */ ssh_ed25519_verify, | |
25de1c01 | 289 | }; |
290 | ||
291 | const struct sshkey_impl sshkey_ed25519_impl = { | |
292 | /* .name = */ "ssh-ed25519", | |
293 | /* .shortname = */ "ED25519", | |
294 | /* .sigalg = */ NULL, | |
295 | /* .type = */ KEY_ED25519, | |
296 | /* .nid = */ 0, | |
297 | /* .cert = */ 0, | |
298 | /* .sigonly = */ 0, | |
299 | /* .keybits = */ 256, | |
300 | /* .funcs = */ &sshkey_ed25519_funcs, | |
301 | }; | |
302 | ||
303 | const struct sshkey_impl sshkey_ed25519_cert_impl = { | |
304 | /* .name = */ "ssh-ed25519-cert-v01@openssh.com", | |
305 | /* .shortname = */ "ED25519-CERT", | |
306 | /* .sigalg = */ NULL, | |
307 | /* .type = */ KEY_ED25519_CERT, | |
308 | /* .nid = */ 0, | |
309 | /* .cert = */ 1, | |
310 | /* .sigonly = */ 0, | |
311 | /* .keybits = */ 256, | |
312 | /* .funcs = */ &sshkey_ed25519_funcs, | |
313 | }; |