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 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 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 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