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