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