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