1 /* 2 * Copyright (c) 2018-2022 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7 #include <openssl/bn.h> 8 #include <openssl/ecdsa.h> 9 #include <openssl/obj_mac.h> 10 11 #include "fido.h" 12 #include "fido/es256.h" 13 14 #if OPENSSL_VERSION_NUMBER >= 0x30000000 15 #define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x)) 16 #else 17 #define get0_EC_KEY(x) EVP_PKEY_get0((x)) 18 #endif 19 20 static const int es256_nid = NID_X9_62_prime256v1; 21 22 static int 23 decode_coord(const cbor_item_t *item, void *xy, size_t xy_len) 24 { 25 if (cbor_isa_bytestring(item) == false || 26 cbor_bytestring_is_definite(item) == false || 27 cbor_bytestring_length(item) != xy_len) { 28 fido_log_debug("%s: cbor type", __func__); 29 return (-1); 30 } 31 32 memcpy(xy, cbor_bytestring_handle(item), xy_len); 33 34 return (0); 35 } 36 37 static int 38 decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg) 39 { 40 es256_pk_t *k = arg; 41 42 if (cbor_isa_negint(key) == false || 43 cbor_int_get_width(key) != CBOR_INT_8) 44 return (0); /* ignore */ 45 46 switch (cbor_get_uint8(key)) { 47 case 1: /* x coordinate */ 48 return (decode_coord(val, &k->x, sizeof(k->x))); 49 case 2: /* y coordinate */ 50 return (decode_coord(val, &k->y, sizeof(k->y))); 51 } 52 53 return (0); /* ignore */ 54 } 55 56 int 57 es256_pk_decode(const cbor_item_t *item, es256_pk_t *k) 58 { 59 if (cbor_isa_map(item) == false || 60 cbor_map_is_definite(item) == false || 61 cbor_map_iter(item, k, decode_pubkey_point) < 0) { 62 fido_log_debug("%s: cbor type", __func__); 63 return (-1); 64 } 65 66 return (0); 67 } 68 69 cbor_item_t * 70 es256_pk_encode(const es256_pk_t *pk, int ecdh) 71 { 72 cbor_item_t *item = NULL; 73 struct cbor_pair argv[5]; 74 int alg; 75 int ok = -1; 76 77 memset(argv, 0, sizeof(argv)); 78 79 if ((item = cbor_new_definite_map(5)) == NULL) 80 goto fail; 81 82 /* kty */ 83 if ((argv[0].key = cbor_build_uint8(1)) == NULL || 84 (argv[0].value = cbor_build_uint8(2)) == NULL || 85 !cbor_map_add(item, argv[0])) 86 goto fail; 87 88 /* 89 * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES + 90 * HKDF-256) although this is NOT the algorithm actually 91 * used. Setting this to a different value may result in 92 * compatibility issues." 93 */ 94 if (ecdh) 95 alg = COSE_ECDH_ES256; 96 else 97 alg = COSE_ES256; 98 99 /* alg */ 100 if ((argv[1].key = cbor_build_uint8(3)) == NULL || 101 (argv[1].value = cbor_build_negint8((uint8_t)(-alg - 1))) == NULL || 102 !cbor_map_add(item, argv[1])) 103 goto fail; 104 105 /* crv */ 106 if ((argv[2].key = cbor_build_negint8(0)) == NULL || 107 (argv[2].value = cbor_build_uint8(1)) == NULL || 108 !cbor_map_add(item, argv[2])) 109 goto fail; 110 111 /* x */ 112 if ((argv[3].key = cbor_build_negint8(1)) == NULL || 113 (argv[3].value = cbor_build_bytestring(pk->x, 114 sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3])) 115 goto fail; 116 117 /* y */ 118 if ((argv[4].key = cbor_build_negint8(2)) == NULL || 119 (argv[4].value = cbor_build_bytestring(pk->y, 120 sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4])) 121 goto fail; 122 123 ok = 0; 124 fail: 125 if (ok < 0) { 126 if (item != NULL) { 127 cbor_decref(&item); 128 item = NULL; 129 } 130 } 131 132 for (size_t i = 0; i < 5; i++) { 133 if (argv[i].key) 134 cbor_decref(&argv[i].key); 135 if (argv[i].value) 136 cbor_decref(&argv[i].value); 137 } 138 139 return (item); 140 } 141 142 es256_sk_t * 143 es256_sk_new(void) 144 { 145 return (calloc(1, sizeof(es256_sk_t))); 146 } 147 148 void 149 es256_sk_free(es256_sk_t **skp) 150 { 151 es256_sk_t *sk; 152 153 if (skp == NULL || (sk = *skp) == NULL) 154 return; 155 156 freezero(sk, sizeof(*sk)); 157 *skp = NULL; 158 } 159 160 es256_pk_t * 161 es256_pk_new(void) 162 { 163 return (calloc(1, sizeof(es256_pk_t))); 164 } 165 166 void 167 es256_pk_free(es256_pk_t **pkp) 168 { 169 es256_pk_t *pk; 170 171 if (pkp == NULL || (pk = *pkp) == NULL) 172 return; 173 174 freezero(pk, sizeof(*pk)); 175 *pkp = NULL; 176 } 177 178 int 179 es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len) 180 { 181 const uint8_t *p = ptr; 182 EVP_PKEY *pkey; 183 184 if (len < sizeof(*pk)) 185 return (FIDO_ERR_INVALID_ARGUMENT); 186 187 if (len == sizeof(*pk) + 1 && *p == 0x04) 188 memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */ 189 else 190 memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */ 191 192 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { 193 fido_log_debug("%s: es256_pk_to_EVP_PKEY", __func__); 194 explicit_bzero(pk, sizeof(*pk)); 195 return (FIDO_ERR_INVALID_ARGUMENT); 196 } 197 198 EVP_PKEY_free(pkey); 199 200 return (FIDO_OK); 201 } 202 203 int 204 es256_pk_set_x(es256_pk_t *pk, const unsigned char *x) 205 { 206 memcpy(pk->x, x, sizeof(pk->x)); 207 208 return (0); 209 } 210 211 int 212 es256_pk_set_y(es256_pk_t *pk, const unsigned char *y) 213 { 214 memcpy(pk->y, y, sizeof(pk->y)); 215 216 return (0); 217 } 218 219 int 220 es256_sk_create(es256_sk_t *key) 221 { 222 EVP_PKEY_CTX *pctx = NULL; 223 EVP_PKEY_CTX *kctx = NULL; 224 EVP_PKEY *p = NULL; 225 EVP_PKEY *k = NULL; 226 const EC_KEY *ec; 227 const BIGNUM *d; 228 int n; 229 int ok = -1; 230 231 if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL || 232 EVP_PKEY_paramgen_init(pctx) <= 0 || 233 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, es256_nid) <= 0 || 234 EVP_PKEY_paramgen(pctx, &p) <= 0) { 235 fido_log_debug("%s: EVP_PKEY_paramgen", __func__); 236 goto fail; 237 } 238 239 if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL || 240 EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) { 241 fido_log_debug("%s: EVP_PKEY_keygen", __func__); 242 goto fail; 243 } 244 245 if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL || 246 (d = EC_KEY_get0_private_key(ec)) == NULL || 247 (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) || 248 (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) { 249 fido_log_debug("%s: EC_KEY_get0_private_key", __func__); 250 goto fail; 251 } 252 253 ok = 0; 254 fail: 255 if (p != NULL) 256 EVP_PKEY_free(p); 257 if (k != NULL) 258 EVP_PKEY_free(k); 259 if (pctx != NULL) 260 EVP_PKEY_CTX_free(pctx); 261 if (kctx != NULL) 262 EVP_PKEY_CTX_free(kctx); 263 264 return (ok); 265 } 266 267 EVP_PKEY * 268 es256_pk_to_EVP_PKEY(const es256_pk_t *k) 269 { 270 BN_CTX *bnctx = NULL; 271 EC_KEY *ec = NULL; 272 EC_POINT *q = NULL; 273 EVP_PKEY *pkey = NULL; 274 BIGNUM *x = NULL; 275 BIGNUM *y = NULL; 276 const EC_GROUP *g = NULL; 277 int ok = -1; 278 279 if ((bnctx = BN_CTX_new()) == NULL) 280 goto fail; 281 282 BN_CTX_start(bnctx); 283 284 if ((x = BN_CTX_get(bnctx)) == NULL || 285 (y = BN_CTX_get(bnctx)) == NULL) 286 goto fail; 287 288 if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL || 289 BN_bin2bn(k->y, sizeof(k->y), y) == NULL) { 290 fido_log_debug("%s: BN_bin2bn", __func__); 291 goto fail; 292 } 293 294 if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL || 295 (g = EC_KEY_get0_group(ec)) == NULL) { 296 fido_log_debug("%s: EC_KEY init", __func__); 297 goto fail; 298 } 299 300 if ((q = EC_POINT_new(g)) == NULL || 301 EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || 302 EC_KEY_set_public_key(ec, q) == 0) { 303 fido_log_debug("%s: EC_KEY_set_public_key", __func__); 304 goto fail; 305 } 306 307 if ((pkey = EVP_PKEY_new()) == NULL || 308 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { 309 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); 310 goto fail; 311 } 312 313 ec = NULL; /* at this point, ec belongs to evp */ 314 315 ok = 0; 316 fail: 317 if (bnctx != NULL) { 318 BN_CTX_end(bnctx); 319 BN_CTX_free(bnctx); 320 } 321 322 if (ec != NULL) 323 EC_KEY_free(ec); 324 if (q != NULL) 325 EC_POINT_free(q); 326 327 if (ok < 0 && pkey != NULL) { 328 EVP_PKEY_free(pkey); 329 pkey = NULL; 330 } 331 332 return (pkey); 333 } 334 335 int 336 es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) 337 { 338 BN_CTX *bnctx = NULL; 339 BIGNUM *x = NULL; 340 BIGNUM *y = NULL; 341 const EC_POINT *q = NULL; 342 EC_GROUP *g = NULL; 343 size_t dx; 344 size_t dy; 345 int ok = FIDO_ERR_INTERNAL; 346 int nx; 347 int ny; 348 349 if ((q = EC_KEY_get0_public_key(ec)) == NULL || 350 (g = EC_GROUP_new_by_curve_name(es256_nid)) == NULL || 351 (bnctx = BN_CTX_new()) == NULL) 352 goto fail; 353 354 BN_CTX_start(bnctx); 355 356 if ((x = BN_CTX_get(bnctx)) == NULL || 357 (y = BN_CTX_get(bnctx)) == NULL) 358 goto fail; 359 360 if (EC_POINT_is_on_curve(g, q, bnctx) != 1) { 361 fido_log_debug("%s: EC_POINT_is_on_curve", __func__); 362 ok = FIDO_ERR_INVALID_ARGUMENT; 363 goto fail; 364 } 365 366 if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || 367 (nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) || 368 (ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) { 369 fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", 370 __func__); 371 goto fail; 372 } 373 374 dx = sizeof(pk->x) - (size_t)nx; 375 dy = sizeof(pk->y) - (size_t)ny; 376 377 if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) || 378 (ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) { 379 fido_log_debug("%s: BN_bn2bin", __func__); 380 goto fail; 381 } 382 383 ok = FIDO_OK; 384 fail: 385 EC_GROUP_free(g); 386 387 if (bnctx != NULL) { 388 BN_CTX_end(bnctx); 389 BN_CTX_free(bnctx); 390 } 391 392 return (ok); 393 } 394 395 int 396 es256_pk_from_EVP_PKEY(es256_pk_t *pk, const EVP_PKEY *pkey) 397 { 398 const EC_KEY *ec; 399 400 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC || 401 (ec = get0_EC_KEY(pkey)) == NULL) 402 return (FIDO_ERR_INVALID_ARGUMENT); 403 404 return (es256_pk_from_EC_KEY(pk, ec)); 405 } 406 407 EVP_PKEY * 408 es256_sk_to_EVP_PKEY(const es256_sk_t *k) 409 { 410 BN_CTX *bnctx = NULL; 411 EC_KEY *ec = NULL; 412 EVP_PKEY *pkey = NULL; 413 BIGNUM *d = NULL; 414 int ok = -1; 415 416 if ((bnctx = BN_CTX_new()) == NULL) 417 goto fail; 418 419 BN_CTX_start(bnctx); 420 421 if ((d = BN_CTX_get(bnctx)) == NULL || 422 BN_bin2bn(k->d, sizeof(k->d), d) == NULL) { 423 fido_log_debug("%s: BN_bin2bn", __func__); 424 goto fail; 425 } 426 427 if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL || 428 EC_KEY_set_private_key(ec, d) == 0) { 429 fido_log_debug("%s: EC_KEY_set_private_key", __func__); 430 goto fail; 431 } 432 433 if ((pkey = EVP_PKEY_new()) == NULL || 434 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { 435 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); 436 goto fail; 437 } 438 439 ec = NULL; /* at this point, ec belongs to evp */ 440 441 ok = 0; 442 fail: 443 if (bnctx != NULL) { 444 BN_CTX_end(bnctx); 445 BN_CTX_free(bnctx); 446 } 447 448 if (ec != NULL) 449 EC_KEY_free(ec); 450 451 if (ok < 0 && pkey != NULL) { 452 EVP_PKEY_free(pkey); 453 pkey = NULL; 454 } 455 456 return (pkey); 457 } 458 459 int 460 es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk) 461 { 462 BIGNUM *d = NULL; 463 EC_KEY *ec = NULL; 464 EC_POINT *q = NULL; 465 const EC_GROUP *g = NULL; 466 int ok = -1; 467 468 if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL || 469 (ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL || 470 (g = EC_KEY_get0_group(ec)) == NULL || 471 (q = EC_POINT_new(g)) == NULL) { 472 fido_log_debug("%s: get", __func__); 473 goto fail; 474 } 475 476 if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 || 477 EC_KEY_set_public_key(ec, q) == 0 || 478 es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) { 479 fido_log_debug("%s: set", __func__); 480 goto fail; 481 } 482 483 ok = 0; 484 fail: 485 if (d != NULL) 486 BN_clear_free(d); 487 if (q != NULL) 488 EC_POINT_free(q); 489 if (ec != NULL) 490 EC_KEY_free(ec); 491 492 return (ok); 493 } 494 495 int 496 es256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, 497 const fido_blob_t *sig) 498 { 499 EVP_PKEY_CTX *pctx = NULL; 500 int ok = -1; 501 502 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { 503 fido_log_debug("%s: EVP_PKEY_base_id", __func__); 504 goto fail; 505 } 506 507 if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL || 508 EVP_PKEY_verify_init(pctx) != 1 || 509 EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr, 510 dgst->len) != 1) { 511 fido_log_debug("%s: EVP_PKEY_verify", __func__); 512 goto fail; 513 } 514 515 ok = 0; 516 fail: 517 EVP_PKEY_CTX_free(pctx); 518 519 return (ok); 520 } 521 522 int 523 es256_pk_verify_sig(const fido_blob_t *dgst, const es256_pk_t *pk, 524 const fido_blob_t *sig) 525 { 526 EVP_PKEY *pkey; 527 int ok = -1; 528 529 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL || 530 es256_verify_sig(dgst, pkey, sig) < 0) { 531 fido_log_debug("%s: es256_verify_sig", __func__); 532 goto fail; 533 } 534 535 ok = 0; 536 fail: 537 EVP_PKEY_free(pkey); 538 539 return (ok); 540 } 541