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