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