]>
Commit | Line | Data |
---|---|---|
27267642 | 1 | /* $OpenBSD: ssh-ed25519-sk.c,v 1.15 2022/10/28 00:44:44 djm Exp $ */ |
7c096c45 | 2 | /* |
3 | * Copyright (c) 2019 Markus Friedl. All rights reserved. | |
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 | */ | |
e0d38ae9 | 17 | |
18 | /* #define DEBUG_SK 1 */ | |
19 | ||
a0989b60 DT |
20 | #include "includes.h" |
21 | ||
7c096c45 | 22 | #define SSHKEY_INTERNAL |
23 | #include <sys/types.h> | |
24 | #include <limits.h> | |
25 | ||
26 | #include "crypto_api.h" | |
27 | ||
28 | #include <string.h> | |
29 | #include <stdarg.h> | |
30 | ||
31 | #include "log.h" | |
32 | #include "sshbuf.h" | |
33 | #include "sshkey.h" | |
34 | #include "ssherr.h" | |
35 | #include "ssh.h" | |
36 | #include "digest.h" | |
37 | ||
1e78844a | 38 | /* Reuse some ED25519 internals */ |
39 | extern struct sshkey_impl_funcs sshkey_ed25519_funcs; | |
40 | ||
25de1c01 | 41 | static void |
42 | ssh_ed25519_sk_cleanup(struct sshkey *k) | |
43 | { | |
1e78844a | 44 | sshkey_sk_cleanup(k); |
45 | sshkey_ed25519_funcs.cleanup(k); | |
46 | } | |
47 | ||
48 | static int | |
49 | ssh_ed25519_sk_equal(const struct sshkey *a, const struct sshkey *b) | |
50 | { | |
51 | if (!sshkey_sk_fields_equal(a, b)) | |
52 | return 0; | |
53 | if (!sshkey_ed25519_funcs.equal(a, b)) | |
54 | return 0; | |
55 | return 1; | |
25de1c01 | 56 | } |
57 | ||
591fed94 | 58 | static int |
59 | ssh_ed25519_sk_serialize_public(const struct sshkey *key, struct sshbuf *b, | |
a1deb6cd | 60 | enum sshkey_serialize_rep opts) |
591fed94 | 61 | { |
62 | int r; | |
63 | ||
a1deb6cd | 64 | if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) |
591fed94 | 65 | return r; |
66 | if ((r = sshkey_serialize_sk(key, b)) != 0) | |
67 | return r; | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
2519a707 | 72 | static int |
73 | ssh_ed25519_sk_serialize_private(const struct sshkey *key, struct sshbuf *b, | |
74 | enum sshkey_serialize_rep opts) | |
75 | { | |
76 | int r; | |
77 | ||
78 | if ((r = sshkey_ed25519_funcs.serialize_public(key, b, opts)) != 0) | |
79 | return r; | |
80 | if ((r = sshkey_serialize_private_sk(key, b)) != 0) | |
81 | return r; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
7d00799c | 86 | static int |
87 | ssh_ed25519_sk_copy_public(const struct sshkey *from, struct sshkey *to) | |
88 | { | |
89 | int r; | |
90 | ||
91 | if ((r = sshkey_ed25519_funcs.copy_public(from, to)) != 0) | |
92 | return r; | |
93 | if ((r = sshkey_copy_public_sk(from, to)) != 0) | |
94 | return r; | |
95 | return 0; | |
96 | } | |
97 | ||
a1deb6cd | 98 | static int |
99 | ssh_ed25519_sk_deserialize_public(const char *ktype, struct sshbuf *b, | |
100 | struct sshkey *key) | |
101 | { | |
102 | int r; | |
103 | ||
104 | if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) | |
105 | return r; | |
106 | if ((r = sshkey_deserialize_sk(b, key)) != 0) | |
107 | return r; | |
108 | return 0; | |
109 | } | |
110 | ||
27267642 | 111 | static int |
112 | ssh_ed25519_sk_deserialize_private(const char *ktype, struct sshbuf *b, | |
113 | struct sshkey *key) | |
114 | { | |
115 | int r; | |
116 | ||
117 | if ((r = sshkey_ed25519_funcs.deserialize_public(ktype, b, key)) != 0) | |
118 | return r; | |
119 | if ((r = sshkey_private_deserialize_sk(b, key)) != 0) | |
120 | return r; | |
121 | return 0; | |
122 | } | |
123 | ||
3fbc58bb | 124 | static int |
7c096c45 | 125 | ssh_ed25519_sk_verify(const struct sshkey *key, |
3fbc58bb | 126 | const u_char *sig, size_t siglen, |
127 | const u_char *data, size_t dlen, const char *alg, u_int compat, | |
b7e74ea0 | 128 | struct sshkey_sig_details **detailsp) |
7c096c45 | 129 | { |
130 | struct sshbuf *b = NULL; | |
7c096c45 | 131 | struct sshbuf *encoded = NULL; |
132 | char *ktype = NULL; | |
133 | const u_char *sigblob; | |
134 | const u_char *sm; | |
135 | u_char *m = NULL; | |
136 | u_char apphash[32]; | |
137 | u_char msghash[32]; | |
138 | u_char sig_flags; | |
139 | u_int sig_counter; | |
140 | size_t len; | |
141 | unsigned long long smlen = 0, mlen = 0; | |
142 | int r = SSH_ERR_INTERNAL_ERROR; | |
143 | int ret; | |
b7e74ea0 | 144 | struct sshkey_sig_details *details = NULL; |
145 | ||
146 | if (detailsp != NULL) | |
147 | *detailsp = NULL; | |
7c096c45 | 148 | |
149 | if (key == NULL || | |
150 | sshkey_type_plain(key->type) != KEY_ED25519_SK || | |
151 | key->ed25519_pk == NULL || | |
3fbc58bb | 152 | sig == NULL || siglen == 0) |
7c096c45 | 153 | return SSH_ERR_INVALID_ARGUMENT; |
154 | ||
3fbc58bb | 155 | if ((b = sshbuf_from(sig, siglen)) == NULL) |
7c096c45 | 156 | return SSH_ERR_ALLOC_FAIL; |
157 | if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || | |
b556cc3c | 158 | sshbuf_get_string_direct(b, &sigblob, &len) != 0 || |
159 | sshbuf_get_u8(b, &sig_flags) != 0 || | |
160 | sshbuf_get_u32(b, &sig_counter) != 0) { | |
7c096c45 | 161 | r = SSH_ERR_INVALID_FORMAT; |
162 | goto out; | |
163 | } | |
e0d38ae9 | 164 | #ifdef DEBUG_SK |
165 | fprintf(stderr, "%s: data:\n", __func__); | |
166 | /* sshbuf_dump_data(data, datalen, stderr); */ | |
167 | fprintf(stderr, "%s: sigblob:\n", __func__); | |
168 | sshbuf_dump_data(sigblob, len, stderr); | |
169 | fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", | |
170 | __func__, sig_flags, sig_counter); | |
171 | #endif | |
7c096c45 | 172 | if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { |
173 | r = SSH_ERR_KEY_TYPE_MISMATCH; | |
174 | goto out; | |
175 | } | |
176 | if (sshbuf_len(b) != 0) { | |
177 | r = SSH_ERR_UNEXPECTED_TRAILING_DATA; | |
178 | goto out; | |
179 | } | |
180 | if (len > crypto_sign_ed25519_BYTES) { | |
181 | r = SSH_ERR_INVALID_FORMAT; | |
182 | goto out; | |
183 | } | |
184 | if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, | |
185 | strlen(key->sk_application), apphash, sizeof(apphash)) != 0 || | |
3fbc58bb | 186 | ssh_digest_memory(SSH_DIGEST_SHA256, data, dlen, |
7c096c45 | 187 | msghash, sizeof(msghash)) != 0) { |
188 | r = SSH_ERR_INVALID_ARGUMENT; | |
189 | goto out; | |
190 | } | |
e0d38ae9 | 191 | #ifdef DEBUG_SK |
192 | fprintf(stderr, "%s: hashed application:\n", __func__); | |
193 | sshbuf_dump_data(apphash, sizeof(apphash), stderr); | |
194 | fprintf(stderr, "%s: hashed message:\n", __func__); | |
195 | sshbuf_dump_data(msghash, sizeof(msghash), stderr); | |
196 | #endif | |
b7e74ea0 | 197 | if ((details = calloc(1, sizeof(*details))) == NULL) { |
198 | r = SSH_ERR_ALLOC_FAIL; | |
199 | goto out; | |
200 | } | |
201 | details->sk_counter = sig_counter; | |
202 | details->sk_flags = sig_flags; | |
7c096c45 | 203 | if ((encoded = sshbuf_new()) == NULL) { |
204 | r = SSH_ERR_ALLOC_FAIL; | |
205 | goto out; | |
206 | } | |
207 | if (sshbuf_put(encoded, sigblob, len) != 0 || | |
208 | sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 || | |
209 | sshbuf_put_u8(encoded, sig_flags) != 0 || | |
210 | sshbuf_put_u32(encoded, sig_counter) != 0 || | |
211 | sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) { | |
212 | r = SSH_ERR_ALLOC_FAIL; | |
213 | goto out; | |
214 | } | |
e0d38ae9 | 215 | #ifdef DEBUG_SK |
216 | fprintf(stderr, "%s: signed buf:\n", __func__); | |
217 | sshbuf_dump(encoded, stderr); | |
218 | #endif | |
7c096c45 | 219 | sm = sshbuf_ptr(encoded); |
220 | smlen = sshbuf_len(encoded); | |
221 | mlen = smlen; | |
222 | if ((m = malloc(smlen)) == NULL) { | |
223 | r = SSH_ERR_ALLOC_FAIL; | |
224 | goto out; | |
225 | } | |
226 | if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, | |
227 | key->ed25519_pk)) != 0) { | |
816036f1 | 228 | debug2_f("crypto_sign_ed25519_open failed: %d", ret); |
7c096c45 | 229 | } |
230 | if (ret != 0 || mlen != smlen - len) { | |
231 | r = SSH_ERR_SIGNATURE_INVALID; | |
232 | goto out; | |
233 | } | |
234 | /* XXX compare 'm' and 'sm + len' ? */ | |
235 | /* success */ | |
236 | r = 0; | |
b7e74ea0 | 237 | if (detailsp != NULL) { |
238 | *detailsp = details; | |
239 | details = NULL; | |
240 | } | |
7c096c45 | 241 | out: |
d5ba1c03 | 242 | if (m != NULL) |
243 | freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ | |
b7e74ea0 | 244 | sshkey_sig_details_free(details); |
7c096c45 | 245 | sshbuf_free(b); |
7c096c45 | 246 | sshbuf_free(encoded); |
247 | free(ktype); | |
248 | return r; | |
249 | } | |
25de1c01 | 250 | |
251 | static const struct sshkey_impl_funcs sshkey_ed25519_sk_funcs = { | |
252 | /* .size = */ NULL, | |
253 | /* .alloc = */ NULL, | |
254 | /* .cleanup = */ ssh_ed25519_sk_cleanup, | |
1e78844a | 255 | /* .equal = */ ssh_ed25519_sk_equal, |
591fed94 | 256 | /* .ssh_serialize_public = */ ssh_ed25519_sk_serialize_public, |
a1deb6cd | 257 | /* .ssh_deserialize_public = */ ssh_ed25519_sk_deserialize_public, |
2519a707 | 258 | /* .ssh_serialize_private = */ ssh_ed25519_sk_serialize_private, |
27267642 | 259 | /* .ssh_deserialize_private = */ ssh_ed25519_sk_deserialize_private, |
262647c2 | 260 | /* .generate = */ NULL, |
7d00799c | 261 | /* .copy_public = */ ssh_ed25519_sk_copy_public, |
3fbc58bb | 262 | /* .sign = */ NULL, |
263 | /* .verify = */ ssh_ed25519_sk_verify, | |
25de1c01 | 264 | }; |
265 | ||
266 | const struct sshkey_impl sshkey_ed25519_sk_impl = { | |
267 | /* .name = */ "sk-ssh-ed25519@openssh.com", | |
268 | /* .shortname = */ "ED25519-SK", | |
269 | /* .sigalg = */ NULL, | |
270 | /* .type = */ KEY_ED25519_SK, | |
271 | /* .nid = */ 0, | |
272 | /* .cert = */ 0, | |
273 | /* .sigonly = */ 0, | |
274 | /* .keybits = */ 256, | |
275 | /* .funcs = */ &sshkey_ed25519_sk_funcs, | |
276 | }; | |
277 | ||
278 | const struct sshkey_impl sshkey_ed25519_sk_cert_impl = { | |
279 | /* .name = */ "sk-ssh-ed25519-cert-v01@openssh.com", | |
280 | /* .shortname = */ "ED25519-SK-CERT", | |
281 | /* .sigalg = */ NULL, | |
282 | /* .type = */ KEY_ED25519_SK_CERT, | |
283 | /* .nid = */ 0, | |
284 | /* .cert = */ 1, | |
285 | /* .sigonly = */ 0, | |
286 | /* .keybits = */ 256, | |
287 | /* .funcs = */ &sshkey_ed25519_sk_funcs, | |
288 | }; |