#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;
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_decode_bool(val, NULL) < 0) {
fido_log_debug("%s: cbor_decode_bool", __func__);
return (0);
}
if (cbor_string_copy(key, &o->name[i]) < 0) {
fido_log_debug("%s: cbor_string_copy", __func__);
return (0);
}
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);
}
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;
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 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__);
fido_algo_free(&aa->ptr[i]);
return (-1);
}
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
decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
fido_cert_array_t *c = arg;
const size_t i = c->len;
if (cbor_is_int(val) == false) {
fido_log_debug("%s: cbor_is_int", __func__);
return (0);
}
if (cbor_string_copy(key, &c->name[i]) < 0) {
fido_log_debug("%s: cbor_string_copy", __func__);
return (0);
}
c->value[i] = cbor_get_int(val);
c->len++;
return (0);
}
static int
decode_certs(const cbor_item_t *item, fido_cert_array_t *c)
{
c->name = NULL;
c->value = NULL;
c->len = 0;
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false) {
fido_log_debug("%s: cbor type", __func__);
return (-1);
}
c->name = calloc(cbor_map_size(item), sizeof(char *));
c->value = calloc(cbor_map_size(item), sizeof(uint64_t));
if (c->name == NULL || c->value == NULL)
return (-1);
return (cbor_map_iter(item, c, decode_cert));
}
static int
parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
fido_cbor_info_t *ci = arg;
uint64_t x;
if (cbor_isa_uint(key) == false ||
cbor_int_get_width(key) != CBOR_INT_8) {
fido_log_debug("%s: cbor type", __func__);
return (0);
}
switch (cbor_get_uint8(key)) {
case 1:
return (decode_string_array(val, &ci->versions));
case 2:
return (decode_string_array(val, &ci->extensions));
case 3:
return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
case 4:
return (decode_options(val, &ci->options));
case 5:
return (cbor_decode_uint64(val, &ci->maxmsgsiz));
case 6:
return (decode_protocols(val, &ci->protocols));
case 7:
return (cbor_decode_uint64(val, &ci->maxcredcntlst));
case 8:
return (cbor_decode_uint64(val, &ci->maxcredidlen));
case 9:
return (decode_string_array(val, &ci->transports));
case 10:
return (decode_algorithms(val, &ci->algorithms));
case 11:
return (cbor_decode_uint64(val, &ci->maxlargeblob));
case 12:
return (cbor_decode_bool(val, &ci->new_pin_reqd));
case 13:
return (cbor_decode_uint64(val, &ci->minpinlen));
case 14:
return (cbor_decode_uint64(val, &ci->fwversion));
case 15:
return (cbor_decode_uint64(val, &ci->maxcredbloblen));
case 16:
return (cbor_decode_uint64(val, &ci->maxrpid_minlen));
case 17:
return (cbor_decode_uint64(val, &ci->uv_attempts));
case 18:
return (cbor_decode_uint64(val, &ci->uv_modality));
case 19:
return (decode_certs(val, &ci->certs));
case 20:
if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) {
fido_log_debug("%s: cbor_decode_uint64", __func__);
return (-1);
}
ci->rk_remaining = (int64_t)x;
return (0);
default:
fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key));
return (0);
}
}
static int
fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
{
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), ms) < 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 *msg;
int msglen;
int r;
fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
(void *)ci, *ms);
fido_cbor_info_reset(ci);
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
r = FIDO_ERR_INTERNAL;
goto out;
}
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
fido_log_debug("%s: fido_rx", __func__);
r = FIDO_ERR_RX;
goto out;
}
r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element);
out:
freezero(msg, FIDO_MAXMSG);
return (r);
}
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, ms)) != 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)
{
int ms = dev->timeout_ms;
return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
}
fido_cbor_info_t *
fido_cbor_info_new(void)
{
fido_cbor_info_t *ci;
if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL)
return (NULL);
fido_cbor_info_reset(ci);
return (ci);
}
void
fido_cbor_info_reset(fido_cbor_info_t *ci)
{
fido_str_array_free(&ci->versions);
fido_str_array_free(&ci->extensions);
fido_str_array_free(&ci->transports);
fido_opt_array_free(&ci->options);
fido_byte_array_free(&ci->protocols);
fido_algo_array_free(&ci->algorithms);
fido_cert_array_free(&ci->certs);
ci->rk_remaining = -1;
}
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_maxlargeblob(const fido_cbor_info_t *ci)
{
return (ci->maxlargeblob);
}
uint64_t
fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
{
return (ci->fwversion);
}
uint64_t
fido_cbor_info_minpinlen(const fido_cbor_info_t *ci)
{
return (ci->minpinlen);
}
uint64_t
fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci)
{
return (ci->maxrpid_minlen);
}
uint64_t
fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci)
{
return (ci->uv_attempts);
}
uint64_t
fido_cbor_info_uv_modality(const fido_cbor_info_t *ci)
{
return (ci->uv_modality);
}
int64_t
fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci)
{
return (ci->rk_remaining);
}
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);
}
bool
fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci)
{
return (ci->new_pin_reqd);
}
char **
fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci)
{
return (ci->certs.name);
}
const uint64_t *
fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci)
{
return (ci->certs.value);
}
size_t
fido_cbor_info_certs_len(const fido_cbor_info_t *ci)
{
return (ci->certs.len);
}