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