2 * Copyright (c) 2019 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.
9 #include "fido/es256.h"
11 #define CMD_ENROLL_BEGIN 0x01
12 #define CMD_ENROLL_NEXT 0x02
13 #define CMD_ENROLL_CANCEL 0x03
15 #define CMD_SET_NAME 0x05
16 #define CMD_ENROLL_REMOVE 0x06
17 #define CMD_GET_INFO 0x07
20 bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
21 cbor_item_t **param, fido_blob_t *hmac_data)
23 const uint8_t prefix[2] = { 0x01 /* modality */, cmd };
25 size_t cbor_alloc_len;
27 unsigned char *cbor = NULL;
29 if (argv == NULL || param == NULL)
30 return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
32 if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
33 fido_log_debug("%s: cbor_flatten_vector", __func__);
37 if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
38 &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
39 fido_log_debug("%s: cbor_serialize_alloc", __func__);
43 if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
44 fido_log_debug("%s: malloc", __func__);
48 memcpy(hmac_data->ptr, prefix, sizeof(prefix));
49 memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
50 hmac_data->len = cbor_len + sizeof(prefix);
60 bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
61 const char *pin, const fido_blob_t *token)
64 es256_pk_t *pk = NULL;
65 fido_blob_t *ecdh = NULL;
68 const uint8_t cmd = CTAP_CBOR_BIO_ENROLL_PRE;
69 int r = FIDO_ERR_INTERNAL;
71 memset(&f, 0, sizeof(f));
72 memset(&hmac, 0, sizeof(hmac));
73 memset(&argv, 0, sizeof(argv));
75 /* modality, subCommand */
76 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
77 (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
78 fido_log_debug("%s: cbor encode", __func__);
84 if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2],
86 fido_log_debug("%s: bio_prepare_hmac", __func__);
91 /* pinProtocol, pinAuth */
93 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
94 fido_log_debug("%s: fido_do_ecdh", __func__);
97 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
98 NULL, &argv[4], &argv[3])) != FIDO_OK) {
99 fido_log_debug("%s: cbor_add_uv_params", __func__);
103 if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL ||
104 (argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) {
105 fido_log_debug("%s: encode pin", __func__);
110 /* framing and transmission */
111 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
112 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
113 fido_log_debug("%s: fido_tx", __func__);
120 cbor_vector_free(argv, nitems(argv));
122 fido_blob_free(&ecdh);
130 bio_reset_template(fido_bio_template_t *t)
134 fido_blob_reset(&t->id);
138 bio_reset_template_array(fido_bio_template_array_t *ta)
140 for (size_t i = 0; i < ta->n_alloc; i++)
141 bio_reset_template(&ta->ptr[i]);
145 memset(ta, 0, sizeof(*ta));
149 decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
151 fido_bio_template_t *t = arg;
153 if (cbor_isa_uint(key) == false ||
154 cbor_int_get_width(key) != CBOR_INT_8) {
155 fido_log_debug("%s: cbor type", __func__);
156 return (0); /* ignore */
159 switch (cbor_get_uint8(key)) {
161 return (fido_blob_decode(val, &t->id));
163 return (cbor_string_copy(val, &t->name));
166 return (0); /* ignore */
170 decode_template_array(const cbor_item_t *item, void *arg)
172 fido_bio_template_array_t *ta = arg;
174 if (cbor_isa_map(item) == false ||
175 cbor_map_is_definite(item) == false) {
176 fido_log_debug("%s: cbor type", __func__);
180 if (ta->n_rx >= ta->n_alloc) {
181 fido_log_debug("%s: n_rx >= n_alloc", __func__);
185 if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
186 fido_log_debug("%s: decode_template", __func__);
196 bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
199 fido_bio_template_array_t *ta = arg;
201 if (cbor_isa_uint(key) == false ||
202 cbor_int_get_width(key) != CBOR_INT_8 ||
203 cbor_get_uint8(key) != 7) {
204 fido_log_debug("%s: cbor type", __func__);
205 return (0); /* ignore */
208 if (cbor_isa_array(val) == false ||
209 cbor_array_is_definite(val) == false) {
210 fido_log_debug("%s: cbor type", __func__);
214 if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
215 fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
220 if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
223 ta->n_alloc = cbor_array_size(val);
225 if (cbor_array_iter(val, ta, decode_template_array) < 0) {
226 fido_log_debug("%s: decode_template_array", __func__);
234 bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
236 unsigned char reply[FIDO_MAXMSG];
240 bio_reset_template_array(ta);
242 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
244 fido_log_debug("%s: fido_rx", __func__);
245 return (FIDO_ERR_RX);
248 if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta,
249 bio_parse_template_array)) != FIDO_OK) {
250 fido_log_debug("%s: bio_parse_template_array" , __func__);
258 bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
259 const char *pin, int ms)
263 if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
264 (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
271 fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
275 return (FIDO_ERR_INVALID_ARGUMENT);
277 return (bio_get_template_array_wait(dev, ta, pin, -1));
281 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
282 const char *pin, int ms)
284 cbor_item_t *argv[2];
285 int r = FIDO_ERR_INTERNAL;
287 memset(&argv, 0, sizeof(argv));
289 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
290 (argv[1] = cbor_build_string(t->name)) == NULL) {
291 fido_log_debug("%s: cbor encode", __func__);
295 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
296 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
297 fido_log_debug("%s: tx/rx", __func__);
303 cbor_vector_free(argv, nitems(argv));
309 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
312 if (pin == NULL || t->name == NULL)
313 return (FIDO_ERR_INVALID_ARGUMENT);
315 return (bio_set_template_name_wait(dev, t, pin, -1));
319 bio_reset_enroll(fido_bio_enroll_t *e)
321 e->remaining_samples = 0;
325 fido_blob_free(&e->token);
329 bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
332 fido_bio_enroll_t *e = arg;
335 if (cbor_isa_uint(key) == false ||
336 cbor_int_get_width(key) != CBOR_INT_8) {
337 fido_log_debug("%s: cbor type", __func__);
338 return (0); /* ignore */
341 switch (cbor_get_uint8(key)) {
343 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
344 fido_log_debug("%s: cbor_decode_uint64", __func__);
347 e->last_status = (uint8_t)x;
350 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
351 fido_log_debug("%s: cbor_decode_uint64", __func__);
354 e->remaining_samples = (uint8_t)x;
357 return (0); /* ignore */
364 bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
367 fido_blob_t *id = arg;
369 if (cbor_isa_uint(key) == false ||
370 cbor_int_get_width(key) != CBOR_INT_8 ||
371 cbor_get_uint8(key) != 4) {
372 fido_log_debug("%s: cbor type", __func__);
373 return (0); /* ignore */
376 return (fido_blob_decode(val, id));
380 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
381 fido_bio_enroll_t *e, int ms)
383 unsigned char reply[FIDO_MAXMSG];
387 bio_reset_template(t);
389 e->remaining_samples = 0;
392 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
394 fido_log_debug("%s: fido_rx", __func__);
395 return (FIDO_ERR_RX);
398 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
399 bio_parse_enroll_status)) != FIDO_OK) {
400 fido_log_debug("%s: bio_parse_enroll_status", __func__);
403 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id,
404 bio_parse_template_id)) != FIDO_OK) {
405 fido_log_debug("%s: bio_parse_template_id", __func__);
413 bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
414 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
416 cbor_item_t *argv[3];
417 const uint8_t cmd = CMD_ENROLL_BEGIN;
418 int r = FIDO_ERR_INTERNAL;
420 memset(&argv, 0, sizeof(argv));
422 if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
423 fido_log_debug("%s: cbor encode", __func__);
427 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
428 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
429 fido_log_debug("%s: tx/rx", __func__);
435 cbor_vector_free(argv, nitems(argv));
441 fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
442 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
444 es256_pk_t *pk = NULL;
445 fido_blob_t *ecdh = NULL;
446 fido_blob_t *token = NULL;
449 if (pin == NULL || e->token != NULL)
450 return (FIDO_ERR_INVALID_ARGUMENT);
452 if ((token = fido_blob_new()) == NULL) {
453 r = FIDO_ERR_INTERNAL;
457 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
458 fido_log_debug("%s: fido_do_ecdh", __func__);
462 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
463 pk, NULL, token)) != FIDO_OK) {
464 fido_log_debug("%s: fido_dev_get_uv_token", __func__);
472 fido_blob_free(&ecdh);
473 fido_blob_free(&token);
478 return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
482 bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
484 unsigned char reply[FIDO_MAXMSG];
488 e->remaining_samples = 0;
491 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
493 fido_log_debug("%s: fido_rx", __func__);
494 return (FIDO_ERR_RX);
497 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
498 bio_parse_enroll_status)) != FIDO_OK) {
499 fido_log_debug("%s: bio_parse_enroll_status", __func__);
507 bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
508 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
510 cbor_item_t *argv[3];
511 const uint8_t cmd = CMD_ENROLL_NEXT;
512 int r = FIDO_ERR_INTERNAL;
514 memset(&argv, 0, sizeof(argv));
516 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
517 (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
518 fido_log_debug("%s: cbor encode", __func__);
522 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
523 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
524 fido_log_debug("%s: tx/rx", __func__);
530 cbor_vector_free(argv, nitems(argv));
536 fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
537 fido_bio_enroll_t *e, uint32_t timo_ms)
539 if (e->token == NULL)
540 return (FIDO_ERR_INVALID_ARGUMENT);
542 return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
546 bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
548 const uint8_t cmd = CMD_ENROLL_CANCEL;
551 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK ||
552 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
553 fido_log_debug("%s: tx/rx", __func__);
561 fido_bio_dev_enroll_cancel(fido_dev_t *dev)
563 return (bio_enroll_cancel_wait(dev, -1));
567 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
568 const char *pin, int ms)
570 cbor_item_t *argv[1];
571 const uint8_t cmd = CMD_ENROLL_REMOVE;
572 int r = FIDO_ERR_INTERNAL;
574 memset(&argv, 0, sizeof(argv));
576 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
577 fido_log_debug("%s: cbor encode", __func__);
581 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
582 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
583 fido_log_debug("%s: tx/rx", __func__);
589 cbor_vector_free(argv, nitems(argv));
595 fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
598 return (bio_enroll_remove_wait(dev, t, pin, -1));
602 bio_reset_info(fido_bio_info_t *i)
609 bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
611 fido_bio_info_t *i = arg;
614 if (cbor_isa_uint(key) == false ||
615 cbor_int_get_width(key) != CBOR_INT_8) {
616 fido_log_debug("%s: cbor type", __func__);
617 return (0); /* ignore */
620 switch (cbor_get_uint8(key)) {
622 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
623 fido_log_debug("%s: cbor_decode_uint64", __func__);
626 i->type = (uint8_t)x;
629 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
630 fido_log_debug("%s: cbor_decode_uint64", __func__);
633 i->max_samples = (uint8_t)x;
636 return (0); /* ignore */
643 bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
645 unsigned char reply[FIDO_MAXMSG];
651 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
653 fido_log_debug("%s: fido_rx", __func__);
654 return (FIDO_ERR_RX);
657 if ((r = cbor_parse_reply(reply, (size_t)reply_len, i,
658 bio_parse_info)) != FIDO_OK) {
659 fido_log_debug("%s: bio_parse_info" , __func__);
667 bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
671 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK ||
672 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
673 fido_log_debug("%s: tx/rx", __func__);
681 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
683 return (bio_get_info_wait(dev, i, -1));
687 fido_bio_template_name(const fido_bio_template_t *t)
692 const unsigned char *
693 fido_bio_template_id_ptr(const fido_bio_template_t *t)
699 fido_bio_template_id_len(const fido_bio_template_t *t)
705 fido_bio_template_array_count(const fido_bio_template_array_t *ta)
710 fido_bio_template_array_t *
711 fido_bio_template_array_new(void)
713 return (calloc(1, sizeof(fido_bio_template_array_t)));
716 fido_bio_template_t *
717 fido_bio_template_new(void)
719 return (calloc(1, sizeof(fido_bio_template_t)));
723 fido_bio_template_array_free(fido_bio_template_array_t **tap)
725 fido_bio_template_array_t *ta;
727 if (tap == NULL || (ta = *tap) == NULL)
730 bio_reset_template_array(ta);
736 fido_bio_template_free(fido_bio_template_t **tp)
738 fido_bio_template_t *t;
740 if (tp == NULL || (t = *tp) == NULL)
743 bio_reset_template(t);
749 fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
754 if (name && (t->name = strdup(name)) == NULL)
755 return (FIDO_ERR_INTERNAL);
761 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
764 fido_blob_reset(&t->id);
766 if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
767 return (FIDO_ERR_INTERNAL);
772 const fido_bio_template_t *
773 fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
775 if (idx >= ta->n_alloc)
778 return (&ta->ptr[idx]);
782 fido_bio_enroll_new(void)
784 return (calloc(1, sizeof(fido_bio_enroll_t)));
788 fido_bio_info_new(void)
790 return (calloc(1, sizeof(fido_bio_info_t)));
794 fido_bio_info_type(const fido_bio_info_t *i)
800 fido_bio_info_max_samples(const fido_bio_info_t *i)
802 return (i->max_samples);
806 fido_bio_enroll_free(fido_bio_enroll_t **ep)
808 fido_bio_enroll_t *e;
810 if (ep == NULL || (e = *ep) == NULL)
820 fido_bio_info_free(fido_bio_info_t **ip)
824 if (ip == NULL || (i = *ip) == NULL)
832 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
834 return (e->remaining_samples);
838 fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
840 return (e->last_status);