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