1/* $NetBSD: ssh-ecdsa.c,v 1.15 2023/07/26 17:58:16 christos Exp $ */ 2/* $OpenBSD: ssh-ecdsa.c,v 1.26 2023/03/08 04:43:12 guenther Exp $ */ 3/* 4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 5 * Copyright (c) 2010 Damien Miller. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "includes.h" 29__RCSID("$NetBSD: ssh-ecdsa.c,v 1.15 2023/07/26 17:58:16 christos Exp $"); 30#include <sys/types.h> 31 32#include <openssl/bn.h> 33#include <openssl/ec.h> 34#include <openssl/ecdsa.h> 35#include <openssl/evp.h> 36 37#include <string.h> 38 39#include "sshbuf.h" 40#include "ssherr.h" 41#include "digest.h" 42#define SSHKEY_INTERNAL 43#include "sshkey.h" 44 45static u_int 46ssh_ecdsa_size(const struct sshkey *key) 47{ 48 switch (key->ecdsa_nid) { 49 case NID_X9_62_prime256v1: 50 return 256; 51 case NID_secp384r1: 52 return 384; 53 case NID_secp521r1: 54 return 521; 55 default: 56 return 0; 57 } 58} 59 60static void 61ssh_ecdsa_cleanup(struct sshkey *k) 62{ 63 EC_KEY_free(k->ecdsa); 64 k->ecdsa = NULL; 65} 66 67static int 68ssh_ecdsa_equal(const struct sshkey *a, const struct sshkey *b) 69{ 70 const EC_GROUP *grp_a, *grp_b; 71 const EC_POINT *pub_a, *pub_b; 72 73 if (a->ecdsa == NULL || b->ecdsa == NULL) 74 return 0; 75 if ((grp_a = EC_KEY_get0_group(a->ecdsa)) == NULL || 76 (grp_b = EC_KEY_get0_group(b->ecdsa)) == NULL) 77 return 0; 78 if ((pub_a = EC_KEY_get0_public_key(a->ecdsa)) == NULL || 79 (pub_b = EC_KEY_get0_public_key(b->ecdsa)) == NULL) 80 return 0; 81 if (EC_GROUP_cmp(grp_a, grp_b, NULL) != 0) 82 return 0; 83 if (EC_POINT_cmp(grp_a, pub_a, pub_b, NULL) != 0) 84 return 0; 85 86 return 1; 87} 88 89static int 90ssh_ecdsa_serialize_public(const struct sshkey *key, struct sshbuf *b, 91 enum sshkey_serialize_rep opts) 92{ 93 int r; 94 95 if (key->ecdsa == NULL) 96 return SSH_ERR_INVALID_ARGUMENT; 97 if ((r = sshbuf_put_cstring(b, 98 sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || 99 (r = sshbuf_put_eckey(b, key->ecdsa)) != 0) 100 return r; 101 102 return 0; 103} 104 105static int 106ssh_ecdsa_serialize_private(const struct sshkey *key, struct sshbuf *b, 107 enum sshkey_serialize_rep opts) 108{ 109 int r; 110 111 if (!sshkey_is_cert(key)) { 112 if ((r = ssh_ecdsa_serialize_public(key, b, opts)) != 0) 113 return r; 114 } 115 if ((r = sshbuf_put_bignum2(b, 116 EC_KEY_get0_private_key(key->ecdsa))) != 0) 117 return r; 118 return 0; 119} 120 121static int 122ssh_ecdsa_generate(struct sshkey *k, int bits) 123{ 124 EC_KEY *private; 125 126 if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) 127 return SSH_ERR_KEY_LENGTH; 128 if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL) 129 return SSH_ERR_ALLOC_FAIL; 130 if (EC_KEY_generate_key(private) != 1) { 131 EC_KEY_free(private); 132 return SSH_ERR_LIBCRYPTO_ERROR; 133 } 134 EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); 135 k->ecdsa = private; 136 return 0; 137} 138 139static int 140ssh_ecdsa_copy_public(const struct sshkey *from, struct sshkey *to) 141{ 142 to->ecdsa_nid = from->ecdsa_nid; 143 if ((to->ecdsa = EC_KEY_new_by_curve_name(from->ecdsa_nid)) == NULL) 144 return SSH_ERR_ALLOC_FAIL; 145 if (EC_KEY_set_public_key(to->ecdsa, 146 EC_KEY_get0_public_key(from->ecdsa)) != 1) 147 return SSH_ERR_LIBCRYPTO_ERROR; /* caller will free k->ecdsa */ 148 return 0; 149} 150 151static int 152ssh_ecdsa_deserialize_public(const char *ktype, struct sshbuf *b, 153 struct sshkey *key) 154{ 155 int r; 156 char *curve = NULL; 157 158 if ((key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype)) == -1) 159 return SSH_ERR_INVALID_ARGUMENT; 160 if ((r = sshbuf_get_cstring(b, &curve, NULL)) != 0) 161 goto out; 162 if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { 163 r = SSH_ERR_EC_CURVE_MISMATCH; 164 goto out; 165 } 166 EC_KEY_free(key->ecdsa); 167 key->ecdsa = NULL; 168 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) { 169 r = SSH_ERR_LIBCRYPTO_ERROR; 170 goto out; 171 } 172 if ((r = sshbuf_get_eckey(b, key->ecdsa)) != 0) 173 goto out; 174 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), 175 EC_KEY_get0_public_key(key->ecdsa)) != 0) { 176 r = SSH_ERR_KEY_INVALID_EC_VALUE; 177 goto out; 178 } 179 /* success */ 180 r = 0; 181#ifdef DEBUG_PK 182 sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), 183 EC_KEY_get0_public_key(key->ecdsa)); 184#endif 185 out: 186 free(curve); 187 if (r != 0) { 188 EC_KEY_free(key->ecdsa); 189 key->ecdsa = NULL; 190 } 191 return r; 192} 193 194static int 195ssh_ecdsa_deserialize_private(const char *ktype, struct sshbuf *b, 196 struct sshkey *key) 197{ 198 int r; 199 BIGNUM *exponent = NULL; 200 201 if (!sshkey_is_cert(key)) { 202 if ((r = ssh_ecdsa_deserialize_public(ktype, b, key)) != 0) 203 return r; 204 } 205 if ((r = sshbuf_get_bignum2(b, &exponent)) != 0) 206 goto out; 207 if (EC_KEY_set_private_key(key->ecdsa, exponent) != 1) { 208 r = SSH_ERR_LIBCRYPTO_ERROR; 209 goto out; 210 } 211 if ((r = sshkey_ec_validate_private(key->ecdsa)) != 0) 212 goto out; 213 /* success */ 214 r = 0; 215 out: 216 BN_clear_free(exponent); 217 return r; 218} 219 220static int 221ssh_ecdsa_sign(struct sshkey *key, 222 u_char **sigp, size_t *lenp, 223 const u_char *data, size_t dlen, 224 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 225{ 226 ECDSA_SIG *esig = NULL; 227 const BIGNUM *sig_r, *sig_s; 228 int hash_alg; 229 u_char digest[SSH_DIGEST_MAX_LENGTH]; 230 size_t len, hlen; 231 struct sshbuf *b = NULL, *bb = NULL; 232 int ret = SSH_ERR_INTERNAL_ERROR; 233 234 if (lenp != NULL) 235 *lenp = 0; 236 if (sigp != NULL) 237 *sigp = NULL; 238 239 if (key == NULL || key->ecdsa == NULL || 240 sshkey_type_plain(key->type) != KEY_ECDSA) 241 return SSH_ERR_INVALID_ARGUMENT; 242 243 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 244 (hlen = ssh_digest_bytes(hash_alg)) == 0) 245 return SSH_ERR_INTERNAL_ERROR; 246 if ((ret = ssh_digest_memory(hash_alg, data, dlen, 247 digest, sizeof(digest))) != 0) 248 goto out; 249 250 if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) { 251 ret = SSH_ERR_LIBCRYPTO_ERROR; 252 goto out; 253 } 254 255 if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { 256 ret = SSH_ERR_ALLOC_FAIL; 257 goto out; 258 } 259 ECDSA_SIG_get0(esig, &sig_r, &sig_s); 260 if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 || 261 (ret = sshbuf_put_bignum2(bb, sig_s)) != 0) 262 goto out; 263 if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || 264 (ret = sshbuf_put_stringb(b, bb)) != 0) 265 goto out; 266 len = sshbuf_len(b); 267 if (sigp != NULL) { 268 if ((*sigp = malloc(len)) == NULL) { 269 ret = SSH_ERR_ALLOC_FAIL; 270 goto out; 271 } 272 memcpy(*sigp, sshbuf_ptr(b), len); 273 } 274 if (lenp != NULL) 275 *lenp = len; 276 ret = 0; 277 out: 278 explicit_bzero(digest, sizeof(digest)); 279 sshbuf_free(b); 280 sshbuf_free(bb); 281 ECDSA_SIG_free(esig); 282 return ret; 283} 284 285static int 286ssh_ecdsa_verify(const struct sshkey *key, 287 const u_char *sig, size_t siglen, 288 const u_char *data, size_t dlen, const char *alg, u_int compat, 289 struct sshkey_sig_details **detailsp) 290{ 291 ECDSA_SIG *esig = NULL; 292 BIGNUM *sig_r = NULL, *sig_s = NULL; 293 int hash_alg; 294 u_char digest[SSH_DIGEST_MAX_LENGTH]; 295 size_t hlen; 296 int ret = SSH_ERR_INTERNAL_ERROR; 297 struct sshbuf *b = NULL, *sigbuf = NULL; 298 char *ktype = NULL; 299 300 if (key == NULL || key->ecdsa == NULL || 301 sshkey_type_plain(key->type) != KEY_ECDSA || 302 sig == NULL || siglen == 0) 303 return SSH_ERR_INVALID_ARGUMENT; 304 305 if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 306 (hlen = ssh_digest_bytes(hash_alg)) == 0) 307 return SSH_ERR_INTERNAL_ERROR; 308 309 /* fetch signature */ 310 if ((b = sshbuf_from(sig, siglen)) == NULL) 311 return SSH_ERR_ALLOC_FAIL; 312 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 313 sshbuf_froms(b, &sigbuf) != 0) { 314 ret = SSH_ERR_INVALID_FORMAT; 315 goto out; 316 } 317 if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 318 ret = SSH_ERR_KEY_TYPE_MISMATCH; 319 goto out; 320 } 321 if (sshbuf_len(b) != 0) { 322 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 323 goto out; 324 } 325 326 /* parse signature */ 327 if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || 328 sshbuf_get_bignum2(sigbuf, &sig_s) != 0) { 329 ret = SSH_ERR_INVALID_FORMAT; 330 goto out; 331 } 332 if ((esig = ECDSA_SIG_new()) == NULL) { 333 ret = SSH_ERR_ALLOC_FAIL; 334 goto out; 335 } 336 if (!ECDSA_SIG_set0(esig, sig_r, sig_s)) { 337 ret = SSH_ERR_LIBCRYPTO_ERROR; 338 goto out; 339 } 340 sig_r = sig_s = NULL; /* transferred */ 341 342 if (sshbuf_len(sigbuf) != 0) { 343 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 344 goto out; 345 } 346 if ((ret = ssh_digest_memory(hash_alg, data, dlen, 347 digest, sizeof(digest))) != 0) 348 goto out; 349 350 switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) { 351 case 1: 352 ret = 0; 353 break; 354 case 0: 355 ret = SSH_ERR_SIGNATURE_INVALID; 356 goto out; 357 default: 358 ret = SSH_ERR_LIBCRYPTO_ERROR; 359 goto out; 360 } 361 362 out: 363 explicit_bzero(digest, sizeof(digest)); 364 sshbuf_free(sigbuf); 365 sshbuf_free(b); 366 ECDSA_SIG_free(esig); 367 BN_clear_free(sig_r); 368 BN_clear_free(sig_s); 369 free(ktype); 370 return ret; 371} 372 373/* NB. not static; used by ECDSA-SK */ 374const struct sshkey_impl_funcs sshkey_ecdsa_funcs = { 375 /* .size = */ ssh_ecdsa_size, 376 /* .alloc = */ NULL, 377 /* .cleanup = */ ssh_ecdsa_cleanup, 378 /* .equal = */ ssh_ecdsa_equal, 379 /* .ssh_serialize_public = */ ssh_ecdsa_serialize_public, 380 /* .ssh_deserialize_public = */ ssh_ecdsa_deserialize_public, 381 /* .ssh_serialize_private = */ ssh_ecdsa_serialize_private, 382 /* .ssh_deserialize_private = */ ssh_ecdsa_deserialize_private, 383 /* .generate = */ ssh_ecdsa_generate, 384 /* .copy_public = */ ssh_ecdsa_copy_public, 385 /* .sign = */ ssh_ecdsa_sign, 386 /* .verify = */ ssh_ecdsa_verify, 387}; 388 389const struct sshkey_impl sshkey_ecdsa_nistp256_impl = { 390 /* .name = */ "ecdsa-sha2-nistp256", 391 /* .shortname = */ "ECDSA", 392 /* .sigalg = */ NULL, 393 /* .type = */ KEY_ECDSA, 394 /* .nid = */ NID_X9_62_prime256v1, 395 /* .cert = */ 0, 396 /* .sigonly = */ 0, 397 /* .keybits = */ 0, 398 /* .funcs = */ &sshkey_ecdsa_funcs, 399}; 400 401const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl = { 402 /* .name = */ "ecdsa-sha2-nistp256-cert-v01@openssh.com", 403 /* .shortname = */ "ECDSA-CERT", 404 /* .sigalg = */ NULL, 405 /* .type = */ KEY_ECDSA_CERT, 406 /* .nid = */ NID_X9_62_prime256v1, 407 /* .cert = */ 1, 408 /* .sigonly = */ 0, 409 /* .keybits = */ 0, 410 /* .funcs = */ &sshkey_ecdsa_funcs, 411}; 412 413const struct sshkey_impl sshkey_ecdsa_nistp384_impl = { 414 /* .name = */ "ecdsa-sha2-nistp384", 415 /* .shortname = */ "ECDSA", 416 /* .sigalg = */ NULL, 417 /* .type = */ KEY_ECDSA, 418 /* .nid = */ NID_secp384r1, 419 /* .cert = */ 0, 420 /* .sigonly = */ 0, 421 /* .keybits = */ 0, 422 /* .funcs = */ &sshkey_ecdsa_funcs, 423}; 424 425const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl = { 426 /* .name = */ "ecdsa-sha2-nistp384-cert-v01@openssh.com", 427 /* .shortname = */ "ECDSA-CERT", 428 /* .sigalg = */ NULL, 429 /* .type = */ KEY_ECDSA_CERT, 430 /* .nid = */ NID_secp384r1, 431 /* .cert = */ 1, 432 /* .sigonly = */ 0, 433 /* .keybits = */ 0, 434 /* .funcs = */ &sshkey_ecdsa_funcs, 435}; 436 437const struct sshkey_impl sshkey_ecdsa_nistp521_impl = { 438 /* .name = */ "ecdsa-sha2-nistp521", 439 /* .shortname = */ "ECDSA", 440 /* .sigalg = */ NULL, 441 /* .type = */ KEY_ECDSA, 442 /* .nid = */ NID_secp521r1, 443 /* .cert = */ 0, 444 /* .sigonly = */ 0, 445 /* .keybits = */ 0, 446 /* .funcs = */ &sshkey_ecdsa_funcs, 447}; 448 449const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl = { 450 /* .name = */ "ecdsa-sha2-nistp521-cert-v01@openssh.com", 451 /* .shortname = */ "ECDSA-CERT", 452 /* .sigalg = */ NULL, 453 /* .type = */ KEY_ECDSA_CERT, 454 /* .nid = */ NID_secp521r1, 455 /* .cert = */ 1, 456 /* .sigonly = */ 0, 457 /* .keybits = */ 0, 458 /* .funcs = */ &sshkey_ecdsa_funcs, 459}; 460