jcs's openbsd hax
openbsd
at jcs 572 lines 14 kB view raw
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};