2 * Copyright (c) 2019-2021 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.
7 #include <openssl/sha.h>
10 #include "fido/credman.h"
11 #include "fido/es256.h"
13 #define CMD_CRED_METADATA 0x01
14 #define CMD_RP_BEGIN 0x02
15 #define CMD_RP_NEXT 0x03
16 #define CMD_RK_BEGIN 0x04
17 #define CMD_RK_NEXT 0x05
18 #define CMD_DELETE_CRED 0x06
19 #define CMD_UPDATE_CRED 0x07
22 credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n,
29 fido_log_debug("%s: n > UINT8_MAX", __func__);
38 if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
39 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
44 if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
54 credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param,
55 fido_blob_t *hmac_data)
57 cbor_item_t *param_cbor[3];
58 const fido_cred_t *cred;
62 memset(¶m_cbor, 0, sizeof(param_cbor));
65 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
70 if ((param_cbor[0] = fido_blob_encode(body)) == NULL) {
71 fido_log_debug("%s: cbor encode", __func__);
77 if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) {
78 fido_log_debug("%s: cbor encode", __func__);
85 param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id);
86 param_cbor[2] = cbor_encode_user_entity(&cred->user);
87 if (param_cbor[1] == NULL || param_cbor[2] == NULL) {
88 fido_log_debug("%s: cbor encode", __func__);
93 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
97 if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
98 fido_log_debug("%s: cbor_flatten_vector", __func__);
101 if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
102 fido_log_debug("%s: cbor_build_frame", __func__);
108 cbor_vector_free(param_cbor, nitems(param_cbor));
114 credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
115 const char *rp_id, fido_opt_t uv, int *ms)
118 fido_blob_t *ecdh = NULL;
120 es256_pk_t *pk = NULL;
121 cbor_item_t *argv[4];
122 const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE;
123 int r = FIDO_ERR_INTERNAL;
125 memset(&f, 0, sizeof(f));
126 memset(&hmac, 0, sizeof(hmac));
127 memset(&argv, 0, sizeof(argv));
129 if (fido_dev_is_fido2(dev) == false) {
130 fido_log_debug("%s: fido_dev_is_fido2", __func__);
131 r = FIDO_ERR_INVALID_COMMAND;
136 if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
137 fido_log_debug("%s: cbor encode", __func__);
141 /* pinProtocol, pinAuth */
142 if (pin != NULL || uv == FIDO_OPT_TRUE) {
143 if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) {
144 fido_log_debug("%s: credman_prepare_hmac", __func__);
147 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
148 fido_log_debug("%s: fido_do_ecdh", __func__);
151 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
152 rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) {
153 fido_log_debug("%s: cbor_add_uv_params", __func__);
158 /* framing and transmission */
159 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
160 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
161 fido_log_debug("%s: fido_tx", __func__);
169 fido_blob_free(&ecdh);
170 cbor_vector_free(argv, nitems(argv));
178 credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
181 fido_credman_metadata_t *metadata = arg;
183 if (cbor_isa_uint(key) == false ||
184 cbor_int_get_width(key) != CBOR_INT_8) {
185 fido_log_debug("%s: cbor type", __func__);
186 return (0); /* ignore */
189 switch (cbor_get_uint8(key)) {
191 return (cbor_decode_uint64(val, &metadata->rk_existing));
193 return (cbor_decode_uint64(val, &metadata->rk_remaining));
195 fido_log_debug("%s: cbor type", __func__);
196 return (0); /* ignore */
201 credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms)
203 unsigned char reply[FIDO_MAXMSG];
207 memset(metadata, 0, sizeof(*metadata));
209 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
211 fido_log_debug("%s: fido_rx", __func__);
212 return (FIDO_ERR_RX);
215 if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata,
216 credman_parse_metadata)) != FIDO_OK) {
217 fido_log_debug("%s: credman_parse_metadata", __func__);
225 credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
226 const char *pin, int *ms)
230 if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
231 FIDO_OPT_TRUE, ms)) != FIDO_OK ||
232 (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
239 fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
242 int ms = dev->timeout_ms;
244 return (credman_get_metadata_wait(dev, metadata, pin, &ms));
248 credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
250 fido_cred_t *cred = arg;
253 if (cbor_isa_uint(key) == false ||
254 cbor_int_get_width(key) != CBOR_INT_8) {
255 fido_log_debug("%s: cbor type", __func__);
256 return (0); /* ignore */
259 switch (cbor_get_uint8(key)) {
261 return (cbor_decode_user(val, &cred->user));
263 return (cbor_decode_cred_id(val, &cred->attcred.id));
265 if (cbor_decode_pubkey(val, &cred->attcred.type,
266 &cred->attcred.pubkey) < 0)
268 cred->type = cred->attcred.type; /* XXX */
271 if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX ||
272 fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
276 return (fido_blob_decode(val, &cred->largeblob_key));
278 fido_log_debug("%s: cbor type", __func__);
279 return (0); /* ignore */
284 credman_reset_rk(fido_credman_rk_t *rk)
286 for (size_t i = 0; i < rk->n_alloc; i++) {
287 fido_cred_reset_tx(&rk->ptr[i]);
288 fido_cred_reset_rx(&rk->ptr[i]);
293 memset(rk, 0, sizeof(*rk));
297 credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
300 fido_credman_rk_t *rk = arg;
303 /* totalCredentials */
304 if (cbor_isa_uint(key) == false ||
305 cbor_int_get_width(key) != CBOR_INT_8 ||
306 cbor_get_uint8(key) != 9) {
307 fido_log_debug("%s: cbor_type", __func__);
308 return (0); /* ignore */
311 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
312 fido_log_debug("%s: cbor_decode_uint64", __func__);
316 if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
317 (size_t)n, sizeof(*rk->ptr)) < 0) {
318 fido_log_debug("%s: credman_grow_array", __func__);
326 credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
328 unsigned char reply[FIDO_MAXMSG];
332 credman_reset_rk(rk);
334 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
336 fido_log_debug("%s: fido_rx", __func__);
337 return (FIDO_ERR_RX);
340 /* adjust as needed */
341 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk,
342 credman_parse_rk_count)) != FIDO_OK) {
343 fido_log_debug("%s: credman_parse_rk_count", __func__);
347 if (rk->n_alloc == 0) {
348 fido_log_debug("%s: n_alloc=0", __func__);
352 /* parse the first rk */
353 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0],
354 credman_parse_rk)) != FIDO_OK) {
355 fido_log_debug("%s: credman_parse_rk", __func__);
365 credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
367 unsigned char reply[FIDO_MAXMSG];
371 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
373 fido_log_debug("%s: fido_rx", __func__);
374 return (FIDO_ERR_RX);
378 if (rk->n_rx >= rk->n_alloc) {
379 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
381 return (FIDO_ERR_INTERNAL);
384 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx],
385 credman_parse_rk)) != FIDO_OK) {
386 fido_log_debug("%s: credman_parse_rk", __func__);
394 credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
395 const char *pin, int *ms)
398 uint8_t dgst[SHA256_DIGEST_LENGTH];
401 if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
402 fido_log_debug("%s: sha256", __func__);
403 return (FIDO_ERR_INTERNAL);
407 rp_dgst.len = sizeof(dgst);
409 if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
410 FIDO_OPT_TRUE, ms)) != FIDO_OK ||
411 (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
414 while (rk->n_rx < rk->n_alloc) {
415 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL,
416 FIDO_OPT_FALSE, ms)) != FIDO_OK ||
417 (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
426 fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
427 fido_credman_rk_t *rk, const char *pin)
429 int ms = dev->timeout_ms;
431 return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms));
435 credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
436 size_t cred_id_len, const char *pin, int *ms)
441 memset(&cred, 0, sizeof(cred));
443 if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
444 return (FIDO_ERR_INVALID_ARGUMENT);
446 if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
447 FIDO_OPT_TRUE, ms)) != FIDO_OK ||
448 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
459 fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
460 size_t cred_id_len, const char *pin)
462 int ms = dev->timeout_ms;
464 return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms));
468 credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
470 struct fido_credman_single_rp *rp = arg;
472 if (cbor_isa_uint(key) == false ||
473 cbor_int_get_width(key) != CBOR_INT_8) {
474 fido_log_debug("%s: cbor type", __func__);
475 return (0); /* ignore */
478 switch (cbor_get_uint8(key)) {
480 return (cbor_decode_rp_entity(val, &rp->rp_entity));
482 return (fido_blob_decode(val, &rp->rp_id_hash));
484 fido_log_debug("%s: cbor type", __func__);
485 return (0); /* ignore */
490 credman_reset_rp(fido_credman_rp_t *rp)
492 for (size_t i = 0; i < rp->n_alloc; i++) {
493 free(rp->ptr[i].rp_entity.id);
494 free(rp->ptr[i].rp_entity.name);
495 rp->ptr[i].rp_entity.id = NULL;
496 rp->ptr[i].rp_entity.name = NULL;
497 fido_blob_reset(&rp->ptr[i].rp_id_hash);
502 memset(rp, 0, sizeof(*rp));
506 credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
509 fido_credman_rp_t *rp = arg;
513 if (cbor_isa_uint(key) == false ||
514 cbor_int_get_width(key) != CBOR_INT_8 ||
515 cbor_get_uint8(key) != 5) {
516 fido_log_debug("%s: cbor_type", __func__);
517 return (0); /* ignore */
520 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
521 fido_log_debug("%s: cbor_decode_uint64", __func__);
525 if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
526 (size_t)n, sizeof(*rp->ptr)) < 0) {
527 fido_log_debug("%s: credman_grow_array", __func__);
535 credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
537 unsigned char reply[FIDO_MAXMSG];
541 credman_reset_rp(rp);
543 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
545 fido_log_debug("%s: fido_rx", __func__);
546 return (FIDO_ERR_RX);
549 /* adjust as needed */
550 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp,
551 credman_parse_rp_count)) != FIDO_OK) {
552 fido_log_debug("%s: credman_parse_rp_count", __func__);
556 if (rp->n_alloc == 0) {
557 fido_log_debug("%s: n_alloc=0", __func__);
561 /* parse the first rp */
562 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0],
563 credman_parse_rp)) != FIDO_OK) {
564 fido_log_debug("%s: credman_parse_rp", __func__);
574 credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
576 unsigned char reply[FIDO_MAXMSG];
580 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
582 fido_log_debug("%s: fido_rx", __func__);
583 return (FIDO_ERR_RX);
587 if (rp->n_rx >= rp->n_alloc) {
588 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
590 return (FIDO_ERR_INTERNAL);
593 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx],
594 credman_parse_rp)) != FIDO_OK) {
595 fido_log_debug("%s: credman_parse_rp", __func__);
603 credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
608 if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
609 FIDO_OPT_TRUE, ms)) != FIDO_OK ||
610 (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
613 while (rp->n_rx < rp->n_alloc) {
614 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL,
615 FIDO_OPT_FALSE, ms)) != FIDO_OK ||
616 (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
625 fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
627 int ms = dev->timeout_ms;
629 return (credman_get_rp_wait(dev, rp, pin, &ms));
633 credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
638 if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL,
639 FIDO_OPT_TRUE, ms)) != FIDO_OK ||
640 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
647 fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
649 int ms = dev->timeout_ms;
651 return (credman_set_dev_rk_wait(dev, cred, pin, &ms));
655 fido_credman_rk_new(void)
657 return (calloc(1, sizeof(fido_credman_rk_t)));
661 fido_credman_rk_free(fido_credman_rk_t **rk_p)
663 fido_credman_rk_t *rk;
665 if (rk_p == NULL || (rk = *rk_p) == NULL)
668 credman_reset_rk(rk);
674 fido_credman_rk_count(const fido_credman_rk_t *rk)
680 fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
682 if (idx >= rk->n_alloc)
685 return (&rk->ptr[idx]);
688 fido_credman_metadata_t *
689 fido_credman_metadata_new(void)
691 return (calloc(1, sizeof(fido_credman_metadata_t)));
695 fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
697 fido_credman_metadata_t *metadata;
699 if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
707 fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
709 return (metadata->rk_existing);
713 fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
715 return (metadata->rk_remaining);
719 fido_credman_rp_new(void)
721 return (calloc(1, sizeof(fido_credman_rp_t)));
725 fido_credman_rp_free(fido_credman_rp_t **rp_p)
727 fido_credman_rp_t *rp;
729 if (rp_p == NULL || (rp = *rp_p) == NULL)
732 credman_reset_rp(rp);
738 fido_credman_rp_count(const fido_credman_rp_t *rp)
744 fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
746 if (idx >= rp->n_alloc)
749 return (rp->ptr[idx].rp_entity.id);
753 fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
755 if (idx >= rp->n_alloc)
758 return (rp->ptr[idx].rp_entity.name);
762 fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
764 if (idx >= rp->n_alloc)
767 return (rp->ptr[idx].rp_id_hash.len);
770 const unsigned char *
771 fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
773 if (idx >= rp->n_alloc)
776 return (rp->ptr[idx].rp_id_hash.ptr);