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