| Line | Count | Source | 
| 1 |  | /* | 
| 2 |  |  * Copyright (c) 2018-2023 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/sha.h> | 
| 9 |  |  | 
| 10 |  | #include "fido.h" | 
| 11 |  | #include "fido/es256.h" | 
| 12 |  | #include "fido/rs256.h" | 
| 13 |  | #include "fido/eddsa.h" | 
| 14 |  |  | 
| 15 |  | static int | 
| 16 |  | adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) | 
| 17 | 6.22k | { | 
| 18 | 6.22k |         fido_assert_t   *assert = arg; | 
| 19 | 6.22k |         uint64_t         n; | 
| 20 |  |  | 
| 21 |  |         /* numberOfCredentials; see section 6.2 */ | 
| 22 | 6.22k |         if (cbor_isa_uint(key) == false || | 
| 23 | 6.22k |             cbor_int_get_width(key) != CBOR_INT_8 || | 
| 24 | 6.22k |             cbor_get_uint8(key) != 5) { | 
| 25 | 5.70k |                 fido_log_debug("%s: cbor_type", __func__); | 
| 26 | 5.70k |                 return (0); /* ignore */ | 
| 27 | 5.70k |         } | 
| 28 |  |  | 
| 29 | 516 |         if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { | 
| 30 | 4 |                 fido_log_debug("%s: cbor_decode_uint64", __func__); | 
| 31 | 4 |                 return (-1); | 
| 32 | 4 |         } | 
| 33 |  |  | 
| 34 | 512 |         if (assert->stmt_len != 0 || assert->stmt_cnt != 1 || | 
| 35 | 512 |             (size_t)n < assert->stmt_cnt) { | 
| 36 | 4 |                 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu", | 
| 37 | 4 |                     __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n); | 
| 38 | 4 |                 return (-1); | 
| 39 | 4 |         } | 
| 40 |  |  | 
| 41 | 508 |         if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) { | 
| 42 | 112 |                 fido_log_debug("%s: fido_assert_set_count", __func__); | 
| 43 | 112 |                 return (-1); | 
| 44 | 112 |         } | 
| 45 |  |  | 
| 46 | 396 |         assert->stmt_len = 0; /* XXX */ | 
| 47 |  |  | 
| 48 | 396 |         return (0); | 
| 49 | 508 | } | 
| 50 |  |  | 
| 51 |  | static int | 
| 52 |  | parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) | 
| 53 | 9.97k | { | 
| 54 | 9.97k |         fido_assert_stmt *stmt = arg; | 
| 55 |  |  | 
| 56 | 9.97k |         if (cbor_isa_uint(key) == false || | 
| 57 | 9.97k |             cbor_int_get_width(key) != CBOR_INT_8) { | 
| 58 | 300 |                 fido_log_debug("%s: cbor type", __func__); | 
| 59 | 300 |                 return (0); /* ignore */ | 
| 60 | 300 |         } | 
| 61 |  |  | 
| 62 | 9.67k |         switch (cbor_get_uint8(key)) { | 
| 63 | 2.75k |         case 1: /* credential id */ | 
| 64 | 2.75k |                 return (cbor_decode_cred_id(val, &stmt->id)); | 
| 65 | 2.38k |         case 2: /* authdata */ | 
| 66 | 2.38k |                 if (fido_blob_decode(val, &stmt->authdata_raw) < 0) { | 
| 67 | 14 |                         fido_log_debug("%s: fido_blob_decode", __func__); | 
| 68 | 14 |                         return (-1); | 
| 69 | 14 |                 } | 
| 70 | 2.37k |                 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor, | 
| 71 | 2.37k |                     &stmt->authdata, &stmt->authdata_ext)); | 
| 72 | 2.21k |         case 3: /* signature */ | 
| 73 | 2.21k |                 return (fido_blob_decode(val, &stmt->sig)); | 
| 74 | 1.78k |         case 4: /* user attributes */ | 
| 75 | 1.78k |                 return (cbor_decode_user(val, &stmt->user)); | 
| 76 | 3 |         case 7: /* large blob key */ | 
| 77 | 3 |                 return (fido_blob_decode(val, &stmt->largeblob_key)); | 
| 78 | 534 |         default: /* ignore */ | 
| 79 | 534 |                 fido_log_debug("%s: cbor type", __func__); | 
| 80 | 534 |                 return (0); | 
| 81 | 9.67k |         } | 
| 82 | 9.67k | } | 
| 83 |  |  | 
| 84 |  | static int | 
| 85 |  | fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert, | 
| 86 |  |     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) | 
| 87 | 3.57k | { | 
| 88 | 3.57k |         fido_blob_t      f; | 
| 89 | 3.57k |         fido_opt_t       uv = assert->uv; | 
| 90 | 3.57k |         cbor_item_t     *argv[7]; | 
| 91 | 3.57k |         const uint8_t    cmd = CTAP_CBOR_ASSERT; | 
| 92 | 3.57k |         int              r; | 
| 93 |  |  | 
| 94 | 3.57k |         memset(argv, 0, sizeof(argv)); | 
| 95 | 3.57k |         memset(&f, 0, sizeof(f)); | 
| 96 |  |  | 
| 97 |  |         /* do we have everything we need? */ | 
| 98 | 3.57k |         if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { | 
| 99 | 0 |                 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, | 
| 100 | 0 |                     (void *)assert->rp_id, (void *)assert->cdh.ptr); | 
| 101 | 0 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 102 | 0 |                 goto fail; | 
| 103 | 0 |         } | 
| 104 |  |  | 
| 105 | 3.57k |         if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL || | 
| 106 | 3.57k |             (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) { | 
| 107 | 22 |                 fido_log_debug("%s: cbor encode", __func__); | 
| 108 | 22 |                 r = FIDO_ERR_INTERNAL; | 
| 109 | 22 |                 goto fail; | 
| 110 | 22 |         } | 
| 111 |  |  | 
| 112 |  |         /* allowed credentials */ | 
| 113 | 3.55k |         if (assert->allow_list.len) { | 
| 114 | 1.89k |                 const fido_blob_array_t *cl = &assert->allow_list; | 
| 115 | 1.89k |                 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) { | 
| 116 | 321 |                         fido_log_debug("%s: cbor_encode_pubkey_list", __func__); | 
| 117 | 321 |                         r = FIDO_ERR_INTERNAL; | 
| 118 | 321 |                         goto fail; | 
| 119 | 321 |                 } | 
| 120 | 1.89k |         } | 
| 121 |  |  | 
| 122 | 3.23k |         if (assert->ext.mask) | 
| 123 | 1.67k |                 if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh, | 
| 124 | 1.67k |                     pk)) == NULL) { | 
| 125 | 221 |                         fido_log_debug("%s: cbor_encode_assert_ext", __func__); | 
| 126 | 221 |                         r = FIDO_ERR_INTERNAL; | 
| 127 | 221 |                         goto fail; | 
| 128 | 221 |                 } | 
| 129 |  |  | 
| 130 |  |         /* user verification */ | 
| 131 | 3.01k |         if (pin != NULL || (uv == FIDO_OPT_TRUE && | 
| 132 | 1.81k |             fido_dev_supports_permissions(dev))) { | 
| 133 | 1.23k |                 if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh, | 
| 134 | 1.23k |                     pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) { | 
| 135 | 404 |                         fido_log_debug("%s: cbor_add_uv_params", __func__); | 
| 136 | 404 |                         goto fail; | 
| 137 | 404 |                 } | 
| 138 | 828 |                 uv = FIDO_OPT_OMIT; | 
| 139 | 828 |         } | 
| 140 |  |  | 
| 141 |  |         /* options */ | 
| 142 | 2.61k |         if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) | 
| 143 | 1.06k |                 if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) { | 
| 144 | 16 |                         fido_log_debug("%s: cbor_encode_assert_opt", __func__); | 
| 145 | 16 |                         r = FIDO_ERR_INTERNAL; | 
| 146 | 16 |                         goto fail; | 
| 147 | 16 |                 } | 
| 148 |  |  | 
| 149 |  |         /* frame and transmit */ | 
| 150 | 2.59k |         if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || | 
| 151 | 2.59k |             fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { | 
| 152 | 370 |                 fido_log_debug("%s: fido_tx", __func__); | 
| 153 | 370 |                 r = FIDO_ERR_TX; | 
| 154 | 370 |                 goto fail; | 
| 155 | 370 |         } | 
| 156 |  |  | 
| 157 | 2.22k |         r = FIDO_OK; | 
| 158 | 3.57k | fail: | 
| 159 | 3.57k |         cbor_vector_free(argv, nitems(argv)); | 
| 160 | 3.57k |         free(f.ptr); | 
| 161 |  |  | 
| 162 | 3.57k |         return (r); | 
| 163 | 2.22k | } | 
| 164 |  |  | 
| 165 |  | static int | 
| 166 |  | fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) | 
| 167 | 2.22k | { | 
| 168 | 2.22k |         unsigned char   *msg; | 
| 169 | 2.22k |         int              msglen; | 
| 170 | 2.22k |         int              r; | 
| 171 |  |  | 
| 172 | 2.22k |         fido_assert_reset_rx(assert); | 
| 173 |  |  | 
| 174 | 2.22k |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 175 | 3 |                 r = FIDO_ERR_INTERNAL; | 
| 176 | 3 |                 goto out; | 
| 177 | 3 |         } | 
| 178 |  |  | 
| 179 | 2.22k |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 180 | 169 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 181 | 169 |                 r = FIDO_ERR_RX; | 
| 182 | 169 |                 goto out; | 
| 183 | 169 |         } | 
| 184 |  |  | 
| 185 |  |         /* start with room for a single assertion */ | 
| 186 | 2.05k |         if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) { | 
| 187 | 4 |                 r = FIDO_ERR_INTERNAL; | 
| 188 | 4 |                 goto out; | 
| 189 | 4 |         } | 
| 190 | 2.04k |         assert->stmt_len = 0; | 
| 191 | 2.04k |         assert->stmt_cnt = 1; | 
| 192 |  |  | 
| 193 |  |         /* adjust as needed */ | 
| 194 | 2.04k |         if ((r = cbor_parse_reply(msg, (size_t)msglen, assert, | 
| 195 | 2.04k |             adjust_assert_count)) != FIDO_OK) { | 
| 196 | 245 |                 fido_log_debug("%s: adjust_assert_count", __func__); | 
| 197 | 245 |                 goto out; | 
| 198 | 245 |         } | 
| 199 |  |  | 
| 200 |  |         /* parse the first assertion */ | 
| 201 | 1.80k |         if ((r = cbor_parse_reply(msg, (size_t)msglen, &assert->stmt[0], | 
| 202 | 1.80k |             parse_assert_reply)) != FIDO_OK) { | 
| 203 | 152 |                 fido_log_debug("%s: parse_assert_reply", __func__); | 
| 204 | 152 |                 goto out; | 
| 205 | 152 |         } | 
| 206 | 1.65k |         assert->stmt_len = 1; | 
| 207 |  |  | 
| 208 | 1.65k |         r = FIDO_OK; | 
| 209 | 2.22k | out: | 
| 210 | 2.22k |         freezero(msg, FIDO_MAXMSG); | 
| 211 |  |  | 
| 212 | 2.22k |         return (r); | 
| 213 | 1.65k | } | 
| 214 |  |  | 
| 215 |  | static int | 
| 216 |  | fido_get_next_assert_tx(fido_dev_t *dev, int *ms) | 
| 217 | 1.22k | { | 
| 218 | 1.22k |         const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; | 
| 219 |  |  | 
| 220 | 1.22k |         if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) { | 
| 221 | 10 |                 fido_log_debug("%s: fido_tx", __func__); | 
| 222 | 10 |                 return (FIDO_ERR_TX); | 
| 223 | 10 |         } | 
| 224 |  |  | 
| 225 | 1.21k |         return (FIDO_OK); | 
| 226 | 1.22k | } | 
| 227 |  |  | 
| 228 |  | static int | 
| 229 |  | fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) | 
| 230 | 1.21k | { | 
| 231 | 1.21k |         unsigned char   *msg; | 
| 232 | 1.21k |         int              msglen; | 
| 233 | 1.21k |         int              r; | 
| 234 |  |  | 
| 235 | 1.21k |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 236 | 4 |                 r = FIDO_ERR_INTERNAL; | 
| 237 | 4 |                 goto out; | 
| 238 | 4 |         } | 
| 239 |  |  | 
| 240 | 1.21k |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 241 | 49 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 242 | 49 |                 r = FIDO_ERR_RX; | 
| 243 | 49 |                 goto out; | 
| 244 | 49 |         } | 
| 245 |  |  | 
| 246 |  |         /* sanity check */ | 
| 247 | 1.16k |         if (assert->stmt_len >= assert->stmt_cnt) { | 
| 248 | 0 |                 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__, | 
| 249 | 0 |                     assert->stmt_len, assert->stmt_cnt); | 
| 250 | 0 |                 r = FIDO_ERR_INTERNAL; | 
| 251 | 0 |                 goto out; | 
| 252 | 0 |         } | 
| 253 |  |  | 
| 254 | 1.16k |         if ((r = cbor_parse_reply(msg, (size_t)msglen, | 
| 255 | 1.16k |             &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { | 
| 256 | 113 |                 fido_log_debug("%s: parse_assert_reply", __func__); | 
| 257 | 113 |                 goto out; | 
| 258 | 113 |         } | 
| 259 |  |  | 
| 260 | 1.05k |         r = FIDO_OK; | 
| 261 | 1.21k | out: | 
| 262 | 1.21k |         freezero(msg, FIDO_MAXMSG); | 
| 263 |  |  | 
| 264 | 1.21k |         return (r); | 
| 265 | 1.05k | } | 
| 266 |  |  | 
| 267 |  | static int | 
| 268 |  | fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert, | 
| 269 |  |     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) | 
| 270 | 3.57k | { | 
| 271 | 3.57k |         int r; | 
| 272 |  |  | 
| 273 | 3.57k |         if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin, | 
| 274 | 3.57k |             ms)) != FIDO_OK || | 
| 275 | 3.57k |             (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK) | 
| 276 | 1.92k |                 return (r); | 
| 277 |  |  | 
| 278 | 2.70k |         while (assert->stmt_len < assert->stmt_cnt) { | 
| 279 | 1.22k |                 if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK || | 
| 280 | 1.22k |                     (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK) | 
| 281 | 176 |                         return (r); | 
| 282 | 1.05k |                 assert->stmt_len++; | 
| 283 | 1.05k |         } | 
| 284 |  |  | 
| 285 | 1.47k |         return (FIDO_OK); | 
| 286 | 1.65k | } | 
| 287 |  |  | 
| 288 |  | static int | 
| 289 |  | decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert, | 
| 290 |  |     const fido_blob_t *key) | 
| 291 | 148 | { | 
| 292 | 454 |         for (size_t i = 0; i < assert->stmt_cnt; i++) { | 
| 293 | 358 |                 fido_assert_stmt *stmt = &assert->stmt[i]; | 
| 294 | 358 |                 if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) { | 
| 295 | 198 |                         if (aes256_cbc_dec(dev, key, | 
| 296 | 198 |                             &stmt->authdata_ext.hmac_secret_enc, | 
| 297 | 198 |                             &stmt->hmac_secret) < 0) { | 
| 298 | 52 |                                 fido_log_debug("%s: aes256_cbc_dec %zu", | 
| 299 | 52 |                                     __func__, i); | 
| 300 | 52 |                                 return (-1); | 
| 301 | 52 |                         } | 
| 302 | 198 |                 } | 
| 303 | 358 |         } | 
| 304 |  |  | 
| 305 | 96 |         return (0); | 
| 306 | 148 | } | 
| 307 |  |  | 
| 308 |  | int | 
| 309 |  | fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin) | 
| 310 | 8.50k | { | 
| 311 | 8.50k |         fido_blob_t     *ecdh = NULL; | 
| 312 | 8.50k |         es256_pk_t      *pk = NULL; | 
| 313 | 8.50k |         int              ms = dev->timeout_ms; | 
| 314 | 8.50k |         int              r; | 
| 315 |  |  | 
| 316 |  | #ifdef USE_WINHELLO | 
| 317 |  |         if (dev->flags & FIDO_DEV_WINHELLO) | 
| 318 |  |                 return (fido_winhello_get_assert(dev, assert, pin, ms)); | 
| 319 |  | #endif | 
| 320 |  |  | 
| 321 | 8.50k |         if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { | 
| 322 | 29 |                 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, | 
| 323 | 29 |                     (void *)assert->rp_id, (void *)assert->cdh.ptr); | 
| 324 | 29 |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 325 | 29 |         } | 
| 326 |  |  | 
| 327 | 8.47k |         if (fido_dev_is_fido2(dev) == false) { | 
| 328 | 3.93k |                 if (pin != NULL || assert->ext.mask != 0) | 
| 329 | 1.08k |                         return (FIDO_ERR_UNSUPPORTED_OPTION); | 
| 330 | 2.85k |                 return (u2f_authenticate(dev, assert, &ms)); | 
| 331 | 3.93k |         } | 
| 332 |  |  | 
| 333 | 4.53k |         if (pin != NULL || (assert->uv == FIDO_OPT_TRUE && | 
| 334 | 2.60k |             fido_dev_supports_permissions(dev)) || | 
| 335 | 4.53k |             (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) { | 
| 336 | 2.79k |                 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { | 
| 337 | 956 |                         fido_log_debug("%s: fido_do_ecdh", __func__); | 
| 338 | 956 |                         goto fail; | 
| 339 | 956 |                 } | 
| 340 | 2.79k |         } | 
| 341 |  |  | 
| 342 | 3.57k |         r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms); | 
| 343 | 3.57k |         if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) | 
| 344 | 148 |                 if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) { | 
| 345 | 52 |                         fido_log_debug("%s: decrypt_hmac_secrets", __func__); | 
| 346 | 52 |                         r = FIDO_ERR_INTERNAL; | 
| 347 | 52 |                         goto fail; | 
| 348 | 52 |                 } | 
| 349 |  |  | 
| 350 | 4.53k | fail: | 
| 351 | 4.53k |         es256_pk_free(&pk); | 
| 352 | 4.53k |         fido_blob_free(&ecdh); | 
| 353 |  |  | 
| 354 | 4.53k |         return (r); | 
| 355 | 3.57k | } | 
| 356 |  |  | 
| 357 |  | int | 
| 358 |  | fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv) | 
| 359 | 8.97k | { | 
| 360 | 8.97k |         fido_log_debug("%s: flags=%02x", __func__, flags); | 
| 361 | 8.97k |         fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv); | 
| 362 |  |  | 
| 363 | 8.97k |         if (up == FIDO_OPT_TRUE && | 
| 364 | 8.97k |             (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) { | 
| 365 | 130 |                 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__); | 
| 366 | 130 |                 return (-1); /* user not present */ | 
| 367 | 130 |         } | 
| 368 |  |  | 
| 369 | 8.84k |         if (uv == FIDO_OPT_TRUE && | 
| 370 | 8.84k |             (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) { | 
| 371 | 134 |                 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__); | 
| 372 | 134 |                 return (-1); /* user not verified */ | 
| 373 | 134 |         } | 
| 374 |  |  | 
| 375 | 8.70k |         return (0); | 
| 376 | 8.84k | } | 
| 377 |  |  | 
| 378 |  | static int | 
| 379 |  | check_extensions(int authdata_ext, int ext) | 
| 380 | 3.34k | { | 
| 381 |  |         /* XXX: largeBlobKey is not part of extensions map */ | 
| 382 | 3.34k |         ext &= ~FIDO_EXT_LARGEBLOB_KEY; | 
| 383 | 3.34k |         if (authdata_ext != ext) { | 
| 384 | 164 |                 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, | 
| 385 | 164 |                     authdata_ext, ext); | 
| 386 | 164 |                 return (-1); | 
| 387 | 164 |         } | 
| 388 |  |  | 
| 389 | 3.17k |         return (0); | 
| 390 | 3.34k | } | 
| 391 |  |  | 
| 392 |  | static int | 
| 393 |  | get_es256_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, | 
| 394 |  |     const fido_blob_t *authdata) | 
| 395 | 4.12k | { | 
| 396 | 4.12k |         const EVP_MD    *md; | 
| 397 | 4.12k |         EVP_MD_CTX      *ctx = NULL; | 
| 398 |  |  | 
| 399 | 4.12k |         if (dgst->len < SHA256_DIGEST_LENGTH || | 
| 400 | 4.12k |             (md = EVP_sha256()) == NULL || | 
| 401 | 4.12k |             (ctx = EVP_MD_CTX_new()) == NULL || | 
| 402 | 4.12k |             EVP_DigestInit_ex(ctx, md, NULL) != 1 || | 
| 403 | 4.12k |             EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || | 
| 404 | 4.12k |             EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || | 
| 405 | 4.12k |             EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { | 
| 406 | 128 |                 EVP_MD_CTX_free(ctx); | 
| 407 | 128 |                 return (-1); | 
| 408 | 128 |         } | 
| 409 | 3.99k |         dgst->len = SHA256_DIGEST_LENGTH; | 
| 410 |  |  | 
| 411 | 3.99k |         EVP_MD_CTX_free(ctx); | 
| 412 |  |  | 
| 413 | 3.99k |         return (0); | 
| 414 | 4.12k | } | 
| 415 |  |  | 
| 416 |  | static int | 
| 417 |  | get_es384_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, | 
| 418 |  |     const fido_blob_t *authdata) | 
| 419 | 1.89k | { | 
| 420 | 1.89k |         const EVP_MD    *md; | 
| 421 | 1.89k |         EVP_MD_CTX      *ctx = NULL; | 
| 422 |  |  | 
| 423 | 1.89k |         if (dgst->len < SHA384_DIGEST_LENGTH || | 
| 424 | 1.89k |             (md = EVP_sha384()) == NULL || | 
| 425 | 1.89k |             (ctx = EVP_MD_CTX_new()) == NULL || | 
| 426 | 1.89k |             EVP_DigestInit_ex(ctx, md, NULL) != 1 || | 
| 427 | 1.89k |             EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || | 
| 428 | 1.89k |             EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || | 
| 429 | 1.89k |             EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { | 
| 430 | 91 |                 EVP_MD_CTX_free(ctx); | 
| 431 | 91 |                 return (-1); | 
| 432 | 91 |         } | 
| 433 | 1.80k |         dgst->len = SHA384_DIGEST_LENGTH; | 
| 434 |  |  | 
| 435 | 1.80k |         EVP_MD_CTX_free(ctx); | 
| 436 |  |  | 
| 437 | 1.80k |         return (0); | 
| 438 | 1.89k | } | 
| 439 |  |  | 
| 440 |  | static int | 
| 441 |  | get_eddsa_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, | 
| 442 |  |     const fido_blob_t *authdata) | 
| 443 | 706 | { | 
| 444 | 706 |         if (SIZE_MAX - authdata->len < clientdata->len || | 
| 445 | 706 |             dgst->len < authdata->len + clientdata->len) | 
| 446 | 45 |                 return (-1); | 
| 447 |  |  | 
| 448 | 661 |         memcpy(dgst->ptr, authdata->ptr, authdata->len); | 
| 449 | 661 |         memcpy(dgst->ptr + authdata->len, clientdata->ptr, clientdata->len); | 
| 450 | 661 |         dgst->len = authdata->len + clientdata->len; | 
| 451 |  |  | 
| 452 | 661 |         return (0); | 
| 453 | 706 | } | 
| 454 |  |  | 
| 455 |  | int | 
| 456 |  | fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, | 
| 457 |  |     const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor) | 
| 458 | 6.76k | { | 
| 459 | 6.76k |         cbor_item_t             *item = NULL; | 
| 460 | 6.76k |         fido_blob_t              authdata; | 
| 461 | 6.76k |         struct cbor_load_result  cbor; | 
| 462 | 6.76k |         int                      ok = -1; | 
| 463 |  |  | 
| 464 | 6.76k |         fido_log_debug("%s: cose_alg=%d", __func__, cose_alg); | 
| 465 |  |  | 
| 466 | 6.76k |         if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, | 
| 467 | 6.76k |             &cbor)) == NULL || cbor_isa_bytestring(item) == false || | 
| 468 | 6.76k |             cbor_bytestring_is_definite(item) == false) { | 
| 469 | 30 |                 fido_log_debug("%s: authdata", __func__); | 
| 470 | 30 |                 goto fail; | 
| 471 | 30 |         } | 
| 472 | 6.73k |         authdata.ptr = cbor_bytestring_handle(item); | 
| 473 | 6.73k |         authdata.len = cbor_bytestring_length(item); | 
| 474 |  |  | 
| 475 | 6.73k |         switch (cose_alg) { | 
| 476 | 3.71k |         case COSE_ES256: | 
| 477 | 4.12k |         case COSE_RS256: | 
| 478 | 4.12k |                 ok = get_es256_hash(dgst, clientdata, &authdata); | 
| 479 | 4.12k |                 break; | 
| 480 | 1.89k |         case COSE_ES384: | 
| 481 | 1.89k |                 ok = get_es384_hash(dgst, clientdata, &authdata); | 
| 482 | 1.89k |                 break; | 
| 483 | 706 |         case COSE_EDDSA: | 
| 484 | 706 |                 ok = get_eddsa_hash(dgst, clientdata, &authdata); | 
| 485 | 706 |                 break; | 
| 486 | 0 |         default: | 
| 487 | 0 |                 fido_log_debug("%s: unknown cose_alg", __func__); | 
| 488 | 0 |                 break; | 
| 489 | 6.73k |         } | 
| 490 | 6.76k | fail: | 
| 491 | 6.76k |         if (item != NULL) | 
| 492 | 6.73k |                 cbor_decref(&item); | 
| 493 |  |  | 
| 494 | 6.76k |         return (ok); | 
| 495 | 6.73k | } | 
| 496 |  |  | 
| 497 |  | int | 
| 498 |  | fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, | 
| 499 |  |     const void *pk) | 
| 500 | 155k | { | 
| 501 | 155k |         unsigned char            buf[1024]; /* XXX */ | 
| 502 | 155k |         fido_blob_t              dgst; | 
| 503 | 155k |         const fido_assert_stmt  *stmt = NULL; | 
| 504 | 155k |         int                      ok = -1; | 
| 505 | 155k |         int                      r; | 
| 506 |  |  | 
| 507 | 155k |         dgst.ptr = buf; | 
| 508 | 155k |         dgst.len = sizeof(buf); | 
| 509 |  |  | 
| 510 | 155k |         if (idx >= assert->stmt_len || pk == NULL) { | 
| 511 | 407 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 512 | 407 |                 goto out; | 
| 513 | 407 |         } | 
| 514 |  |  | 
| 515 | 155k |         stmt = &assert->stmt[idx]; | 
| 516 |  |  | 
| 517 |  |         /* do we have everything we need? */ | 
| 518 | 155k |         if (assert->cdh.ptr == NULL || assert->rp_id == NULL || | 
| 519 | 155k |             stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) { | 
| 520 | 151k |                 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p", | 
| 521 | 151k |                     __func__, (void *)assert->cdh.ptr, assert->rp_id, | 
| 522 | 151k |                     (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr); | 
| 523 | 151k |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 524 | 151k |                 goto out; | 
| 525 | 151k |         } | 
| 526 |  |  | 
| 527 | 3.59k |         if (fido_check_flags(stmt->authdata.flags, assert->up, | 
| 528 | 3.59k |             assert->uv) < 0) { | 
| 529 | 251 |                 fido_log_debug("%s: fido_check_flags", __func__); | 
| 530 | 251 |                 r = FIDO_ERR_INVALID_PARAM; | 
| 531 | 251 |                 goto out; | 
| 532 | 251 |         } | 
| 533 |  |  | 
| 534 | 3.34k |         if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) { | 
| 535 | 164 |                 fido_log_debug("%s: check_extensions", __func__); | 
| 536 | 164 |                 r = FIDO_ERR_INVALID_PARAM; | 
| 537 | 164 |                 goto out; | 
| 538 | 164 |         } | 
| 539 |  |  | 
| 540 | 3.17k |         if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) { | 
| 541 | 394 |                 fido_log_debug("%s: fido_check_rp_id", __func__); | 
| 542 | 394 |                 r = FIDO_ERR_INVALID_PARAM; | 
| 543 | 394 |                 goto out; | 
| 544 | 394 |         } | 
| 545 |  |  | 
| 546 | 2.78k |         if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh, | 
| 547 | 2.78k |             &stmt->authdata_cbor) < 0) { | 
| 548 | 194 |                 fido_log_debug("%s: fido_get_signed_hash", __func__); | 
| 549 | 194 |                 r = FIDO_ERR_INTERNAL; | 
| 550 | 194 |                 goto out; | 
| 551 | 194 |         } | 
| 552 |  |  | 
| 553 | 2.58k |         switch (cose_alg) { | 
| 554 | 307 |         case COSE_ES256: | 
| 555 | 307 |                 ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig); | 
| 556 | 307 |                 break; | 
| 557 | 1.59k |         case COSE_ES384: | 
| 558 | 1.59k |                 ok = es384_pk_verify_sig(&dgst, pk, &stmt->sig); | 
| 559 | 1.59k |                 break; | 
| 560 | 270 |         case COSE_RS256: | 
| 561 | 270 |                 ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig); | 
| 562 | 270 |                 break; | 
| 563 | 421 |         case COSE_EDDSA: | 
| 564 | 421 |                 ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig); | 
| 565 | 421 |                 break; | 
| 566 | 0 |         default: | 
| 567 | 0 |                 fido_log_debug("%s: unsupported cose_alg %d", __func__, | 
| 568 | 0 |                     cose_alg); | 
| 569 | 0 |                 r = FIDO_ERR_UNSUPPORTED_OPTION; | 
| 570 | 0 |                 goto out; | 
| 571 | 2.58k |         } | 
| 572 |  |  | 
| 573 | 2.58k |         if (ok < 0) | 
| 574 | 2.58k |                 r = FIDO_ERR_INVALID_SIG; | 
| 575 | 0 |         else | 
| 576 | 0 |                 r = FIDO_OK; | 
| 577 | 155k | out: | 
| 578 | 155k |         explicit_bzero(buf, sizeof(buf)); | 
| 579 |  |  | 
| 580 | 155k |         return (r); | 
| 581 | 2.58k | } | 
| 582 |  |  | 
| 583 |  | int | 
| 584 |  | fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data, | 
| 585 |  |     size_t data_len) | 
| 586 | 0 | { | 
| 587 | 0 |         if (!fido_blob_is_empty(&assert->cdh) || | 
| 588 | 0 |             fido_blob_set(&assert->cd, data, data_len) < 0) { | 
| 589 | 0 |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 590 | 0 |         } | 
| 591 | 0 |         if (fido_sha256(&assert->cdh, data, data_len) < 0) { | 
| 592 | 0 |                 fido_blob_reset(&assert->cd); | 
| 593 | 0 |                 return (FIDO_ERR_INTERNAL); | 
| 594 | 0 |         } | 
| 595 |  |  | 
| 596 | 0 |         return (FIDO_OK); | 
| 597 | 0 | } | 
| 598 |  |  | 
| 599 |  | int | 
| 600 |  | fido_assert_set_clientdata_hash(fido_assert_t *assert, | 
| 601 |  |     const unsigned char *hash, size_t hash_len) | 
| 602 | 172k | { | 
| 603 | 172k |         if (!fido_blob_is_empty(&assert->cd) || | 
| 604 | 172k |             fido_blob_set(&assert->cdh, hash, hash_len) < 0) | 
| 605 | 3.00k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 606 |  |  | 
| 607 | 169k |         return (FIDO_OK); | 
| 608 | 172k | } | 
| 609 |  |  | 
| 610 |  | int | 
| 611 |  | fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt, | 
| 612 |  |     size_t salt_len) | 
| 613 | 17.0k | { | 
| 614 | 17.0k |         if ((salt_len != 32 && salt_len != 64) || | 
| 615 | 17.0k |             fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0) | 
| 616 | 16.1k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 617 |  |  | 
| 618 | 882 |         return (FIDO_OK); | 
| 619 | 17.0k | } | 
| 620 |  |  | 
| 621 |  | int | 
| 622 |  | fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx, | 
| 623 |  |     const unsigned char *secret, size_t secret_len) | 
| 624 | 0 | { | 
| 625 | 0 |         if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) || | 
| 626 | 0 |             fido_blob_set(&assert->stmt[idx].hmac_secret, secret, | 
| 627 | 0 |             secret_len) < 0) | 
| 628 | 0 |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 629 |  |  | 
| 630 | 0 |         return (FIDO_OK); | 
| 631 | 0 | } | 
| 632 |  |  | 
| 633 |  | int | 
| 634 |  | fido_assert_set_rp(fido_assert_t *assert, const char *id) | 
| 635 | 172k | { | 
| 636 | 172k |         if (assert->rp_id != NULL) { | 
| 637 | 8.48k |                 free(assert->rp_id); | 
| 638 | 8.48k |                 assert->rp_id = NULL; | 
| 639 | 8.48k |         } | 
| 640 |  |  | 
| 641 | 172k |         if (id == NULL) | 
| 642 | 2.52k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 643 |  |  | 
| 644 | 170k |         if ((assert->rp_id = strdup(id)) == NULL) | 
| 645 | 429 |                 return (FIDO_ERR_INTERNAL); | 
| 646 |  |  | 
| 647 | 169k |         return (FIDO_OK); | 
| 648 | 170k | } | 
| 649 |  |  | 
| 650 |  | #ifdef USE_WINHELLO | 
| 651 |  | int | 
| 652 |  | fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) | 
| 653 |  | { | 
| 654 |  |         if (assert->appid != NULL) { | 
| 655 |  |                 free(assert->appid); | 
| 656 |  |                 assert->appid = NULL; | 
| 657 |  |         } | 
| 658 |  |  | 
| 659 |  |         if (id == NULL) | 
| 660 |  |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 661 |  |  | 
| 662 |  |         if ((assert->appid = strdup(id)) == NULL) | 
| 663 |  |                 return (FIDO_ERR_INTERNAL); | 
| 664 |  |  | 
| 665 |  |         return (FIDO_OK); | 
| 666 |  | } | 
| 667 |  | #else | 
| 668 |  | int | 
| 669 |  | fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) | 
| 670 | 0 | { | 
| 671 | 0 |         (void)assert; | 
| 672 | 0 |         (void)id; | 
| 673 |  | 
 | 
| 674 | 0 |         return (FIDO_ERR_UNSUPPORTED_EXTENSION); | 
| 675 | 0 | } | 
| 676 |  | #endif /* USE_WINHELLO */ | 
| 677 |  |  | 
| 678 |  | int | 
| 679 |  | fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr, | 
| 680 |  |     size_t len) | 
| 681 | 320k | { | 
| 682 | 320k |         fido_blob_t      id; | 
| 683 | 320k |         fido_blob_t     *list_ptr; | 
| 684 | 320k |         int              r; | 
| 685 |  |  | 
| 686 | 320k |         memset(&id, 0, sizeof(id)); | 
| 687 |  |  | 
| 688 | 320k |         if (assert->allow_list.len == SIZE_MAX) { | 
| 689 | 0 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 690 | 0 |                 goto fail; | 
| 691 | 0 |         } | 
| 692 |  |  | 
| 693 | 320k |         if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr = | 
| 694 | 318k |             recallocarray(assert->allow_list.ptr, assert->allow_list.len, | 
| 695 | 318k |             assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) { | 
| 696 | 2.72k |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 697 | 2.72k |                 goto fail; | 
| 698 | 2.72k |         } | 
| 699 |  |  | 
| 700 | 317k |         list_ptr[assert->allow_list.len++] = id; | 
| 701 | 317k |         assert->allow_list.ptr = list_ptr; | 
| 702 |  |  | 
| 703 | 317k |         return (FIDO_OK); | 
| 704 | 2.72k | fail: | 
| 705 | 2.72k |         free(id.ptr); | 
| 706 |  |  | 
| 707 | 2.72k |         return (r); | 
| 708 | 320k | } | 
| 709 |  |  | 
| 710 |  | int | 
| 711 |  | fido_assert_empty_allow_list(fido_assert_t *assert) | 
| 712 | 166k | { | 
| 713 | 166k |         fido_free_blob_array(&assert->allow_list); | 
| 714 | 166k |         memset(&assert->allow_list, 0, sizeof(assert->allow_list)); | 
| 715 |  |  | 
| 716 | 166k |         return (FIDO_OK); | 
| 717 | 166k | } | 
| 718 |  |  | 
| 719 |  | int | 
| 720 |  | fido_assert_set_extensions(fido_assert_t *assert, int ext) | 
| 721 | 161k | { | 
| 722 | 161k |         if (ext == 0) | 
| 723 | 9.78k |                 assert->ext.mask = 0; | 
| 724 | 152k |         else { | 
| 725 | 152k |                 if ((ext & FIDO_EXT_ASSERT_MASK) != ext) | 
| 726 | 145k |                         return (FIDO_ERR_INVALID_ARGUMENT); | 
| 727 | 6.48k |                 assert->ext.mask |= ext; | 
| 728 | 6.48k |         } | 
| 729 |  |  | 
| 730 | 16.2k |         return (FIDO_OK); | 
| 731 | 161k | } | 
| 732 |  |  | 
| 733 |  | int | 
| 734 |  | fido_assert_set_options(fido_assert_t *assert, bool up, bool uv) | 
| 735 | 0 | { | 
| 736 | 0 |         assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; | 
| 737 | 0 |         assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; | 
| 738 |  | 
 | 
| 739 | 0 |         return (FIDO_OK); | 
| 740 | 0 | } | 
| 741 |  |  | 
| 742 |  | int | 
| 743 |  | fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) | 
| 744 | 91.8k | { | 
| 745 | 91.8k |         assert->up = up; | 
| 746 |  |  | 
| 747 | 91.8k |         return (FIDO_OK); | 
| 748 | 91.8k | } | 
| 749 |  |  | 
| 750 |  | int | 
| 751 |  | fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) | 
| 752 | 6.97k | { | 
| 753 | 6.97k |         assert->uv = uv; | 
| 754 |  |  | 
| 755 | 6.97k |         return (FIDO_OK); | 
| 756 | 6.97k | } | 
| 757 |  |  | 
| 758 |  | const unsigned char * | 
| 759 |  | fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) | 
| 760 | 156k | { | 
| 761 | 156k |         return (assert->cdh.ptr); | 
| 762 | 156k | } | 
| 763 |  |  | 
| 764 |  | size_t | 
| 765 |  | fido_assert_clientdata_hash_len(const fido_assert_t *assert) | 
| 766 | 156k | { | 
| 767 | 156k |         return (assert->cdh.len); | 
| 768 | 156k | } | 
| 769 |  |  | 
| 770 |  | fido_assert_t * | 
| 771 |  | fido_assert_new(void) | 
| 772 | 167k | { | 
| 773 | 167k |         return (calloc(1, sizeof(fido_assert_t))); | 
| 774 | 167k | } | 
| 775 |  |  | 
| 776 |  | void | 
| 777 |  | fido_assert_reset_tx(fido_assert_t *assert) | 
| 778 | 166k | { | 
| 779 | 166k |         free(assert->rp_id); | 
| 780 | 166k |         free(assert->appid); | 
| 781 | 166k |         fido_blob_reset(&assert->cd); | 
| 782 | 166k |         fido_blob_reset(&assert->cdh); | 
| 783 | 166k |         fido_blob_reset(&assert->ext.hmac_salt); | 
| 784 | 166k |         fido_assert_empty_allow_list(assert); | 
| 785 | 166k |         memset(&assert->ext, 0, sizeof(assert->ext)); | 
| 786 | 166k |         assert->rp_id = NULL; | 
| 787 | 166k |         assert->appid = NULL; | 
| 788 | 166k |         assert->up = FIDO_OPT_OMIT; | 
| 789 | 166k |         assert->uv = FIDO_OPT_OMIT; | 
| 790 | 166k | } | 
| 791 |  |  | 
| 792 |  | static void | 
| 793 |  | fido_assert_reset_extattr(fido_assert_extattr_t *ext) | 
| 794 | 316k | { | 
| 795 | 316k |         fido_blob_reset(&ext->hmac_secret_enc); | 
| 796 | 316k |         fido_blob_reset(&ext->blob); | 
| 797 | 316k |         memset(ext, 0, sizeof(*ext)); | 
| 798 | 316k | } | 
| 799 |  |  | 
| 800 |  | void | 
| 801 |  | fido_assert_reset_rx(fido_assert_t *assert) | 
| 802 | 169k | { | 
| 803 | 472k |         for (size_t i = 0; i < assert->stmt_cnt; i++) { | 
| 804 | 303k |                 free(assert->stmt[i].user.icon); | 
| 805 | 303k |                 free(assert->stmt[i].user.name); | 
| 806 | 303k |                 free(assert->stmt[i].user.display_name); | 
| 807 | 303k |                 fido_blob_reset(&assert->stmt[i].user.id); | 
| 808 | 303k |                 fido_blob_reset(&assert->stmt[i].id); | 
| 809 | 303k |                 fido_blob_reset(&assert->stmt[i].hmac_secret); | 
| 810 | 303k |                 fido_blob_reset(&assert->stmt[i].authdata_cbor); | 
| 811 | 303k |                 fido_blob_reset(&assert->stmt[i].authdata_raw); | 
| 812 | 303k |                 fido_blob_reset(&assert->stmt[i].largeblob_key); | 
| 813 | 303k |                 fido_blob_reset(&assert->stmt[i].sig); | 
| 814 | 303k |                 fido_assert_reset_extattr(&assert->stmt[i].authdata_ext); | 
| 815 | 303k |                 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i])); | 
| 816 | 303k |         } | 
| 817 | 169k |         free(assert->stmt); | 
| 818 | 169k |         assert->stmt = NULL; | 
| 819 | 169k |         assert->stmt_len = 0; | 
| 820 | 169k |         assert->stmt_cnt = 0; | 
| 821 | 169k | } | 
| 822 |  |  | 
| 823 |  | void | 
| 824 |  | fido_assert_free(fido_assert_t **assert_p) | 
| 825 | 167k | { | 
| 826 | 167k |         fido_assert_t *assert; | 
| 827 |  |  | 
| 828 | 167k |         if (assert_p == NULL || (assert = *assert_p) == NULL) | 
| 829 | 103 |                 return; | 
| 830 | 166k |         fido_assert_reset_tx(assert); | 
| 831 | 166k |         fido_assert_reset_rx(assert); | 
| 832 | 166k |         free(assert); | 
| 833 | 166k |         *assert_p = NULL; | 
| 834 | 166k | } | 
| 835 |  |  | 
| 836 |  | size_t | 
| 837 |  | fido_assert_count(const fido_assert_t *assert) | 
| 838 | 167k | { | 
| 839 | 167k |         return (assert->stmt_len); | 
| 840 | 167k | } | 
| 841 |  |  | 
| 842 |  | const char * | 
| 843 |  | fido_assert_rp_id(const fido_assert_t *assert) | 
| 844 | 156k | { | 
| 845 | 156k |         return (assert->rp_id); | 
| 846 | 156k | } | 
| 847 |  |  | 
| 848 |  | uint8_t | 
| 849 |  | fido_assert_flags(const fido_assert_t *assert, size_t idx) | 
| 850 | 156k | { | 
| 851 | 156k |         if (idx >= assert->stmt_len) | 
| 852 | 11.0k |                 return (0); | 
| 853 |  |  | 
| 854 | 145k |         return (assert->stmt[idx].authdata.flags); | 
| 855 | 156k | } | 
| 856 |  |  | 
| 857 |  | uint32_t | 
| 858 |  | fido_assert_sigcount(const fido_assert_t *assert, size_t idx) | 
| 859 | 156k | { | 
| 860 | 156k |         if (idx >= assert->stmt_len) | 
| 861 | 11.0k |                 return (0); | 
| 862 |  |  | 
| 863 | 145k |         return (assert->stmt[idx].authdata.sigcount); | 
| 864 | 156k | } | 
| 865 |  |  | 
| 866 |  | const unsigned char * | 
| 867 |  | fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx) | 
| 868 | 156k | { | 
| 869 | 156k |         if (idx >= assert->stmt_len) | 
| 870 | 11.0k |                 return (NULL); | 
| 871 |  |  | 
| 872 | 145k |         return (assert->stmt[idx].authdata_cbor.ptr); | 
| 873 | 156k | } | 
| 874 |  |  | 
| 875 |  | size_t | 
| 876 |  | fido_assert_authdata_len(const fido_assert_t *assert, size_t idx) | 
| 877 | 156k | { | 
| 878 | 156k |         if (idx >= assert->stmt_len) | 
| 879 | 11.0k |                 return (0); | 
| 880 |  |  | 
| 881 | 145k |         return (assert->stmt[idx].authdata_cbor.len); | 
| 882 | 156k | } | 
| 883 |  |  | 
| 884 |  | const unsigned char * | 
| 885 |  | fido_assert_authdata_raw_ptr(const fido_assert_t *assert, size_t idx) | 
| 886 | 156k | { | 
| 887 | 156k |         if (idx >= assert->stmt_len) | 
| 888 | 11.0k |                 return (NULL); | 
| 889 |  |  | 
| 890 | 145k |         return (assert->stmt[idx].authdata_raw.ptr); | 
| 891 | 156k | } | 
| 892 |  |  | 
| 893 |  | size_t | 
| 894 |  | fido_assert_authdata_raw_len(const fido_assert_t *assert, size_t idx) | 
| 895 | 156k | { | 
| 896 | 156k |         if (idx >= assert->stmt_len) | 
| 897 | 11.0k |                 return (0); | 
| 898 |  |  | 
| 899 | 145k |         return (assert->stmt[idx].authdata_raw.len); | 
| 900 | 156k | } | 
| 901 |  |  | 
| 902 |  | const unsigned char * | 
| 903 |  | fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx) | 
| 904 | 156k | { | 
| 905 | 156k |         if (idx >= assert->stmt_len) | 
| 906 | 11.0k |                 return (NULL); | 
| 907 |  |  | 
| 908 | 145k |         return (assert->stmt[idx].sig.ptr); | 
| 909 | 156k | } | 
| 910 |  |  | 
| 911 |  | size_t | 
| 912 |  | fido_assert_sig_len(const fido_assert_t *assert, size_t idx) | 
| 913 | 156k | { | 
| 914 | 156k |         if (idx >= assert->stmt_len) | 
| 915 | 11.0k |                 return (0); | 
| 916 |  |  | 
| 917 | 145k |         return (assert->stmt[idx].sig.len); | 
| 918 | 156k | } | 
| 919 |  |  | 
| 920 |  | const unsigned char * | 
| 921 |  | fido_assert_id_ptr(const fido_assert_t *assert, size_t idx) | 
| 922 | 156k | { | 
| 923 | 156k |         if (idx >= assert->stmt_len) | 
| 924 | 11.0k |                 return (NULL); | 
| 925 |  |  | 
| 926 | 145k |         return (assert->stmt[idx].id.ptr); | 
| 927 | 156k | } | 
| 928 |  |  | 
| 929 |  | size_t | 
| 930 |  | fido_assert_id_len(const fido_assert_t *assert, size_t idx) | 
| 931 | 156k | { | 
| 932 | 156k |         if (idx >= assert->stmt_len) | 
| 933 | 11.0k |                 return (0); | 
| 934 |  |  | 
| 935 | 145k |         return (assert->stmt[idx].id.len); | 
| 936 | 156k | } | 
| 937 |  |  | 
| 938 |  | const unsigned char * | 
| 939 |  | fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx) | 
| 940 | 156k | { | 
| 941 | 156k |         if (idx >= assert->stmt_len) | 
| 942 | 11.0k |                 return (NULL); | 
| 943 |  |  | 
| 944 | 145k |         return (assert->stmt[idx].user.id.ptr); | 
| 945 | 156k | } | 
| 946 |  |  | 
| 947 |  | size_t | 
| 948 |  | fido_assert_user_id_len(const fido_assert_t *assert, size_t idx) | 
| 949 | 156k | { | 
| 950 | 156k |         if (idx >= assert->stmt_len) | 
| 951 | 11.0k |                 return (0); | 
| 952 |  |  | 
| 953 | 145k |         return (assert->stmt[idx].user.id.len); | 
| 954 | 156k | } | 
| 955 |  |  | 
| 956 |  | const char * | 
| 957 |  | fido_assert_user_icon(const fido_assert_t *assert, size_t idx) | 
| 958 | 156k | { | 
| 959 | 156k |         if (idx >= assert->stmt_len) | 
| 960 | 11.0k |                 return (NULL); | 
| 961 |  |  | 
| 962 | 145k |         return (assert->stmt[idx].user.icon); | 
| 963 | 156k | } | 
| 964 |  |  | 
| 965 |  | const char * | 
| 966 |  | fido_assert_user_name(const fido_assert_t *assert, size_t idx) | 
| 967 | 156k | { | 
| 968 | 156k |         if (idx >= assert->stmt_len) | 
| 969 | 11.0k |                 return (NULL); | 
| 970 |  |  | 
| 971 | 145k |         return (assert->stmt[idx].user.name); | 
| 972 | 156k | } | 
| 973 |  |  | 
| 974 |  | const char * | 
| 975 |  | fido_assert_user_display_name(const fido_assert_t *assert, size_t idx) | 
| 976 | 156k | { | 
| 977 | 156k |         if (idx >= assert->stmt_len) | 
| 978 | 11.0k |                 return (NULL); | 
| 979 |  |  | 
| 980 | 145k |         return (assert->stmt[idx].user.display_name); | 
| 981 | 156k | } | 
| 982 |  |  | 
| 983 |  | const unsigned char * | 
| 984 |  | fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx) | 
| 985 | 156k | { | 
| 986 | 156k |         if (idx >= assert->stmt_len) | 
| 987 | 11.0k |                 return (NULL); | 
| 988 |  |  | 
| 989 | 145k |         return (assert->stmt[idx].hmac_secret.ptr); | 
| 990 | 156k | } | 
| 991 |  |  | 
| 992 |  | size_t | 
| 993 |  | fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx) | 
| 994 | 156k | { | 
| 995 | 156k |         if (idx >= assert->stmt_len) | 
| 996 | 11.0k |                 return (0); | 
| 997 |  |  | 
| 998 | 145k |         return (assert->stmt[idx].hmac_secret.len); | 
| 999 | 156k | } | 
| 1000 |  |  | 
| 1001 |  | const unsigned char * | 
| 1002 |  | fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx) | 
| 1003 | 156k | { | 
| 1004 | 156k |         if (idx >= assert->stmt_len) | 
| 1005 | 11.0k |                 return (NULL); | 
| 1006 |  |  | 
| 1007 | 145k |         return (assert->stmt[idx].largeblob_key.ptr); | 
| 1008 | 156k | } | 
| 1009 |  |  | 
| 1010 |  | size_t | 
| 1011 |  | fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx) | 
| 1012 | 156k | { | 
| 1013 | 156k |         if (idx >= assert->stmt_len) | 
| 1014 | 11.0k |                 return (0); | 
| 1015 |  |  | 
| 1016 | 145k |         return (assert->stmt[idx].largeblob_key.len); | 
| 1017 | 156k | } | 
| 1018 |  |  | 
| 1019 |  | const unsigned char * | 
| 1020 |  | fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx) | 
| 1021 | 156k | { | 
| 1022 | 156k |         if (idx >= assert->stmt_len) | 
| 1023 | 11.0k |                 return (NULL); | 
| 1024 |  |  | 
| 1025 | 145k |         return (assert->stmt[idx].authdata_ext.blob.ptr); | 
| 1026 | 156k | } | 
| 1027 |  |  | 
| 1028 |  | size_t | 
| 1029 |  | fido_assert_blob_len(const fido_assert_t *assert, size_t idx) | 
| 1030 | 156k | { | 
| 1031 | 156k |         if (idx >= assert->stmt_len) | 
| 1032 | 11.0k |                 return (0); | 
| 1033 |  |  | 
| 1034 | 145k |         return (assert->stmt[idx].authdata_ext.blob.len); | 
| 1035 | 156k | } | 
| 1036 |  |  | 
| 1037 |  | static void | 
| 1038 |  | fido_assert_clean_authdata(fido_assert_stmt *stmt) | 
| 1039 | 13.4k | { | 
| 1040 | 13.4k |         fido_blob_reset(&stmt->authdata_cbor); | 
| 1041 | 13.4k |         fido_blob_reset(&stmt->authdata_raw); | 
| 1042 | 13.4k |         fido_assert_reset_extattr(&stmt->authdata_ext); | 
| 1043 | 13.4k |         memset(&stmt->authdata, 0, sizeof(stmt->authdata)); | 
| 1044 | 13.4k | } | 
| 1045 |  |  | 
| 1046 |  | int | 
| 1047 |  | fido_assert_set_authdata(fido_assert_t *assert, size_t idx, | 
| 1048 |  |     const unsigned char *ptr, size_t len) | 
| 1049 | 313k | { | 
| 1050 | 313k |         cbor_item_t             *item = NULL; | 
| 1051 | 313k |         fido_assert_stmt        *stmt = NULL; | 
| 1052 | 313k |         struct cbor_load_result  cbor; | 
| 1053 | 313k |         int                      r; | 
| 1054 |  |  | 
| 1055 | 313k |         if (idx >= assert->stmt_len || ptr == NULL || len == 0) | 
| 1056 | 303k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 1057 |  |  | 
| 1058 | 10.4k |         stmt = &assert->stmt[idx]; | 
| 1059 | 10.4k |         fido_assert_clean_authdata(stmt); | 
| 1060 |  |  | 
| 1061 | 10.4k |         if ((item = cbor_load(ptr, len, &cbor)) == NULL) { | 
| 1062 | 121 |                 fido_log_debug("%s: cbor_load", __func__); | 
| 1063 | 121 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 1064 | 121 |                 goto fail; | 
| 1065 | 121 |         } | 
| 1066 |  |  | 
| 1067 | 10.2k |         if (fido_blob_decode(item, &stmt->authdata_raw) < 0) { | 
| 1068 | 108 |             fido_log_debug("%s: fido_blob_decode", __func__); | 
| 1069 | 108 |             r = FIDO_ERR_INTERNAL; | 
| 1070 | 108 |             goto fail; | 
| 1071 | 108 |         } | 
| 1072 |  |  | 
| 1073 | 10.1k |         if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, | 
| 1074 | 10.1k |             &stmt->authdata, &stmt->authdata_ext) < 0) { | 
| 1075 | 791 |                 fido_log_debug("%s: cbor_decode_assert_authdata", __func__); | 
| 1076 | 791 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 1077 | 791 |                 goto fail; | 
| 1078 | 791 |         } | 
| 1079 |  |  | 
| 1080 | 9.39k |         r = FIDO_OK; | 
| 1081 | 10.4k | fail: | 
| 1082 | 10.4k |         if (item != NULL) | 
| 1083 | 10.2k |                 cbor_decref(&item); | 
| 1084 |  |  | 
| 1085 | 10.4k |         if (r != FIDO_OK) | 
| 1086 | 1.02k |                 fido_assert_clean_authdata(stmt); | 
| 1087 |  |  | 
| 1088 | 10.4k |         return (r); | 
| 1089 | 9.39k | } | 
| 1090 |  |  | 
| 1091 |  | int | 
| 1092 |  | fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx, | 
| 1093 |  |     const unsigned char *ptr, size_t len) | 
| 1094 | 304k | { | 
| 1095 | 304k |         cbor_item_t             *item = NULL; | 
| 1096 | 304k |         fido_assert_stmt        *stmt = NULL; | 
| 1097 | 304k |         int                      r; | 
| 1098 |  |  | 
| 1099 | 304k |         if (idx >= assert->stmt_len || ptr == NULL || len == 0) | 
| 1100 | 303k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 1101 |  |  | 
| 1102 | 1.00k |         stmt = &assert->stmt[idx]; | 
| 1103 | 1.00k |         fido_assert_clean_authdata(stmt); | 
| 1104 |  |  | 
| 1105 | 1.00k |         if (fido_blob_set(&stmt->authdata_raw, ptr, len) < 0) { | 
| 1106 | 5 |                 fido_log_debug("%s: fido_blob_set", __func__); | 
| 1107 | 5 |                 r = FIDO_ERR_INTERNAL; | 
| 1108 | 5 |                 goto fail; | 
| 1109 | 5 |         } | 
| 1110 |  |  | 
| 1111 | 995 |         if ((item = cbor_build_bytestring(ptr, len)) == NULL) { | 
| 1112 | 7 |                 fido_log_debug("%s: cbor_build_bytestring", __func__); | 
| 1113 | 7 |                 r = FIDO_ERR_INTERNAL; | 
| 1114 | 7 |                 goto fail; | 
| 1115 | 7 |         } | 
| 1116 |  |  | 
| 1117 | 988 |         if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, | 
| 1118 | 988 |             &stmt->authdata, &stmt->authdata_ext) < 0) { | 
| 1119 | 956 |                 fido_log_debug("%s: cbor_decode_assert_authdata", __func__); | 
| 1120 | 956 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 1121 | 956 |                 goto fail; | 
| 1122 | 956 |         } | 
| 1123 |  |  | 
| 1124 | 32 |         r = FIDO_OK; | 
| 1125 | 1.00k | fail: | 
| 1126 | 1.00k |         if (item != NULL) | 
| 1127 | 988 |                 cbor_decref(&item); | 
| 1128 |  |  | 
| 1129 | 1.00k |         if (r != FIDO_OK) | 
| 1130 | 968 |                 fido_assert_clean_authdata(stmt); | 
| 1131 |  |  | 
| 1132 | 1.00k |         return (r); | 
| 1133 | 32 | } | 
| 1134 |  |  | 
| 1135 |  | int | 
| 1136 |  | fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr, | 
| 1137 |  |     size_t len) | 
| 1138 | 313k | { | 
| 1139 | 313k |         if (idx >= a->stmt_len || ptr == NULL || len == 0) | 
| 1140 | 303k |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 1141 | 10.1k |         if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0) | 
| 1142 | 60 |                 return (FIDO_ERR_INTERNAL); | 
| 1143 |  |  | 
| 1144 | 10.0k |         return (FIDO_OK); | 
| 1145 | 10.1k | } | 
| 1146 |  |  | 
| 1147 |  | /* XXX shrinking leaks memory; fortunately that shouldn't happen */ | 
| 1148 |  | int | 
| 1149 |  | fido_assert_set_count(fido_assert_t *assert, size_t n) | 
| 1150 | 158k | { | 
| 1151 | 158k |         void *new_stmt; | 
| 1152 |  |  | 
| 1153 | 158k | #ifdef FIDO_FUZZ | 
| 1154 | 158k |         if (n > UINT8_MAX) { | 
| 1155 | 112 |                 fido_log_debug("%s: n > UINT8_MAX", __func__); | 
| 1156 | 112 |                 return (FIDO_ERR_INTERNAL); | 
| 1157 | 112 |         } | 
| 1158 | 158k | #endif | 
| 1159 |  |  | 
| 1160 | 158k |         new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n, | 
| 1161 | 158k |             sizeof(fido_assert_stmt)); | 
| 1162 | 158k |         if (new_stmt == NULL) | 
| 1163 | 411 |                 return (FIDO_ERR_INTERNAL); | 
| 1164 |  |  | 
| 1165 | 158k |         assert->stmt = new_stmt; | 
| 1166 | 158k |         assert->stmt_cnt = n; | 
| 1167 | 158k |         assert->stmt_len = n; | 
| 1168 |  |  | 
| 1169 | 158k |         return (FIDO_OK); | 
| 1170 | 158k | } |