diff options
Diffstat (limited to 'src/info.c')
-rw-r--r-- | src/info.c | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/src/info.c b/src/info.c new file mode 100644 index 000000000000..57bc8de44063 --- /dev/null +++ b/src/info.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2018-2021 Yubico AB. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include "fido.h" + +static int +decode_string(const cbor_item_t *item, void *arg) +{ + fido_str_array_t *a = arg; + const size_t i = a->len; + + /* keep ptr[x] and len consistent */ + if (cbor_string_copy(item, &a->ptr[i]) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (-1); + } + + a->len++; + + return (0); +} + +static int +decode_string_array(const cbor_item_t *item, fido_str_array_t *v) +{ + v->ptr = NULL; + v->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + v->ptr = calloc(cbor_array_size(item), sizeof(char *)); + if (v->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, v, decode_string) < 0) { + fido_log_debug("%s: decode_string", __func__); + return (-1); + } + + return (0); +} + +static int +decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len) +{ + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false || + cbor_bytestring_length(item) != aaguid_len) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len); + + return (0); +} + +static int +decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_opt_array_t *o = arg; + const size_t i = o->len; + + if (cbor_isa_float_ctrl(val) == false || + cbor_float_get_width(val) != CBOR_FLOAT_0 || + cbor_is_bool(val) == false) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + if (cbor_string_copy(key, &o->name[i]) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (0); /* ignore */ + } + + /* keep name/value and len consistent */ + o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE; + o->len++; + + return (0); +} + +static int +decode_options(const cbor_item_t *item, fido_opt_array_t *o) +{ + o->name = NULL; + o->value = NULL; + o->len = 0; + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + o->name = calloc(cbor_map_size(item), sizeof(char *)); + o->value = calloc(cbor_map_size(item), sizeof(bool)); + if (o->name == NULL || o->value == NULL) + return (-1); + + return (cbor_map_iter(item, o, decode_option)); +} + +static int +decode_protocol(const cbor_item_t *item, void *arg) +{ + fido_byte_array_t *p = arg; + const size_t i = p->len; + + if (cbor_isa_uint(item) == false || + cbor_int_get_width(item) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + /* keep ptr[x] and len consistent */ + p->ptr[i] = cbor_get_uint8(item); + p->len++; + + return (0); +} + +static int +decode_protocols(const cbor_item_t *item, fido_byte_array_t *p) +{ + p->ptr = NULL; + p->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t)); + if (p->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, p, decode_protocol) < 0) { + fido_log_debug("%s: decode_protocol", __func__); + return (-1); + } + + return (0); +} + +static int +decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_algo_t *alg = arg; + char *name = NULL; + int ok = -1; + + if (cbor_string_copy(key, &name) < 0) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (!strcmp(name, "alg")) { + if (cbor_isa_negint(val) == false || + cbor_get_int(val) > INT_MAX || alg->cose != 0) { + fido_log_debug("%s: alg", __func__); + goto out; + } + alg->cose = -(int)cbor_get_int(val) - 1; + } else if (!strcmp(name, "type")) { + if (cbor_string_copy(val, &alg->type) < 0) { + fido_log_debug("%s: type", __func__); + goto out; + } + } + + ok = 0; +out: + free(name); + + return (ok); +} + +static void +free_algo(fido_algo_t *a) +{ + free(a->type); + a->type = NULL; + a->cose = 0; +} + +static int +decode_algorithm(const cbor_item_t *item, void *arg) +{ + fido_algo_array_t *aa = arg; + const size_t i = aa->len; + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memset(&aa->ptr[i], 0, sizeof(aa->ptr[i])); + + if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) { + fido_log_debug("%s: decode_algorithm_entry", __func__); + free_algo(&aa->ptr[i]); + return (-1); + } + + /* keep ptr[x] and len consistent */ + aa->len++; + + return (0); +} + +static int +decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa) +{ + aa->ptr = NULL; + aa->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t)); + if (aa->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, aa, decode_algorithm) < 0) { + fido_log_debug("%s: decode_algorithm", __func__); + return (-1); + } + + return (0); +} + +static int +parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_cbor_info_t *ci = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: /* versions */ + return (decode_string_array(val, &ci->versions)); + case 2: /* extensions */ + return (decode_string_array(val, &ci->extensions)); + case 3: /* aaguid */ + return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid))); + case 4: /* options */ + return (decode_options(val, &ci->options)); + case 5: /* maxMsgSize */ + return (cbor_decode_uint64(val, &ci->maxmsgsiz)); + case 6: /* pinProtocols */ + return (decode_protocols(val, &ci->protocols)); + case 7: /* maxCredentialCountInList */ + return (cbor_decode_uint64(val, &ci->maxcredcntlst)); + case 8: /* maxCredentialIdLength */ + return (cbor_decode_uint64(val, &ci->maxcredidlen)); + case 9: /* transports */ + return (decode_string_array(val, &ci->transports)); + case 10: /* algorithms */ + return (decode_algorithms(val, &ci->algorithms)); + case 14: /* fwVersion */ + return (cbor_decode_uint64(val, &ci->fwversion)); + case 15: /* maxCredBlobLen */ + return (cbor_decode_uint64(val, &ci->maxcredbloblen)); + default: /* ignore */ + fido_log_debug("%s: cbor type", __func__); + return (0); + } +} + +static int +fido_dev_get_cbor_info_tx(fido_dev_t *dev) +{ + const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; + + fido_log_debug("%s: dev=%p", __func__, (void *)dev); + + if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + return (FIDO_ERR_TX); + } + + return (FIDO_OK); +} + +static int +fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) +{ + unsigned char reply[FIDO_MAXMSG]; + int reply_len; + + fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, + (void *)ci, ms); + + fido_cbor_info_reset(ci); + + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + return (cbor_parse_reply(reply, (size_t)reply_len, ci, + parse_reply_element)); +} + +int +fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) +{ + int r; + +#ifdef USE_WINHELLO + if (dev->flags & FIDO_DEV_WINHELLO) + return (fido_winhello_get_cbor_info(dev, ci)); +#endif + if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK || + (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) +{ + return (fido_dev_get_cbor_info_wait(dev, ci, -1)); +} + +/* + * get/set functions for fido_cbor_info_t; always at the end of the file + */ + +fido_cbor_info_t * +fido_cbor_info_new(void) +{ + return (calloc(1, sizeof(fido_cbor_info_t))); +} + +static void +free_str_array(fido_str_array_t *sa) +{ + for (size_t i = 0; i < sa->len; i++) + free(sa->ptr[i]); + + free(sa->ptr); + sa->ptr = NULL; + sa->len = 0; +} + +static void +free_opt_array(fido_opt_array_t *oa) +{ + for (size_t i = 0; i < oa->len; i++) + free(oa->name[i]); + + free(oa->name); + free(oa->value); + oa->name = NULL; + oa->value = NULL; +} + +static void +free_byte_array(fido_byte_array_t *ba) +{ + free(ba->ptr); + + ba->ptr = NULL; + ba->len = 0; +} + +static void +free_algo_array(fido_algo_array_t *aa) +{ + for (size_t i = 0; i < aa->len; i++) + free_algo(&aa->ptr[i]); + + free(aa->ptr); + aa->ptr = NULL; + aa->len = 0; +} + +void +fido_cbor_info_reset(fido_cbor_info_t *ci) +{ + free_str_array(&ci->versions); + free_str_array(&ci->extensions); + free_str_array(&ci->transports); + free_opt_array(&ci->options); + free_byte_array(&ci->protocols); + free_algo_array(&ci->algorithms); +} + +void +fido_cbor_info_free(fido_cbor_info_t **ci_p) +{ + fido_cbor_info_t *ci; + + if (ci_p == NULL || (ci = *ci_p) == NULL) + return; + fido_cbor_info_reset(ci); + free(ci); + *ci_p = NULL; +} + +char ** +fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) +{ + return (ci->versions.ptr); +} + +size_t +fido_cbor_info_versions_len(const fido_cbor_info_t *ci) +{ + return (ci->versions.len); +} + +char ** +fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) +{ + return (ci->extensions.ptr); +} + +size_t +fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) +{ + return (ci->extensions.len); +} + +char ** +fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci) +{ + return (ci->transports.ptr); +} + +size_t +fido_cbor_info_transports_len(const fido_cbor_info_t *ci) +{ + return (ci->transports.len); +} + +const unsigned char * +fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) +{ + return (ci->aaguid); +} + +size_t +fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) +{ + return (sizeof(ci->aaguid)); +} + +char ** +fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) +{ + return (ci->options.name); +} + +const bool * +fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) +{ + return (ci->options.value); +} + +size_t +fido_cbor_info_options_len(const fido_cbor_info_t *ci) +{ + return (ci->options.len); +} + +uint64_t +fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci) +{ + return (ci->maxcredbloblen); +} + +uint64_t +fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) +{ + return (ci->maxmsgsiz); +} + +uint64_t +fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci) +{ + return (ci->maxcredcntlst); +} + +uint64_t +fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) +{ + return (ci->maxcredidlen); +} + +uint64_t +fido_cbor_info_fwversion(const fido_cbor_info_t *ci) +{ + return (ci->fwversion); +} + +const uint8_t * +fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) +{ + return (ci->protocols.ptr); +} + +size_t +fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) +{ + return (ci->protocols.len); +} + +size_t +fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci) +{ + return (ci->algorithms.len); +} + +const char * +fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx) +{ + if (idx >= ci->algorithms.len) + return (NULL); + + return (ci->algorithms.ptr[idx].type); +} + +int +fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx) +{ + if (idx >= ci->algorithms.len) + return (0); + + return (ci->algorithms.ptr[idx].cose); +} |