1 /* $NetBSD: ssh-ecdsa.c,v 1.17 2025/10/11 15:45:07 christos Exp $ */ 2 /* $OpenBSD: ssh-ecdsa.c,v 1.28 2025/07/24 05:44:55 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.17 2025/10/11 15:45:07 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 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 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 115 ssh_ecdsa_cleanup(struct sshkey *k) 116 { 117 EVP_PKEY_free(k->pkey); 118 k->pkey = NULL; 119 } 120 121 static int 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 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 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 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 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 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 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 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 int ret = SSH_ERR_INTERNAL_ERROR; 326 327 if (lenp != NULL) 328 *lenp = 0; 329 if (sigp != NULL) 330 *sigp = NULL; 331 332 if (key == NULL || key->pkey == NULL || 333 sshkey_type_plain(key->type) != KEY_ECDSA) 334 return SSH_ERR_INVALID_ARGUMENT; 335 336 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) 337 return SSH_ERR_INTERNAL_ERROR; 338 339 if ((ret = sshkey_pkey_digest_sign(key->pkey, hash_alg, &sigb, &slen, 340 data, dlen)) != 0) 341 goto out; 342 343 psig = sigb; 344 if ((esig = d2i_ECDSA_SIG(NULL, &psig, slen)) == NULL) { 345 ret = SSH_ERR_LIBCRYPTO_ERROR; 346 goto out; 347 } 348 ECDSA_SIG_get0(esig, &sig_r, &sig_s); 349 350 if ((ret = ssh_ecdsa_encode_store_sig(key, sig_r, sig_s, 351 sigp, lenp)) != 0) 352 goto out; 353 /* success */ 354 ret = 0; 355 out: 356 freezero(sigb, slen); 357 ECDSA_SIG_free(esig); 358 return ret; 359 } 360 361 int 362 ssh_ecdsa_encode_store_sig(const struct sshkey *key, 363 const BIGNUM *sig_r, const BIGNUM *sig_s, 364 u_char **sigp, size_t *lenp) 365 { 366 struct sshbuf *b = NULL, *bb = NULL; 367 int ret; 368 size_t len; 369 370 if (lenp != NULL) 371 *lenp = 0; 372 if (sigp != NULL) 373 *sigp = NULL; 374 375 if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { 376 ret = SSH_ERR_ALLOC_FAIL; 377 goto out; 378 } 379 if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 || 380 (ret = sshbuf_put_bignum2(bb, sig_s)) != 0) 381 goto out; 382 if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || 383 (ret = sshbuf_put_stringb(b, bb)) != 0) 384 goto out; 385 len = sshbuf_len(b); 386 if (sigp != NULL) { 387 if ((*sigp = malloc(len)) == NULL) { 388 ret = SSH_ERR_ALLOC_FAIL; 389 goto out; 390 } 391 memcpy(*sigp, sshbuf_ptr(b), len); 392 } 393 if (lenp != NULL) 394 *lenp = len; 395 ret = 0; 396 out: 397 sshbuf_free(b); 398 sshbuf_free(bb); 399 return ret; 400 } 401 402 static int 403 ssh_ecdsa_verify(const struct sshkey *key, 404 const u_char *sig, size_t siglen, 405 const u_char *data, size_t dlen, const char *alg, u_int compat, 406 struct sshkey_sig_details **detailsp) 407 { 408 ECDSA_SIG *esig = NULL; 409 BIGNUM *sig_r = NULL, *sig_s = NULL; 410 int hash_alg, len = 0; 411 int ret = SSH_ERR_INTERNAL_ERROR; 412 struct sshbuf *b = NULL, *sigbuf = NULL; 413 char *ktype = NULL; 414 unsigned char *sigb = NULL, *cp; 415 416 if (key == NULL || key->pkey == NULL || 417 sshkey_type_plain(key->type) != KEY_ECDSA || 418 sig == NULL || siglen == 0) 419 return SSH_ERR_INVALID_ARGUMENT; 420 421 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) 422 return SSH_ERR_INTERNAL_ERROR; 423 424 /* fetch signature */ 425 if ((b = sshbuf_from(sig, siglen)) == NULL) 426 return SSH_ERR_ALLOC_FAIL; 427 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 428 sshbuf_froms(b, &sigbuf) != 0) { 429 ret = SSH_ERR_INVALID_FORMAT; 430 goto out; 431 } 432 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 433 ret = SSH_ERR_KEY_TYPE_MISMATCH; 434 goto out; 435 } 436 if (sshbuf_len(b) != 0) { 437 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 438 goto out; 439 } 440 441 /* parse signature */ 442 if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || 443 sshbuf_get_bignum2(sigbuf, &sig_s) != 0) { 444 ret = SSH_ERR_INVALID_FORMAT; 445 goto out; 446 } 447 if (sshbuf_len(sigbuf) != 0) { 448 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 449 goto out; 450 } 451 452 if ((esig = ECDSA_SIG_new()) == NULL) { 453 ret = SSH_ERR_ALLOC_FAIL; 454 goto out; 455 } 456 if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) { 457 ret = SSH_ERR_LIBCRYPTO_ERROR; 458 goto out; 459 } 460 sig_r = sig_s = NULL; /* transferred */ 461 462 if ((len = i2d_ECDSA_SIG(esig, NULL)) <= 0) { 463 len = 0; 464 ret = SSH_ERR_LIBCRYPTO_ERROR; 465 goto out; 466 } 467 if ((sigb = calloc(1, len)) == NULL) { 468 ret = SSH_ERR_ALLOC_FAIL; 469 goto out; 470 } 471 cp = sigb; /* ASN1_item_i2d increments the pointer past the object */ 472 if (i2d_ECDSA_SIG(esig, &cp) != len) { 473 ret = SSH_ERR_LIBCRYPTO_ERROR; 474 goto out; 475 } 476 if ((ret = sshkey_pkey_digest_verify(key->pkey, hash_alg, 477 data, dlen, sigb, len)) != 0) 478 goto out; 479 /* success */ 480 out: 481 freezero(sigb, len); 482 sshbuf_free(sigbuf); 483 sshbuf_free(b); 484 ECDSA_SIG_free(esig); 485 BN_clear_free(sig_r); 486 BN_clear_free(sig_s); 487 free(ktype); 488 return ret; 489 } 490 491 /* NB. not static; used by ECDSA-SK */ 492 const struct sshkey_impl_funcs sshkey_ecdsa_funcs = { 493 /* .size = */ ssh_ecdsa_size, 494 /* .alloc = */ NULL, 495 /* .cleanup = */ ssh_ecdsa_cleanup, 496 /* .equal = */ ssh_ecdsa_equal, 497 /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public, 498 /* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public, 499 /* .ssh_serialize_private = */ ssh_ecdsa_serialize_private, 500 /* .ssh_deserialize_private = */ ssh_ecdsa_deserialize_private, 501 /* .generate = */ ssh_ecdsa_generate, 502 /* .copy_public = */ ssh_ecdsa_copy_public, 503 /* .sign = */ ssh_ecdsa_sign, 504 /* .verify = */ ssh_ecdsa_verify, 505 }; 506 507 const struct sshkey_impl sshkey_ecdsa_nistp256_impl = { 508 /* .name = */ "ecdsa-sha2-nistp256", 509 /* .shortname = */ "ECDSA", 510 /* .sigalg = */ NULL, 511 /* .type = */ KEY_ECDSA, 512 /* .nid = */ NID_X9_62_prime256v1, 513 /* .cert = */ 0, 514 /* .sigonly = */ 0, 515 /* .keybits = */ 0, 516 /* .funcs = */ &sshkey_ecdsa_funcs, 517 }; 518 519 const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = { 520 /* .name = */ "ecdsa-sha2-nistp256-cert-v01 (at) openssh.com", 521 /* .shortname = */ "ECDSA-CERT", 522 /* .sigalg = */ NULL, 523 /* .type = */ KEY_ECDSA_CERT, 524 /* .nid = */ NID_X9_62_prime256v1, 525 /* .cert = */ 1, 526 /* .sigonly = */ 0, 527 /* .keybits = */ 0, 528 /* .funcs = */ &sshkey_ecdsa_funcs, 529 }; 530 531 const struct sshkey_impl sshkey_ecdsa_nistp384_impl = { 532 /* .name = */ "ecdsa-sha2-nistp384", 533 /* .shortname = */ "ECDSA", 534 /* .sigalg = */ NULL, 535 /* .type = */ KEY_ECDSA, 536 /* .nid = */ NID_secp384r1, 537 /* .cert = */ 0, 538 /* .sigonly = */ 0, 539 /* .keybits = */ 0, 540 /* .funcs = */ &sshkey_ecdsa_funcs, 541 }; 542 543 const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = { 544 /* .name = */ "ecdsa-sha2-nistp384-cert-v01 (at) openssh.com", 545 /* .shortname = */ "ECDSA-CERT", 546 /* .sigalg = */ NULL, 547 /* .type = */ KEY_ECDSA_CERT, 548 /* .nid = */ NID_secp384r1, 549 /* .cert = */ 1, 550 /* .sigonly = */ 0, 551 /* .keybits = */ 0, 552 /* .funcs = */ &sshkey_ecdsa_funcs, 553 }; 554 555 const struct sshkey_impl sshkey_ecdsa_nistp521_impl = { 556 /* .name = */ "ecdsa-sha2-nistp521", 557 /* .shortname = */ "ECDSA", 558 /* .sigalg = */ NULL, 559 /* .type = */ KEY_ECDSA, 560 /* .nid = */ NID_secp521r1, 561 /* .cert = */ 0, 562 /* .sigonly = */ 0, 563 /* .keybits = */ 0, 564 /* .funcs = */ &sshkey_ecdsa_funcs, 565 }; 566 567 const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = { 568 /* .name = */ "ecdsa-sha2-nistp521-cert-v01 (at) openssh.com", 569 /* .shortname = */ "ECDSA-CERT", 570 /* .sigalg = */ NULL, 571 /* .type = */ KEY_ECDSA_CERT, 572 /* .nid = */ NID_secp521r1, 573 /* .cert = */ 1, 574 /* .sigonly = */ 0, 575 /* .keybits = */ 0, 576 /* .funcs = */ &sshkey_ecdsa_funcs, 577 }; 578