| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* | 
| 2 |  |  * Copyright (c) 2019-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/sha.h> | 
| 9 |  |  | 
| 10 |  | #include "fido.h" | 
| 11 |  | #include "fido/credman.h" | 
| 12 |  | #include "fido/es256.h" | 
| 13 |  |  | 
| 14 | 535 | #define CMD_CRED_METADATA       0x01 | 
| 15 | 600 | #define CMD_RP_BEGIN            0x02 | 
| 16 | 496 | #define CMD_RP_NEXT             0x03 | 
| 17 | 2.43k | #define CMD_RK_BEGIN            0x04 | 
| 18 | 1.06k | #define CMD_RK_NEXT             0x05 | 
| 19 | 3.05k | #define CMD_DELETE_CRED         0x06 | 
| 20 | 3.15k | #define CMD_UPDATE_CRED         0x07 | 
| 21 |  |  | 
| 22 |  | static int | 
| 23 |  | credman_grow_array(void **ptr, size_t *n_alloc, const size_t *n_rx, size_t n, | 
| 24 |  |     size_t size) | 
| 25 | 1.04k | { | 
| 26 | 1.04k |         void *new_ptr; | 
| 27 |  |  | 
| 28 | 1.04k | #ifdef FIDO_FUZZ | 
| 29 | 1.04k |         if (n > UINT8_MAX) { | 
| 30 | 188 |                 fido_log_debug("%s: n > UINT8_MAX", __func__); | 
| 31 | 188 |                 return (-1); | 
| 32 | 188 |         } | 
| 33 | 859 | #endif | 
| 34 |  |  | 
| 35 | 859 |         if (n < *n_alloc) | 
| 36 | 0 |                 return (0); | 
| 37 |  |  | 
| 38 |  |         /* sanity check */ | 
| 39 | 859 |         if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) { | 
| 40 | 0 |                 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n, | 
| 41 | 0 |                     *n_rx, *n_alloc); | 
| 42 | 0 |                 return (-1); | 
| 43 | 0 |         } | 
| 44 |  |  | 
| 45 | 859 |         if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL) | 
| 46 | 2 |                 return (-1); | 
| 47 |  |  | 
| 48 | 857 |         *ptr = new_ptr; | 
| 49 | 857 |         *n_alloc = n; | 
| 50 |  |  | 
| 51 | 857 |         return (0); | 
| 52 | 859 | } | 
| 53 |  |  | 
| 54 |  | static int | 
| 55 |  | credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param, | 
| 56 |  |     fido_blob_t *hmac_data) | 
| 57 | 3.24k | { | 
| 58 | 3.24k |         cbor_item_t *param_cbor[3]; | 
| 59 | 3.24k |         const fido_cred_t *cred; | 
| 60 | 3.24k |         size_t n; | 
| 61 | 3.24k |         int ok = -1; | 
| 62 |  |  | 
| 63 | 3.24k |         memset(¶m_cbor, 0, sizeof(param_cbor)); | 
| 64 |  |  | 
| 65 | 3.24k |         if (body == NULL) | 
| 66 | 627 |                 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd))); | 
| 67 |  |  | 
| 68 | 2.62k |         switch (cmd) { | 
| 69 | 1.08k |         case CMD_RK_BEGIN: | 
| 70 | 1.08k |                 n = 1; | 
| 71 | 1.08k |                 if ((param_cbor[0] = fido_blob_encode(body)) == NULL) { | 
| 72 | 2 |                         fido_log_debug("%s: cbor encode", __func__); | 
| 73 | 2 |                         goto fail; | 
| 74 | 2 |                 } | 
| 75 | 1.08k |                 break; | 
| 76 | 1.08k |         case CMD_DELETE_CRED: | 
| 77 | 735 |                 n = 2; | 
| 78 | 735 |                 if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) { | 
| 79 | 15 |                         fido_log_debug("%s: cbor encode", __func__); | 
| 80 | 15 |                         goto fail; | 
| 81 | 15 |                 } | 
| 82 | 720 |                 break; | 
| 83 | 797 |         case CMD_UPDATE_CRED: | 
| 84 | 797 |                 n = 3; | 
| 85 | 797 |                 cred = body; | 
| 86 | 797 |                 param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id); | 
| 87 | 797 |                 param_cbor[2] = cbor_encode_user_entity(&cred->user); | 
| 88 | 797 |                 if (param_cbor[1] == NULL || param_cbor[2] == NULL) { | 
| 89 | 25 |                         fido_log_debug("%s: cbor encode", __func__); | 
| 90 | 25 |                         goto fail; | 
| 91 | 25 |                 } | 
| 92 | 772 |                 break; | 
| 93 | 772 |         default: | 
| 94 | 0 |                 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); | 
| 95 | 0 |                 return (-1); | 
| 96 | 2.62k |         } | 
| 97 |  |  | 
| 98 | 2.57k |         if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) { | 
| 99 | 13 |                 fido_log_debug("%s: cbor_flatten_vector", __func__); | 
| 100 | 13 |                 goto fail; | 
| 101 | 13 |         } | 
| 102 | 2.56k |         if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) { | 
| 103 | 23 |                 fido_log_debug("%s: cbor_build_frame", __func__); | 
| 104 | 23 |                 goto fail; | 
| 105 | 23 |         } | 
| 106 |  |  | 
| 107 | 2.54k |         ok = 0; | 
| 108 | 2.62k | fail: | 
| 109 | 2.62k |         cbor_vector_free(param_cbor, nitems(param_cbor)); | 
| 110 |  |  | 
| 111 | 2.62k |         return (ok); | 
| 112 | 2.54k | } | 
| 113 |  |  | 
| 114 |  | static uint8_t | 
| 115 |  | credman_get_cmd(const fido_dev_t *dev) | 
| 116 | 8.72k | { | 
| 117 | 8.72k |         if (dev->flags & FIDO_DEV_CREDMAN) | 
| 118 | 10 |                 return (CTAP_CBOR_CRED_MGMT); | 
| 119 |  |  | 
| 120 | 8.71k |         return (CTAP_CBOR_CRED_MGMT_PRE); | 
| 121 | 8.72k | } | 
| 122 |  |  | 
| 123 |  | static int | 
| 124 |  | credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin, | 
| 125 |  |     const char *rp_id, fido_opt_t uv, int *ms) | 
| 126 | 8.72k | { | 
| 127 | 8.72k |         fido_blob_t      f; | 
| 128 | 8.72k |         fido_blob_t     *ecdh = NULL; | 
| 129 | 8.72k |         fido_blob_t      hmac; | 
| 130 | 8.72k |         es256_pk_t      *pk = NULL; | 
| 131 | 8.72k |         cbor_item_t     *argv[4]; | 
| 132 | 8.72k |         const uint8_t    cmd = credman_get_cmd(dev); | 
| 133 | 8.72k |         int              r = FIDO_ERR_INTERNAL; | 
| 134 |  |  | 
| 135 | 8.72k |         memset(&f, 0, sizeof(f)); | 
| 136 | 8.72k |         memset(&hmac, 0, sizeof(hmac)); | 
| 137 | 8.72k |         memset(&argv, 0, sizeof(argv)); | 
| 138 |  |  | 
| 139 | 8.72k |         if (fido_dev_is_fido2(dev) == false) { | 
| 140 | 3.90k |                 fido_log_debug("%s: fido_dev_is_fido2", __func__); | 
| 141 | 3.90k |                 r = FIDO_ERR_INVALID_COMMAND; | 
| 142 | 3.90k |                 goto fail; | 
| 143 | 3.90k |         } | 
| 144 |  |  | 
| 145 |  |         /* subCommand */ | 
| 146 | 4.81k |         if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) { | 
| 147 | 8 |                 fido_log_debug("%s: cbor encode", __func__); | 
| 148 | 8 |                 goto fail; | 
| 149 | 8 |         } | 
| 150 |  |  | 
| 151 |  |         /* pinProtocol, pinAuth */ | 
| 152 | 4.80k |         if (pin != NULL || uv == FIDO_OPT_TRUE) { | 
| 153 | 3.24k |                 if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) { | 
| 154 | 79 |                         fido_log_debug("%s: credman_prepare_hmac", __func__); | 
| 155 | 79 |                         goto fail; | 
| 156 | 79 |                 } | 
| 157 | 3.16k |                 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { | 
| 158 | 1.44k |                         fido_log_debug("%s: fido_do_ecdh", __func__); | 
| 159 | 1.44k |                         goto fail; | 
| 160 | 1.44k |                 } | 
| 161 | 1.72k |                 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, | 
| 162 | 1.72k |                     rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) { | 
| 163 | 417 |                         fido_log_debug("%s: cbor_add_uv_params", __func__); | 
| 164 | 417 |                         goto fail; | 
| 165 | 417 |                 } | 
| 166 | 1.72k |         } | 
| 167 |  |  | 
| 168 |  |         /* framing and transmission */ | 
| 169 | 2.87k |         if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || | 
| 170 | 2.87k |             fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { | 
| 171 | 68 |                 fido_log_debug("%s: fido_tx", __func__); | 
| 172 | 68 |                 r = FIDO_ERR_TX; | 
| 173 | 68 |                 goto fail; | 
| 174 | 68 |         } | 
| 175 |  |  | 
| 176 | 2.80k |         r = FIDO_OK; | 
| 177 | 8.72k | fail: | 
| 178 | 8.72k |         es256_pk_free(&pk); | 
| 179 | 8.72k |         fido_blob_free(&ecdh); | 
| 180 | 8.72k |         cbor_vector_free(argv, nitems(argv)); | 
| 181 | 8.72k |         free(f.ptr); | 
| 182 | 8.72k |         free(hmac.ptr); | 
| 183 |  |  | 
| 184 | 8.72k |         return (r); | 
| 185 | 2.80k | } | 
| 186 |  |  | 
| 187 |  | static int | 
| 188 |  | credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, | 
| 189 |  |     void *arg) | 
| 190 | 59 | { | 
| 191 | 59 |         fido_credman_metadata_t *metadata = arg; | 
| 192 |  |  | 
| 193 | 59 |         if (cbor_isa_uint(key) == false || | 
| 194 | 59 |             cbor_int_get_width(key) != CBOR_INT_8) { | 
| 195 | 21 |                 fido_log_debug("%s: cbor type", __func__); | 
| 196 | 21 |                 return (0); /* ignore */ | 
| 197 | 21 |         } | 
| 198 |  |  | 
| 199 | 38 |         switch (cbor_get_uint8(key)) { | 
| 200 | 6 |         case 1: | 
| 201 | 6 |                 return (cbor_decode_uint64(val, &metadata->rk_existing)); | 
| 202 | 6 |         case 2: | 
| 203 | 6 |                 return (cbor_decode_uint64(val, &metadata->rk_remaining)); | 
| 204 | 26 |         default: | 
| 205 | 26 |                 fido_log_debug("%s: cbor type", __func__); | 
| 206 | 26 |                 return (0); /* ignore */ | 
| 207 | 38 |         } | 
| 208 | 38 | } | 
| 209 |  |  | 
| 210 |  | static int | 
| 211 |  | credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms) | 
| 212 | 36 | { | 
| 213 | 36 |         unsigned char   *msg; | 
| 214 | 36 |         int              msglen; | 
| 215 | 36 |         int              r; | 
| 216 |  |  | 
| 217 | 36 |         memset(metadata, 0, sizeof(*metadata)); | 
| 218 |  |  | 
| 219 | 36 |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 220 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 221 | 1 |                 goto out; | 
| 222 | 1 |         } | 
| 223 |  |  | 
| 224 | 35 |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 225 | 1 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 226 | 1 |                 r = FIDO_ERR_RX; | 
| 227 | 1 |                 goto out; | 
| 228 | 1 |         } | 
| 229 |  |  | 
| 230 | 34 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, metadata, | 
| 231 | 34 |             credman_parse_metadata)) != FIDO_OK) { | 
| 232 | 23 |                 fido_log_debug("%s: credman_parse_metadata", __func__); | 
| 233 | 23 |                 goto out; | 
| 234 | 23 |         } | 
| 235 |  |  | 
| 236 | 11 |         r = FIDO_OK; | 
| 237 | 36 | out: | 
| 238 | 36 |         freezero(msg, FIDO_MAXMSG); | 
| 239 |  |  | 
| 240 | 36 |         return (r); | 
| 241 | 11 | } | 
| 242 |  |  | 
| 243 |  | static int | 
| 244 |  | credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata, | 
| 245 |  |     const char *pin, int *ms) | 
| 246 | 535 | { | 
| 247 | 535 |         int r; | 
| 248 |  |  | 
| 249 | 535 |         if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL, | 
| 250 | 535 |             FIDO_OPT_TRUE, ms)) != FIDO_OK || | 
| 251 | 535 |             (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK) | 
| 252 | 524 |                 return (r); | 
| 253 |  |  | 
| 254 | 11 |         return (FIDO_OK); | 
| 255 | 535 | } | 
| 256 |  |  | 
| 257 |  | int | 
| 258 |  | fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, | 
| 259 |  |     const char *pin) | 
| 260 | 535 | { | 
| 261 | 535 |         int ms = dev->timeout_ms; | 
| 262 |  |  | 
| 263 | 535 |         return (credman_get_metadata_wait(dev, metadata, pin, &ms)); | 
| 264 | 535 | } | 
| 265 |  |  | 
| 266 |  | static int | 
| 267 |  | credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) | 
| 268 | 5.55k | { | 
| 269 | 5.55k |         fido_cred_t     *cred = arg; | 
| 270 | 5.55k |         uint64_t         prot; | 
| 271 |  |  | 
| 272 | 5.55k |         if (cbor_isa_uint(key) == false || | 
| 273 | 5.55k |             cbor_int_get_width(key) != CBOR_INT_8) { | 
| 274 | 91 |                 fido_log_debug("%s: cbor type", __func__); | 
| 275 | 91 |                 return (0); /* ignore */ | 
| 276 | 91 |         } | 
| 277 |  |  | 
| 278 | 5.46k |         switch (cbor_get_uint8(key)) { | 
| 279 | 1.26k |         case 6: | 
| 280 | 1.26k |                 return (cbor_decode_user(val, &cred->user)); | 
| 281 | 1.21k |         case 7: | 
| 282 | 1.21k |                 return (cbor_decode_cred_id(val, &cred->attcred.id)); | 
| 283 | 1.24k |         case 8: | 
| 284 | 1.24k |                 if (cbor_decode_pubkey(val, &cred->attcred.type, | 
| 285 | 1.24k |                     &cred->attcred.pubkey) < 0) | 
| 286 | 440 |                         return (-1); | 
| 287 | 804 |                 cred->type = cred->attcred.type; /* XXX */ | 
| 288 | 804 |                 return (0); | 
| 289 | 670 |         case 10: | 
| 290 | 670 |                 if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX || | 
| 291 | 670 |                     fido_cred_set_prot(cred, (int)prot) != FIDO_OK) | 
| 292 | 104 |                         return (-1); | 
| 293 | 566 |                 return (0); | 
| 294 | 4 |         case 11: | 
| 295 | 4 |                 return (fido_blob_decode(val, &cred->largeblob_key)); | 
| 296 | 1.07k |         default: | 
| 297 | 1.07k |                 fido_log_debug("%s: cbor type", __func__); | 
| 298 | 1.07k |                 return (0); /* ignore */ | 
| 299 | 5.46k |         } | 
| 300 | 5.46k | } | 
| 301 |  |  | 
| 302 |  | static void | 
| 303 |  | credman_reset_rk(fido_credman_rk_t *rk) | 
| 304 | 2.23k | { | 
| 305 | 14.9k |         for (size_t i = 0; i < rk->n_alloc; i++) { | 
| 306 | 12.7k |                 fido_cred_reset_tx(&rk->ptr[i]); | 
| 307 | 12.7k |                 fido_cred_reset_rx(&rk->ptr[i]); | 
| 308 | 12.7k |         } | 
| 309 |  |  | 
| 310 | 2.23k |         free(rk->ptr); | 
| 311 | 2.23k |         rk->ptr = NULL; | 
| 312 | 2.23k |         memset(rk, 0, sizeof(*rk)); | 
| 313 | 2.23k | } | 
| 314 |  |  | 
| 315 |  | static int | 
| 316 |  | credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, | 
| 317 |  |     void *arg) | 
| 318 | 4.11k | { | 
| 319 | 4.11k |         fido_credman_rk_t *rk = arg; | 
| 320 | 4.11k |         uint64_t n; | 
| 321 |  |  | 
| 322 |  |         /* totalCredentials */ | 
| 323 | 4.11k |         if (cbor_isa_uint(key) == false || | 
| 324 | 4.11k |             cbor_int_get_width(key) != CBOR_INT_8 || | 
| 325 | 4.11k |             cbor_get_uint8(key) != 9) { | 
| 326 | 3.27k |                 fido_log_debug("%s: cbor_type", __func__); | 
| 327 | 3.27k |                 return (0); /* ignore */ | 
| 328 | 3.27k |         } | 
| 329 |  |  | 
| 330 | 838 |         if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { | 
| 331 | 1 |                 fido_log_debug("%s: cbor_decode_uint64", __func__); | 
| 332 | 1 |                 return (-1); | 
| 333 | 1 |         } | 
| 334 |  |  | 
| 335 | 837 |         if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx, | 
| 336 | 837 |             (size_t)n, sizeof(*rk->ptr)) < 0) { | 
| 337 | 102 |                 fido_log_debug("%s: credman_grow_array", __func__); | 
| 338 | 102 |                 return (-1); | 
| 339 | 102 |         } | 
| 340 |  |  | 
| 341 | 735 |         return (0); | 
| 342 | 837 | } | 
| 343 |  |  | 
| 344 |  | static int | 
| 345 |  | credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) | 
| 346 | 888 | { | 
| 347 | 888 |         unsigned char   *msg; | 
| 348 | 888 |         int              msglen; | 
| 349 | 888 |         int              r; | 
| 350 |  |  | 
| 351 | 888 |         credman_reset_rk(rk); | 
| 352 |  |  | 
| 353 | 888 |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 354 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 355 | 1 |                 goto out; | 
| 356 | 1 |         } | 
| 357 |  |  | 
| 358 | 887 |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 359 | 9 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 360 | 9 |                 r = FIDO_ERR_RX; | 
| 361 | 9 |                 goto out; | 
| 362 | 9 |         } | 
| 363 |  |  | 
| 364 |  |         /* adjust as needed */ | 
| 365 | 878 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, rk, | 
| 366 | 878 |             credman_parse_rk_count)) != FIDO_OK) { | 
| 367 | 144 |                 fido_log_debug("%s: credman_parse_rk_count", __func__); | 
| 368 | 144 |                 goto out; | 
| 369 | 144 |         } | 
| 370 |  |  | 
| 371 | 734 |         if (rk->n_alloc == 0) { | 
| 372 | 4 |                 fido_log_debug("%s: n_alloc=0", __func__); | 
| 373 | 4 |                 r = FIDO_OK; | 
| 374 | 4 |                 goto out; | 
| 375 | 4 |         } | 
| 376 |  |  | 
| 377 |  |         /* parse the first rk */ | 
| 378 | 730 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[0], | 
| 379 | 730 |             credman_parse_rk)) != FIDO_OK) { | 
| 380 | 237 |                 fido_log_debug("%s: credman_parse_rk", __func__); | 
| 381 | 237 |                 goto out; | 
| 382 | 237 |         } | 
| 383 | 493 |         rk->n_rx = 1; | 
| 384 |  |  | 
| 385 | 493 |         r = FIDO_OK; | 
| 386 | 888 | out: | 
| 387 | 888 |         freezero(msg, FIDO_MAXMSG); | 
| 388 |  |  | 
| 389 | 888 |         return (r); | 
| 390 | 493 | } | 
| 391 |  |  | 
| 392 |  | static int | 
| 393 |  | credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) | 
| 394 | 1.05k | { | 
| 395 | 1.05k |         unsigned char   *msg; | 
| 396 | 1.05k |         int              msglen; | 
| 397 | 1.05k |         int              r; | 
| 398 |  |  | 
| 399 | 1.05k |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 400 | 2 |                 r = FIDO_ERR_INTERNAL; | 
| 401 | 2 |                 goto out; | 
| 402 | 2 |         } | 
| 403 |  |  | 
| 404 | 1.05k |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 405 | 78 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 406 | 78 |                 r = FIDO_ERR_RX; | 
| 407 | 78 |                 goto out; | 
| 408 | 78 |         } | 
| 409 |  |  | 
| 410 |  |         /* sanity check */ | 
| 411 | 979 |         if (rk->n_rx >= rk->n_alloc) { | 
| 412 | 0 |                 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx, | 
| 413 | 0 |                     rk->n_alloc); | 
| 414 | 0 |                 r = FIDO_ERR_INTERNAL; | 
| 415 | 0 |                 goto out; | 
| 416 | 0 |         } | 
| 417 |  |  | 
| 418 | 979 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[rk->n_rx], | 
| 419 | 979 |             credman_parse_rk)) != FIDO_OK) { | 
| 420 | 396 |                 fido_log_debug("%s: credman_parse_rk", __func__); | 
| 421 | 396 |                 goto out; | 
| 422 | 396 |         } | 
| 423 |  |  | 
| 424 | 583 |         r = FIDO_OK; | 
| 425 | 1.05k | out: | 
| 426 | 1.05k |         freezero(msg, FIDO_MAXMSG); | 
| 427 |  |  | 
| 428 | 1.05k |         return (r); | 
| 429 | 583 | } | 
| 430 |  |  | 
| 431 |  | static int | 
| 432 |  | credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk, | 
| 433 |  |     const char *pin, int *ms) | 
| 434 | 1.34k | { | 
| 435 | 1.34k |         fido_blob_t     rp_dgst; | 
| 436 | 1.34k |         uint8_t         dgst[SHA256_DIGEST_LENGTH]; | 
| 437 | 1.34k |         int             r; | 
| 438 |  |  | 
| 439 | 1.34k |         if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) { | 
| 440 | 2 |                 fido_log_debug("%s: sha256", __func__); | 
| 441 | 2 |                 return (FIDO_ERR_INTERNAL); | 
| 442 | 2 |         } | 
| 443 |  |  | 
| 444 | 1.34k |         rp_dgst.ptr = dgst; | 
| 445 | 1.34k |         rp_dgst.len = sizeof(dgst); | 
| 446 |  |  | 
| 447 | 1.34k |         if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id, | 
| 448 | 1.34k |             FIDO_OPT_TRUE, ms)) != FIDO_OK || | 
| 449 | 1.34k |             (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK) | 
| 450 | 848 |                 return (r); | 
| 451 |  |  | 
| 452 | 1.08k |         while (rk->n_rx < rk->n_alloc) { | 
| 453 | 1.06k |                 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL, | 
| 454 | 1.06k |                     FIDO_OPT_FALSE, ms)) != FIDO_OK || | 
| 455 | 1.06k |                     (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK) | 
| 456 | 485 |                         return (r); | 
| 457 | 583 |                 rk->n_rx++; | 
| 458 | 583 |         } | 
| 459 |  |  | 
| 460 | 12 |         return (FIDO_OK); | 
| 461 | 497 | } | 
| 462 |  |  | 
| 463 |  | int | 
| 464 |  | fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id, | 
| 465 |  |     fido_credman_rk_t *rk, const char *pin) | 
| 466 | 1.34k | { | 
| 467 | 1.34k |         int ms = dev->timeout_ms; | 
| 468 |  |  | 
| 469 | 1.34k |         return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms)); | 
| 470 | 1.34k | } | 
| 471 |  |  | 
| 472 |  | static int | 
| 473 |  | credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id, | 
| 474 |  |     size_t cred_id_len, const char *pin, int *ms) | 
| 475 | 2.33k | { | 
| 476 | 2.33k |         fido_blob_t cred; | 
| 477 | 2.33k |         int r; | 
| 478 |  |  | 
| 479 | 2.33k |         memset(&cred, 0, sizeof(cred)); | 
| 480 |  |  | 
| 481 | 2.33k |         if (fido_blob_set(&cred, cred_id, cred_id_len) < 0) | 
| 482 | 10 |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 483 |  |  | 
| 484 | 2.32k |         if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL, | 
| 485 | 2.32k |             FIDO_OPT_TRUE, ms)) != FIDO_OK || | 
| 486 | 2.32k |             (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) | 
| 487 | 2.31k |                 goto fail; | 
| 488 |  |  | 
| 489 | 11 |         r = FIDO_OK; | 
| 490 | 2.32k | fail: | 
| 491 | 2.32k |         free(cred.ptr); | 
| 492 |  |  | 
| 493 | 2.32k |         return (r); | 
| 494 | 11 | } | 
| 495 |  |  | 
| 496 |  | int | 
| 497 |  | fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id, | 
| 498 |  |     size_t cred_id_len, const char *pin) | 
| 499 | 2.33k | { | 
| 500 | 2.33k |         int ms = dev->timeout_ms; | 
| 501 |  |  | 
| 502 | 2.33k |         return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms)); | 
| 503 | 2.33k | } | 
| 504 |  |  | 
| 505 |  | static int | 
| 506 |  | credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg) | 
| 507 | 1.14k | { | 
| 508 | 1.14k |         struct fido_credman_single_rp *rp = arg; | 
| 509 |  |  | 
| 510 | 1.14k |         if (cbor_isa_uint(key) == false || | 
| 511 | 1.14k |             cbor_int_get_width(key) != CBOR_INT_8) { | 
| 512 | 195 |                 fido_log_debug("%s: cbor type", __func__); | 
| 513 | 195 |                 return (0); /* ignore */ | 
| 514 | 195 |         } | 
| 515 |  |  | 
| 516 | 954 |         switch (cbor_get_uint8(key)) { | 
| 517 | 417 |         case 3: | 
| 518 | 417 |                 return (cbor_decode_rp_entity(val, &rp->rp_entity)); | 
| 519 | 263 |         case 4: | 
| 520 | 263 |                 return (fido_blob_decode(val, &rp->rp_id_hash)); | 
| 521 | 274 |         default: | 
| 522 | 274 |                 fido_log_debug("%s: cbor type", __func__); | 
| 523 | 274 |                 return (0); /* ignore */ | 
| 524 | 954 |         } | 
| 525 | 954 | } | 
| 526 |  |  | 
| 527 |  | static void | 
| 528 |  | credman_reset_rp(fido_credman_rp_t *rp) | 
| 529 | 846 | { | 
| 530 | 5.68k |         for (size_t i = 0; i < rp->n_alloc; i++) { | 
| 531 | 4.83k |                 free(rp->ptr[i].rp_entity.id); | 
| 532 | 4.83k |                 free(rp->ptr[i].rp_entity.name); | 
| 533 | 4.83k |                 rp->ptr[i].rp_entity.id = NULL; | 
| 534 | 4.83k |                 rp->ptr[i].rp_entity.name = NULL; | 
| 535 | 4.83k |                 fido_blob_reset(&rp->ptr[i].rp_id_hash); | 
| 536 | 4.83k |         } | 
| 537 |  |  | 
| 538 | 846 |         free(rp->ptr); | 
| 539 | 846 |         rp->ptr = NULL; | 
| 540 | 846 |         memset(rp, 0, sizeof(*rp)); | 
| 541 | 846 | } | 
| 542 |  |  | 
| 543 |  | static int | 
| 544 |  | credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, | 
| 545 |  |     void *arg) | 
| 546 | 915 | { | 
| 547 | 915 |         fido_credman_rp_t *rp = arg; | 
| 548 | 915 |         uint64_t n; | 
| 549 |  |  | 
| 550 |  |         /* totalRPs */ | 
| 551 | 915 |         if (cbor_isa_uint(key) == false || | 
| 552 | 915 |             cbor_int_get_width(key) != CBOR_INT_8 || | 
| 553 | 915 |             cbor_get_uint8(key) != 5) { | 
| 554 | 704 |                 fido_log_debug("%s: cbor_type", __func__); | 
| 555 | 704 |                 return (0); /* ignore */ | 
| 556 | 704 |         } | 
| 557 |  |  | 
| 558 | 211 |         if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { | 
| 559 | 1 |                 fido_log_debug("%s: cbor_decode_uint64", __func__); | 
| 560 | 1 |                 return (-1); | 
| 561 | 1 |         } | 
| 562 |  |  | 
| 563 | 210 |         if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx, | 
| 564 | 210 |             (size_t)n, sizeof(*rp->ptr)) < 0) { | 
| 565 | 88 |                 fido_log_debug("%s: credman_grow_array", __func__); | 
| 566 | 88 |                 return (-1); | 
| 567 | 88 |         } | 
| 568 |  |  | 
| 569 | 122 |         return (0); | 
| 570 | 210 | } | 
| 571 |  |  | 
| 572 |  | static int | 
| 573 |  | credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) | 
| 574 | 246 | { | 
| 575 | 246 |         unsigned char   *msg; | 
| 576 | 246 |         int              msglen; | 
| 577 | 246 |         int              r; | 
| 578 |  |  | 
| 579 | 246 |         credman_reset_rp(rp); | 
| 580 |  |  | 
| 581 | 246 |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 582 | 2 |                 r = FIDO_ERR_INTERNAL; | 
| 583 | 2 |                 goto out; | 
| 584 | 2 |         } | 
| 585 |  |  | 
| 586 | 244 |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 587 | 4 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 588 | 4 |                 r = FIDO_ERR_RX; | 
| 589 | 4 |                 goto out; | 
| 590 | 4 |         } | 
| 591 |  |  | 
| 592 |  |         /* adjust as needed */ | 
| 593 | 240 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, rp, | 
| 594 | 240 |             credman_parse_rp_count)) != FIDO_OK) { | 
| 595 | 111 |                 fido_log_debug("%s: credman_parse_rp_count", __func__); | 
| 596 | 111 |                 goto out; | 
| 597 | 111 |         } | 
| 598 |  |  | 
| 599 | 129 |         if (rp->n_alloc == 0) { | 
| 600 | 9 |                 fido_log_debug("%s: n_alloc=0", __func__); | 
| 601 | 9 |                 r = FIDO_OK; | 
| 602 | 9 |                 goto out; | 
| 603 | 9 |         } | 
| 604 |  |  | 
| 605 |  |         /* parse the first rp */ | 
| 606 | 120 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[0], | 
| 607 | 120 |             credman_parse_rp)) != FIDO_OK) { | 
| 608 | 7 |                 fido_log_debug("%s: credman_parse_rp", __func__); | 
| 609 | 7 |                 goto out; | 
| 610 | 7 |         } | 
| 611 | 113 |         rp->n_rx = 1; | 
| 612 |  |  | 
| 613 | 113 |         r = FIDO_OK; | 
| 614 | 246 | out: | 
| 615 | 246 |         freezero(msg, FIDO_MAXMSG); | 
| 616 |  |  | 
| 617 | 246 |         return (r); | 
| 618 | 113 | } | 
| 619 |  |  | 
| 620 |  | static int | 
| 621 |  | credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) | 
| 622 | 485 | { | 
| 623 | 485 |         unsigned char   *msg; | 
| 624 | 485 |         int              msglen; | 
| 625 | 485 |         int              r; | 
| 626 |  |  | 
| 627 | 485 |         if ((msg = malloc(FIDO_MAXMSG)) == NULL) { | 
| 628 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 629 | 1 |                 goto out; | 
| 630 | 1 |         } | 
| 631 |  |  | 
| 632 | 484 |         if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { | 
| 633 | 60 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 634 | 60 |                 r = FIDO_ERR_RX; | 
| 635 | 60 |                 goto out; | 
| 636 | 60 |         } | 
| 637 |  |  | 
| 638 |  |         /* sanity check */ | 
| 639 | 424 |         if (rp->n_rx >= rp->n_alloc) { | 
| 640 | 0 |                 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx, | 
| 641 | 0 |                     rp->n_alloc); | 
| 642 | 0 |                 r = FIDO_ERR_INTERNAL; | 
| 643 | 0 |                 goto out; | 
| 644 | 0 |         } | 
| 645 |  |  | 
| 646 | 424 |         if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[rp->n_rx], | 
| 647 | 424 |             credman_parse_rp)) != FIDO_OK) { | 
| 648 | 33 |                 fido_log_debug("%s: credman_parse_rp", __func__); | 
| 649 | 33 |                 goto out; | 
| 650 | 33 |         } | 
| 651 |  |  | 
| 652 | 391 |         r = FIDO_OK; | 
| 653 | 485 | out: | 
| 654 | 485 |         freezero(msg, FIDO_MAXMSG); | 
| 655 |  |  | 
| 656 | 485 |         return (r); | 
| 657 | 391 | } | 
| 658 |  |  | 
| 659 |  | static int | 
| 660 |  | credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin, | 
| 661 |  |     int *ms) | 
| 662 | 600 | { | 
| 663 | 600 |         int r; | 
| 664 |  |  | 
| 665 | 600 |         if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL, | 
| 666 | 600 |             FIDO_OPT_TRUE, ms)) != FIDO_OK || | 
| 667 | 600 |             (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK) | 
| 668 | 478 |                 return (r); | 
| 669 |  |  | 
| 670 | 513 |         while (rp->n_rx < rp->n_alloc) { | 
| 671 | 496 |                 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL, | 
| 672 | 496 |                     FIDO_OPT_FALSE, ms)) != FIDO_OK || | 
| 673 | 496 |                     (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK) | 
| 674 | 105 |                         return (r); | 
| 675 | 391 |                 rp->n_rx++; | 
| 676 | 391 |         } | 
| 677 |  |  | 
| 678 | 17 |         return (FIDO_OK); | 
| 679 | 122 | } | 
| 680 |  |  | 
| 681 |  | int | 
| 682 |  | fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin) | 
| 683 | 600 | { | 
| 684 | 600 |         int ms = dev->timeout_ms; | 
| 685 |  |  | 
| 686 | 600 |         return (credman_get_rp_wait(dev, rp, pin, &ms)); | 
| 687 | 600 | } | 
| 688 |  |  | 
| 689 |  | static int | 
| 690 |  | credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, | 
| 691 |  |     int *ms) | 
| 692 | 2.35k | { | 
| 693 | 2.35k |         int r; | 
| 694 |  |  | 
| 695 | 2.35k |         if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL, | 
| 696 | 2.35k |             FIDO_OPT_TRUE, ms)) != FIDO_OK || | 
| 697 | 2.35k |             (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) | 
| 698 | 2.34k |                 return (r); | 
| 699 |  |  | 
| 700 | 8 |         return (FIDO_OK); | 
| 701 | 2.35k | } | 
| 702 |  |  | 
| 703 |  | int | 
| 704 |  | fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin) | 
| 705 | 2.35k | { | 
| 706 | 2.35k |         int ms = dev->timeout_ms; | 
| 707 |  |  | 
| 708 | 2.35k |         return (credman_set_dev_rk_wait(dev, cred, pin, &ms)); | 
| 709 | 2.35k | } | 
| 710 |  |  | 
| 711 |  | fido_credman_rk_t * | 
| 712 |  | fido_credman_rk_new(void) | 
| 713 | 1.34k | { | 
| 714 | 1.34k |         return (calloc(1, sizeof(fido_credman_rk_t))); | 
| 715 | 1.34k | } | 
| 716 |  |  | 
| 717 |  | void | 
| 718 |  | fido_credman_rk_free(fido_credman_rk_t **rk_p) | 
| 719 | 1.34k | { | 
| 720 | 1.34k |         fido_credman_rk_t *rk; | 
| 721 |  |  | 
| 722 | 1.34k |         if (rk_p == NULL || (rk = *rk_p) == NULL) | 
| 723 | 0 |                 return; | 
| 724 |  |  | 
| 725 | 1.34k |         credman_reset_rk(rk); | 
| 726 | 1.34k |         free(rk); | 
| 727 | 1.34k |         *rk_p = NULL; | 
| 728 | 1.34k | } | 
| 729 |  |  | 
| 730 |  | size_t | 
| 731 |  | fido_credman_rk_count(const fido_credman_rk_t *rk) | 
| 732 | 4.39k | { | 
| 733 | 4.39k |         return (rk->n_rx); | 
| 734 | 4.39k | } | 
| 735 |  |  | 
| 736 |  | const fido_cred_t * | 
| 737 |  | fido_credman_rk(const fido_credman_rk_t *rk, size_t idx) | 
| 738 | 2.42k | { | 
| 739 | 2.42k |         if (idx >= rk->n_alloc) | 
| 740 | 621 |                 return (NULL); | 
| 741 |  |  | 
| 742 | 1.80k |         return (&rk->ptr[idx]); | 
| 743 | 2.42k | } | 
| 744 |  |  | 
| 745 |  | fido_credman_metadata_t * | 
| 746 |  | fido_credman_metadata_new(void) | 
| 747 | 537 | { | 
| 748 | 537 |         return (calloc(1, sizeof(fido_credman_metadata_t))); | 
| 749 | 537 | } | 
| 750 |  |  | 
| 751 |  | void | 
| 752 |  | fido_credman_metadata_free(fido_credman_metadata_t **metadata_p) | 
| 753 | 535 | { | 
| 754 | 535 |         fido_credman_metadata_t *metadata; | 
| 755 |  |  | 
| 756 | 535 |         if (metadata_p == NULL || (metadata = *metadata_p) == NULL) | 
| 757 | 0 |                 return; | 
| 758 |  |  | 
| 759 | 535 |         free(metadata); | 
| 760 | 535 |         *metadata_p = NULL; | 
| 761 | 535 | } | 
| 762 |  |  | 
| 763 |  | uint64_t | 
| 764 |  | fido_credman_rk_existing(const fido_credman_metadata_t *metadata) | 
| 765 | 535 | { | 
| 766 | 535 |         return (metadata->rk_existing); | 
| 767 | 535 | } | 
| 768 |  |  | 
| 769 |  | uint64_t | 
| 770 |  | fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) | 
| 771 | 535 | { | 
| 772 | 535 |         return (metadata->rk_remaining); | 
| 773 | 535 | } | 
| 774 |  |  | 
| 775 |  | fido_credman_rp_t * | 
| 776 |  | fido_credman_rp_new(void) | 
| 777 | 601 | { | 
| 778 | 601 |         return (calloc(1, sizeof(fido_credman_rp_t))); | 
| 779 | 601 | } | 
| 780 |  |  | 
| 781 |  | void | 
| 782 |  | fido_credman_rp_free(fido_credman_rp_t **rp_p) | 
| 783 | 600 | { | 
| 784 | 600 |         fido_credman_rp_t *rp; | 
| 785 |  |  | 
| 786 | 600 |         if (rp_p == NULL || (rp = *rp_p) == NULL) | 
| 787 | 0 |                 return; | 
| 788 |  |  | 
| 789 | 600 |         credman_reset_rp(rp); | 
| 790 | 600 |         free(rp); | 
| 791 | 600 |         *rp_p = NULL; | 
| 792 | 600 | } | 
| 793 |  |  | 
| 794 |  | size_t | 
| 795 |  | fido_credman_rp_count(const fido_credman_rp_t *rp) | 
| 796 | 1.70k | { | 
| 797 | 1.70k |         return (rp->n_rx); | 
| 798 | 1.70k | } | 
| 799 |  |  | 
| 800 |  | const char * | 
| 801 |  | fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx) | 
| 802 | 1.10k | { | 
| 803 | 1.10k |         if (idx >= rp->n_alloc) | 
| 804 | 487 |                 return (NULL); | 
| 805 |  |  | 
| 806 | 617 |         return (rp->ptr[idx].rp_entity.id); | 
| 807 | 1.10k | } | 
| 808 |  |  | 
| 809 |  | const char * | 
| 810 |  | fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx) | 
| 811 | 1.10k | { | 
| 812 | 1.10k |         if (idx >= rp->n_alloc) | 
| 813 | 487 |                 return (NULL); | 
| 814 |  |  | 
| 815 | 617 |         return (rp->ptr[idx].rp_entity.name); | 
| 816 | 1.10k | } | 
| 817 |  |  | 
| 818 |  | size_t | 
| 819 |  | fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx) | 
| 820 | 1.10k | { | 
| 821 | 1.10k |         if (idx >= rp->n_alloc) | 
| 822 | 487 |                 return (0); | 
| 823 |  |  | 
| 824 | 617 |         return (rp->ptr[idx].rp_id_hash.len); | 
| 825 | 1.10k | } | 
| 826 |  |  | 
| 827 |  | const unsigned char * | 
| 828 |  | fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx) | 
| 829 | 1.10k | { | 
| 830 | 1.10k |         if (idx >= rp->n_alloc) | 
| 831 | 487 |                 return (NULL); | 
| 832 |  |  | 
| 833 | 617 |         return (rp->ptr[idx].rp_id_hash.ptr); | 
| 834 | 1.10k | } |