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