1 /* $NetBSD: ssh-ecdsa.c,v 1.16 2024/09/24 21:32:19 christos Exp $ */
2 /* $OpenBSD: ssh-ecdsa.c,v 1.27 2024/08/15 00:51:51 djm Exp $ */
3
4 /*
5 * Copyright (c) 2000 Markus Friedl. All rights reserved.
6 * Copyright (c) 2010 Damien Miller. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "includes.h"
30 __RCSID("$NetBSD: ssh-ecdsa.c,v 1.16 2024/09/24 21:32:19 christos Exp $");
31 #include <sys/types.h>
32
33 #include <openssl/bn.h>
34 #include <openssl/ec.h>
35 #include <openssl/ecdsa.h>
36 #include <openssl/evp.h>
37
38 #include <string.h>
39
40 #include "sshbuf.h"
41 #include "ssherr.h"
42 #include "digest.h"
43 #define SSHKEY_INTERNAL
44 #include "sshkey.h"
45
46 int
sshkey_ecdsa_fixup_group(EVP_PKEY * k)47 sshkey_ecdsa_fixup_group(EVP_PKEY *k)
48 {
49 int nids[] = {
50 NID_X9_62_prime256v1,
51 NID_secp384r1,
52 NID_secp521r1,
53 -1
54 };
55 int nid = -1;
56 u_int i;
57 const EC_GROUP *g;
58 EC_KEY *ec = NULL;
59 EC_GROUP *eg = NULL;
60
61 if ((ec = EVP_PKEY_get1_EC_KEY(k)) == NULL ||
62 (g = EC_KEY_get0_group(ec)) == NULL)
63 goto out;
64 /*
65 * The group may be stored in a ASN.1 encoded private key in one of two
66 * ways: as a "named group", which is reconstituted by ASN.1 object ID
67 * or explicit group parameters encoded into the key blob. Only the
68 * "named group" case sets the group NID for us, but we can figure
69 * it out for the other case by comparing against all the groups that
70 * are supported.
71 */
72 if ((nid = EC_GROUP_get_curve_name(g)) > 0)
73 goto out;
74 nid = -1;
75 for (i = 0; nids[i] != -1; i++) {
76 if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
77 goto out;
78 if (EC_GROUP_cmp(g, eg, NULL) == 0)
79 break;
80 EC_GROUP_free(eg);
81 eg = NULL;
82 }
83 if (nids[i] == -1)
84 goto out;
85
86 /* Use the group with the NID attached */
87 EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
88 if (EC_KEY_set_group(ec, eg) != 1 ||
89 EVP_PKEY_set1_EC_KEY(k, ec) != 1)
90 goto out;
91 /* success */
92 nid = nids[i];
93 out:
94 EC_KEY_free(ec);
95 EC_GROUP_free(eg);
96 return nid;
97 }
98
99 static u_int
ssh_ecdsa_size(const struct sshkey * key)100 ssh_ecdsa_size(const struct sshkey *key)
101 {
102 switch (key->ecdsa_nid) {
103 case NID_X9_62_prime256v1:
104 return 256;
105 case NID_secp384r1:
106 return 384;
107 case NID_secp521r1:
108 return 521;
109 default:
110 return 0;
111 }
112 }
113
114 static void
ssh_ecdsa_cleanup(struct sshkey * k)115 ssh_ecdsa_cleanup(struct sshkey *k)
116 {
117 EVP_PKEY_free(k->pkey);
118 k->pkey = NULL;
119 }
120
121 static int
ssh_ecdsa_equal(const struct sshkey * a,const struct sshkey * b)122 ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b)
123 {
124 if (a->pkey == NULL || b->pkey == NULL)
125 return 0;
126 return EVP_PKEY_cmp(a->pkey, b->pkey) == 1;
127 }
128
129 static int
ssh_ecdsa_serialize_public(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)130 ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b,
131 enum sshkey_serialize_rep opts)
132 {
133 int r;
134
135 if (key->pkey == NULL)
136 return SSH_ERR_INVALID_ARGUMENT;
137 if ((r = sshbuf_put_cstring(b,
138 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
139 (r = sshbuf_put_ec_pkey(b, key->pkey)) != 0)
140 return r;
141
142 return 0;
143 }
144
145 static int
ssh_ecdsa_serialize_private(const struct sshkey * key,struct sshbuf * b,enum sshkey_serialize_rep opts)146 ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b,
147 enum sshkey_serialize_rep opts)
148 {
149 int r;
150
151 if (!sshkey_is_cert(key)) {
152 if ((r = ssh_ecdsa_serialize_public(key, b, opts)) != 0)
153 return r;
154 }
155 if ((r = sshbuf_put_bignum2(b,
156 EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(key->pkey)))) != 0)
157 return r;
158 return 0;
159 }
160
161 static int
ssh_ecdsa_generate(struct sshkey * k,int bits)162 ssh_ecdsa_generate(struct sshkey *k, int bits)
163 {
164 EVP_PKEY *res = NULL;
165 EVP_PKEY_CTX *ctx = NULL;
166 int ret = SSH_ERR_INTERNAL_ERROR;
167
168 if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
169 return SSH_ERR_KEY_LENGTH;
170
171 if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL)
172 return SSH_ERR_ALLOC_FAIL;
173
174 if (EVP_PKEY_keygen_init(ctx) <= 0 ||
175 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, k->ecdsa_nid) <= 0 ||
176 EVP_PKEY_keygen(ctx, &res) <= 0) {
177 ret = SSH_ERR_LIBCRYPTO_ERROR;
178 goto out;
179 }
180 /* success */
181 k->pkey = res;
182 res = NULL;
183 ret = 0;
184 out:
185 EVP_PKEY_free(res);
186 EVP_PKEY_CTX_free(ctx);
187 return ret;
188 }
189
190 static int
ssh_ecdsa_copy_public(const struct sshkey * from,struct sshkey * to)191 ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to)
192 {
193 const EC_KEY *ec_from;
194 EC_KEY *ec_to = NULL;
195 int ret = SSH_ERR_INTERNAL_ERROR;
196
197 ec_from = EVP_PKEY_get0_EC_KEY(from->pkey);
198 if (ec_from == NULL)
199 return SSH_ERR_LIBCRYPTO_ERROR;
200
201 to->ecdsa_nid = from->ecdsa_nid;
202 if ((ec_to = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL)
203 return SSH_ERR_ALLOC_FAIL;
204 if (EC_KEY_set_public_key(ec_to,
205 EC_KEY_get0_public_key(ec_from)) != 1) {
206 ret = SSH_ERR_LIBCRYPTO_ERROR;
207 goto out;
208 }
209 EVP_PKEY_free(to->pkey);
210 if ((to->pkey = EVP_PKEY_new()) == NULL) {
211 ret = SSH_ERR_ALLOC_FAIL;
212 goto out;
213 }
214 if (EVP_PKEY_set1_EC_KEY(to->pkey, ec_to) != 1) {
215 ret = SSH_ERR_LIBCRYPTO_ERROR;
216 goto out;
217 }
218 ret = 0;
219 out:
220 EC_KEY_free(ec_to);
221 return ret;
222 }
223
224 static int
ssh_ecdsa_deserialize_public(const char * ktype,struct sshbuf * b,struct sshkey * key)225 ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b,
226 struct sshkey *key)
227 {
228 int r;
229 char *curve = NULL;
230 EVP_PKEY *pkey = NULL;
231 EC_KEY *ec = NULL;
232
233 if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1)
234 return SSH_ERR_INVALID_ARGUMENT;
235 if ((r = sshbuf_get_cstring(b, &curve, NULL)) != 0)
236 goto out;
237 if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
238 r = SSH_ERR_EC_CURVE_MISMATCH;
239 goto out;
240 }
241 if ((ec = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) {
242 r = SSH_ERR_LIBCRYPTO_ERROR;
243 goto out;
244 }
245 if ((r = sshbuf_get_eckey(b, ec)) != 0)
246 goto out;
247 if (sshkey_ec_validate_public(EC_KEY_get0_group(ec),
248 EC_KEY_get0_public_key(ec)) != 0) {
249 r = SSH_ERR_KEY_INVALID_EC_VALUE;
250 goto out;
251 }
252 if ((pkey = EVP_PKEY_new()) == NULL) {
253 r = SSH_ERR_ALLOC_FAIL;
254 goto out;
255 }
256 if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) {
257 r = SSH_ERR_LIBCRYPTO_ERROR;
258 goto out;
259 }
260 EVP_PKEY_free(key->pkey);
261 key->pkey = pkey;
262 pkey = NULL;
263 /* success */
264 r = 0;
265 #ifdef DEBUG_PK
266 sshkey_dump_ec_point(
267 EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key->pkey)),
268 EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(key->pkey)));
269 #endif
270 out:
271 EC_KEY_free(ec);
272 EVP_PKEY_free(pkey);
273 free(curve);
274 return r;
275 }
276
277 static int
ssh_ecdsa_deserialize_private(const char * ktype,struct sshbuf * b,struct sshkey * key)278 ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b,
279 struct sshkey *key)
280 {
281 int r;
282 BIGNUM *exponent = NULL;
283 EC_KEY *ec = NULL;
284
285 if (!sshkey_is_cert(key)) {
286 if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0)
287 return r;
288 }
289 if ((r = sshbuf_get_bignum2(b, &exponent)) != 0)
290 goto out;
291 if ((ec = EVP_PKEY_get1_EC_KEY(key->pkey)) == NULL) {
292 r = SSH_ERR_LIBCRYPTO_ERROR;
293 goto out;
294 }
295 if (EC_KEY_set_private_key(ec, exponent) != 1) {
296 r = SSH_ERR_LIBCRYPTO_ERROR;
297 goto out;
298 }
299 if ((r = sshkey_ec_validate_private(ec)) != 0)
300 goto out;
301 if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1) {
302 r = SSH_ERR_LIBCRYPTO_ERROR;
303 goto out;
304 }
305 /* success */
306 r = 0;
307 out:
308 BN_clear_free(exponent);
309 EC_KEY_free(ec);
310 return r;
311 }
312
313 static int
ssh_ecdsa_sign(struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t dlen,const char * alg,const char * sk_provider,const char * sk_pin,u_int compat)314 ssh_ecdsa_sign(struct sshkey *key,
315 u_char **sigp, size_t *lenp,
316 const u_char *data, size_t dlen,
317 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
318 {
319 ECDSA_SIG *esig = NULL;
320 unsigned char *sigb = NULL;
321 const unsigned char *psig;
322 const BIGNUM *sig_r, *sig_s;
323 int hash_alg;
324 size_t slen = 0;
325 struct sshbuf *b = NULL, *bb = NULL;
326 int len = 0, ret = SSH_ERR_INTERNAL_ERROR;
327
328 if (lenp != NULL)
329 *lenp = 0;
330 if (sigp != NULL)
331 *sigp = NULL;
332
333 if (key == NULL || key->pkey == NULL ||
334 sshkey_type_plain(key->type) != KEY_ECDSA)
335 return SSH_ERR_INVALID_ARGUMENT;
336
337 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
338 return SSH_ERR_INTERNAL_ERROR;
339
340 if ((ret = sshkey_pkey_digest_sign(key->pkey, hash_alg, &sigb, &slen,
341 data, dlen)) != 0)
342 goto out;
343
344 psig = sigb;
345 if ((esig = d2i_ECDSA_SIG(NULL, &psig, slen)) == NULL) {
346 ret = SSH_ERR_LIBCRYPTO_ERROR;
347 goto out;
348 }
349 if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
350 ret = SSH_ERR_ALLOC_FAIL;
351 goto out;
352 }
353 ECDSA_SIG_get0(esig, &sig_r, &sig_s);
354 if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 ||
355 (ret = sshbuf_put_bignum2(bb, sig_s)) != 0)
356 goto out;
357 if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
358 (ret = sshbuf_put_stringb(b, bb)) != 0)
359 goto out;
360 len = sshbuf_len(b);
361 if (sigp != NULL) {
362 if ((*sigp = malloc(len)) == NULL) {
363 ret = SSH_ERR_ALLOC_FAIL;
364 goto out;
365 }
366 memcpy(*sigp, sshbuf_ptr(b), len);
367 }
368 if (lenp != NULL)
369 *lenp = len;
370 ret = 0;
371 out:
372 freezero(sigb, slen);
373 sshbuf_free(b);
374 sshbuf_free(bb);
375 ECDSA_SIG_free(esig);
376 return ret;
377 }
378
379 static int
ssh_ecdsa_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)380 ssh_ecdsa_verify(const struct sshkey *key,
381 const u_char *sig, size_t siglen,
382 const u_char *data, size_t dlen, const char *alg, u_int compat,
383 struct sshkey_sig_details **detailsp)
384 {
385 ECDSA_SIG *esig = NULL;
386 BIGNUM *sig_r = NULL, *sig_s = NULL;
387 int hash_alg, len = 0;
388 int ret = SSH_ERR_INTERNAL_ERROR;
389 struct sshbuf *b = NULL, *sigbuf = NULL;
390 char *ktype = NULL;
391 unsigned char *sigb = NULL, *cp;
392
393 if (key == NULL || key->pkey == NULL ||
394 sshkey_type_plain(key->type) != KEY_ECDSA ||
395 sig == NULL || siglen == 0)
396 return SSH_ERR_INVALID_ARGUMENT;
397
398 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
399 return SSH_ERR_INTERNAL_ERROR;
400
401 /* fetch signature */
402 if ((b = sshbuf_from(sig, siglen)) == NULL)
403 return SSH_ERR_ALLOC_FAIL;
404 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
405 sshbuf_froms(b, &sigbuf) != 0) {
406 ret = SSH_ERR_INVALID_FORMAT;
407 goto out;
408 }
409 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
410 ret = SSH_ERR_KEY_TYPE_MISMATCH;
411 goto out;
412 }
413 if (sshbuf_len(b) != 0) {
414 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
415 goto out;
416 }
417
418 /* parse signature */
419 if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 ||
420 sshbuf_get_bignum2(sigbuf, &sig_s) != 0) {
421 ret = SSH_ERR_INVALID_FORMAT;
422 goto out;
423 }
424 if (sshbuf_len(sigbuf) != 0) {
425 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
426 goto out;
427 }
428
429 if ((esig = ECDSA_SIG_new()) == NULL) {
430 ret = SSH_ERR_ALLOC_FAIL;
431 goto out;
432 }
433 if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) {
434 ret = SSH_ERR_LIBCRYPTO_ERROR;
435 goto out;
436 }
437 sig_r = sig_s = NULL; /* transferred */
438
439 if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) {
440 len = 0;
441 ret = SSH_ERR_LIBCRYPTO_ERROR;
442 goto out;
443 }
444 if ((sigb = calloc(1, len)) == NULL) {
445 ret = SSH_ERR_ALLOC_FAIL;
446 goto out;
447 }
448 cp = sigb; /* ASN1_item_i2d increments the pointer past the object */
449 if (i2d_ECDSA_SIG(esig, &cp) != len) {
450 ret = SSH_ERR_LIBCRYPTO_ERROR;
451 goto out;
452 }
453 if ((ret = sshkey_pkey_digest_verify(key->pkey, hash_alg,
454 data, dlen, sigb, len)) != 0)
455 goto out;
456 /* success */
457 out:
458 freezero(sigb, len);
459 sshbuf_free(sigbuf);
460 sshbuf_free(b);
461 ECDSA_SIG_free(esig);
462 BN_clear_free(sig_r);
463 BN_clear_free(sig_s);
464 free(ktype);
465 return ret;
466 }
467
468 /* NB. not static; used by ECDSA-SK */
469 const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
470 /* .size = */ ssh_ecdsa_size,
471 /* .alloc = */ NULL,
472 /* .cleanup = */ ssh_ecdsa_cleanup,
473 /* .equal = */ ssh_ecdsa_equal,
474 /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public,
475 /* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public,
476 /* .ssh_serialize_private = */ ssh_ecdsa_serialize_private,
477 /* .ssh_deserialize_private = */ ssh_ecdsa_deserialize_private,
478 /* .generate = */ ssh_ecdsa_generate,
479 /* .copy_public = */ ssh_ecdsa_copy_public,
480 /* .sign = */ ssh_ecdsa_sign,
481 /* .verify = */ ssh_ecdsa_verify,
482 };
483
484 const struct sshkey_impl sshkey_ecdsa_nistp256_impl = {
485 /* .name = */ "ecdsa-sha2-nistp256",
486 /* .shortname = */ "ECDSA",
487 /* .sigalg = */ NULL,
488 /* .type = */ KEY_ECDSA,
489 /* .nid = */ NID_X9_62_prime256v1,
490 /* .cert = */ 0,
491 /* .sigonly = */ 0,
492 /* .keybits = */ 0,
493 /* .funcs = */ &sshkey_ecdsa_funcs,
494 };
495
496 const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = {
497 /* .name = */ "ecdsa-sha2-nistp256-cert-v01@openssh.com",
498 /* .shortname = */ "ECDSA-CERT",
499 /* .sigalg = */ NULL,
500 /* .type = */ KEY_ECDSA_CERT,
501 /* .nid = */ NID_X9_62_prime256v1,
502 /* .cert = */ 1,
503 /* .sigonly = */ 0,
504 /* .keybits = */ 0,
505 /* .funcs = */ &sshkey_ecdsa_funcs,
506 };
507
508 const struct sshkey_impl sshkey_ecdsa_nistp384_impl = {
509 /* .name = */ "ecdsa-sha2-nistp384",
510 /* .shortname = */ "ECDSA",
511 /* .sigalg = */ NULL,
512 /* .type = */ KEY_ECDSA,
513 /* .nid = */ NID_secp384r1,
514 /* .cert = */ 0,
515 /* .sigonly = */ 0,
516 /* .keybits = */ 0,
517 /* .funcs = */ &sshkey_ecdsa_funcs,
518 };
519
520 const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = {
521 /* .name = */ "ecdsa-sha2-nistp384-cert-v01@openssh.com",
522 /* .shortname = */ "ECDSA-CERT",
523 /* .sigalg = */ NULL,
524 /* .type = */ KEY_ECDSA_CERT,
525 /* .nid = */ NID_secp384r1,
526 /* .cert = */ 1,
527 /* .sigonly = */ 0,
528 /* .keybits = */ 0,
529 /* .funcs = */ &sshkey_ecdsa_funcs,
530 };
531
532 const struct sshkey_impl sshkey_ecdsa_nistp521_impl = {
533 /* .name = */ "ecdsa-sha2-nistp521",
534 /* .shortname = */ "ECDSA",
535 /* .sigalg = */ NULL,
536 /* .type = */ KEY_ECDSA,
537 /* .nid = */ NID_secp521r1,
538 /* .cert = */ 0,
539 /* .sigonly = */ 0,
540 /* .keybits = */ 0,
541 /* .funcs = */ &sshkey_ecdsa_funcs,
542 };
543
544 const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = {
545 /* .name = */ "ecdsa-sha2-nistp521-cert-v01@openssh.com",
546 /* .shortname = */ "ECDSA-CERT",
547 /* .sigalg = */ NULL,
548 /* .type = */ KEY_ECDSA_CERT,
549 /* .nid = */ NID_secp521r1,
550 /* .cert = */ 1,
551 /* .sigonly = */ 0,
552 /* .keybits = */ 0,
553 /* .funcs = */ &sshkey_ecdsa_funcs,
554 };
555