1 /* 2 * Copyright (c) 2018 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 */ 6 7 #include <string.h> 8 #include "fido.h" 9 10 static int 11 decode_version(const cbor_item_t *item, void *arg) 12 { 13 fido_str_array_t *v = arg; 14 const size_t i = v->len; 15 16 /* keep ptr[x] and len consistent */ 17 if (cbor_string_copy(item, &v->ptr[i]) < 0) { 18 fido_log_debug("%s: cbor_string_copy", __func__); 19 return (-1); 20 } 21 22 v->len++; 23 24 return (0); 25 } 26 27 static int 28 decode_versions(const cbor_item_t *item, fido_str_array_t *v) 29 { 30 v->ptr = NULL; 31 v->len = 0; 32 33 if (cbor_isa_array(item) == false || 34 cbor_array_is_definite(item) == false) { 35 fido_log_debug("%s: cbor type", __func__); 36 return (-1); 37 } 38 39 v->ptr = calloc(cbor_array_size(item), sizeof(char *)); 40 if (v->ptr == NULL) 41 return (-1); 42 43 if (cbor_array_iter(item, v, decode_version) < 0) { 44 fido_log_debug("%s: decode_version", __func__); 45 return (-1); 46 } 47 48 return (0); 49 } 50 51 static int 52 decode_extension(const cbor_item_t *item, void *arg) 53 { 54 fido_str_array_t *e = arg; 55 const size_t i = e->len; 56 57 /* keep ptr[x] and len consistent */ 58 if (cbor_string_copy(item, &e->ptr[i]) < 0) { 59 fido_log_debug("%s: cbor_string_copy", __func__); 60 return (-1); 61 } 62 63 e->len++; 64 65 return (0); 66 } 67 68 static int 69 decode_extensions(const cbor_item_t *item, fido_str_array_t *e) 70 { 71 e->ptr = NULL; 72 e->len = 0; 73 74 if (cbor_isa_array(item) == false || 75 cbor_array_is_definite(item) == false) { 76 fido_log_debug("%s: cbor type", __func__); 77 return (-1); 78 } 79 80 e->ptr = calloc(cbor_array_size(item), sizeof(char *)); 81 if (e->ptr == NULL) 82 return (-1); 83 84 if (cbor_array_iter(item, e, decode_extension) < 0) { 85 fido_log_debug("%s: decode_extension", __func__); 86 return (-1); 87 } 88 89 return (0); 90 } 91 92 static int 93 decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len) 94 { 95 if (cbor_isa_bytestring(item) == false || 96 cbor_bytestring_is_definite(item) == false || 97 cbor_bytestring_length(item) != aaguid_len) { 98 fido_log_debug("%s: cbor type", __func__); 99 return (-1); 100 } 101 102 memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len); 103 104 return (0); 105 } 106 107 static int 108 decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg) 109 { 110 fido_opt_array_t *o = arg; 111 const size_t i = o->len; 112 113 if (cbor_isa_float_ctrl(val) == false || 114 cbor_float_get_width(val) != CBOR_FLOAT_0 || 115 cbor_is_bool(val) == false) { 116 fido_log_debug("%s: cbor type", __func__); 117 return (0); /* ignore */ 118 } 119 120 if (cbor_string_copy(key, &o->name[i]) < 0) { 121 fido_log_debug("%s: cbor_string_copy", __func__); 122 return (0); /* ignore */ 123 } 124 125 /* keep name/value and len consistent */ 126 o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE; 127 o->len++; 128 129 return (0); 130 } 131 132 static int 133 decode_options(const cbor_item_t *item, fido_opt_array_t *o) 134 { 135 o->name = NULL; 136 o->value = NULL; 137 o->len = 0; 138 139 if (cbor_isa_map(item) == false || 140 cbor_map_is_definite(item) == false) { 141 fido_log_debug("%s: cbor type", __func__); 142 return (-1); 143 } 144 145 o->name = calloc(cbor_map_size(item), sizeof(char *)); 146 o->value = calloc(cbor_map_size(item), sizeof(bool)); 147 if (o->name == NULL || o->value == NULL) 148 return (-1); 149 150 return (cbor_map_iter(item, o, decode_option)); 151 } 152 153 static int 154 decode_protocol(const cbor_item_t *item, void *arg) 155 { 156 fido_byte_array_t *p = arg; 157 const size_t i = p->len; 158 159 if (cbor_isa_uint(item) == false || 160 cbor_int_get_width(item) != CBOR_INT_8) { 161 fido_log_debug("%s: cbor type", __func__); 162 return (-1); 163 } 164 165 /* keep ptr[x] and len consistent */ 166 p->ptr[i] = cbor_get_uint8(item); 167 p->len++; 168 169 return (0); 170 } 171 172 static int 173 decode_protocols(const cbor_item_t *item, fido_byte_array_t *p) 174 { 175 p->ptr = NULL; 176 p->len = 0; 177 178 if (cbor_isa_array(item) == false || 179 cbor_array_is_definite(item) == false) { 180 fido_log_debug("%s: cbor type", __func__); 181 return (-1); 182 } 183 184 p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t)); 185 if (p->ptr == NULL) 186 return (-1); 187 188 if (cbor_array_iter(item, p, decode_protocol) < 0) { 189 fido_log_debug("%s: decode_protocol", __func__); 190 return (-1); 191 } 192 193 return (0); 194 } 195 196 static int 197 parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) 198 { 199 fido_cbor_info_t *ci = arg; 200 201 if (cbor_isa_uint(key) == false || 202 cbor_int_get_width(key) != CBOR_INT_8) { 203 fido_log_debug("%s: cbor type", __func__); 204 return (0); /* ignore */ 205 } 206 207 switch (cbor_get_uint8(key)) { 208 case 1: /* versions */ 209 return (decode_versions(val, &ci->versions)); 210 case 2: /* extensions */ 211 return (decode_extensions(val, &ci->extensions)); 212 case 3: /* aaguid */ 213 return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid))); 214 case 4: /* options */ 215 return (decode_options(val, &ci->options)); 216 case 5: /* maxMsgSize */ 217 return (cbor_decode_uint64(val, &ci->maxmsgsiz)); 218 case 6: /* pinProtocols */ 219 return (decode_protocols(val, &ci->protocols)); 220 case 7: /* maxCredentialCountInList */ 221 return (cbor_decode_uint64(val, &ci->maxcredcntlst)); 222 case 8: /* maxCredentialIdLength */ 223 return (cbor_decode_uint64(val, &ci->maxcredidlen)); 224 case 14: /* fwVersion */ 225 return (cbor_decode_uint64(val, &ci->fwversion)); 226 default: /* ignore */ 227 fido_log_debug("%s: cbor type", __func__); 228 return (0); 229 } 230 } 231 232 static int 233 fido_dev_get_cbor_info_tx(fido_dev_t *dev) 234 { 235 const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; 236 237 fido_log_debug("%s: dev=%p", __func__, (void *)dev); 238 239 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { 240 fido_log_debug("%s: fido_tx", __func__); 241 return (FIDO_ERR_TX); 242 } 243 244 return (FIDO_OK); 245 } 246 247 static int 248 fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) 249 { 250 unsigned char reply[FIDO_MAXMSG]; 251 int reply_len; 252 253 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, 254 (void *)ci, ms); 255 256 memset(ci, 0, sizeof(*ci)); 257 258 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 259 ms)) < 0) { 260 fido_log_debug("%s: fido_rx", __func__); 261 return (FIDO_ERR_RX); 262 } 263 264 return (cbor_parse_reply(reply, (size_t)reply_len, ci, 265 parse_reply_element)); 266 } 267 268 int 269 fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) 270 { 271 int r; 272 273 if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK || 274 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK) 275 return (r); 276 277 return (FIDO_OK); 278 } 279 280 int 281 fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 282 { 283 return (fido_dev_get_cbor_info_wait(dev, ci, -1)); 284 } 285 286 /* 287 * get/set functions for fido_cbor_info_t; always at the end of the file 288 */ 289 290 fido_cbor_info_t * 291 fido_cbor_info_new(void) 292 { 293 return (calloc(1, sizeof(fido_cbor_info_t))); 294 } 295 296 static void 297 free_str_array(fido_str_array_t *sa) 298 { 299 for (size_t i = 0; i < sa->len; i++) 300 free(sa->ptr[i]); 301 302 free(sa->ptr); 303 sa->ptr = NULL; 304 sa->len = 0; 305 } 306 307 static void 308 free_opt_array(fido_opt_array_t *oa) 309 { 310 for (size_t i = 0; i < oa->len; i++) 311 free(oa->name[i]); 312 313 free(oa->name); 314 free(oa->value); 315 oa->name = NULL; 316 oa->value = NULL; 317 } 318 319 static void 320 free_byte_array(fido_byte_array_t *ba) 321 { 322 free(ba->ptr); 323 324 ba->ptr = NULL; 325 ba->len = 0; 326 } 327 328 void 329 fido_cbor_info_free(fido_cbor_info_t **ci_p) 330 { 331 fido_cbor_info_t *ci; 332 333 if (ci_p == NULL || (ci = *ci_p) == NULL) 334 return; 335 336 free_str_array(&ci->versions); 337 free_str_array(&ci->extensions); 338 free_opt_array(&ci->options); 339 free_byte_array(&ci->protocols); 340 free(ci); 341 342 *ci_p = NULL; 343 } 344 345 char ** 346 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) 347 { 348 return (ci->versions.ptr); 349 } 350 351 size_t 352 fido_cbor_info_versions_len(const fido_cbor_info_t *ci) 353 { 354 return (ci->versions.len); 355 } 356 357 char ** 358 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) 359 { 360 return (ci->extensions.ptr); 361 } 362 363 size_t 364 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) 365 { 366 return (ci->extensions.len); 367 } 368 369 const unsigned char * 370 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) 371 { 372 return (ci->aaguid); 373 } 374 375 size_t 376 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) 377 { 378 return (sizeof(ci->aaguid)); 379 } 380 381 char ** 382 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) 383 { 384 return (ci->options.name); 385 } 386 387 const bool * 388 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) 389 { 390 return (ci->options.value); 391 } 392 393 size_t 394 fido_cbor_info_options_len(const fido_cbor_info_t *ci) 395 { 396 return (ci->options.len); 397 } 398 399 uint64_t 400 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) 401 { 402 return (ci->maxmsgsiz); 403 } 404 405 uint64_t 406 fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci) 407 { 408 return (ci->maxcredcntlst); 409 } 410 411 uint64_t 412 fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) 413 { 414 return (ci->maxcredidlen); 415 } 416 417 uint64_t 418 fido_cbor_info_fwversion(const fido_cbor_info_t *ci) 419 { 420 return (ci->fwversion); 421 } 422 423 const uint8_t * 424 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) 425 { 426 return (ci->protocols.ptr); 427 } 428 429 size_t 430 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) 431 { 432 return (ci->protocols.len); 433 } 434