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 default: /* ignore */ 221 fido_log_debug("%s: cbor type", __func__); 222 return (0); 223 } 224 } 225 226 static int 227 fido_dev_get_cbor_info_tx(fido_dev_t *dev) 228 { 229 const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; 230 231 fido_log_debug("%s: dev=%p", __func__, (void *)dev); 232 233 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { 234 fido_log_debug("%s: fido_tx", __func__); 235 return (FIDO_ERR_TX); 236 } 237 238 return (FIDO_OK); 239 } 240 241 static int 242 fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) 243 { 244 unsigned char reply[FIDO_MAXMSG]; 245 int reply_len; 246 247 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, 248 (void *)ci, ms); 249 250 memset(ci, 0, sizeof(*ci)); 251 252 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 253 ms)) < 0) { 254 fido_log_debug("%s: fido_rx", __func__); 255 return (FIDO_ERR_RX); 256 } 257 258 return (cbor_parse_reply(reply, (size_t)reply_len, ci, 259 parse_reply_element)); 260 } 261 262 int 263 fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) 264 { 265 int r; 266 267 if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK || 268 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK) 269 return (r); 270 271 return (FIDO_OK); 272 } 273 274 int 275 fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 276 { 277 return (fido_dev_get_cbor_info_wait(dev, ci, -1)); 278 } 279 280 /* 281 * get/set functions for fido_cbor_info_t; always at the end of the file 282 */ 283 284 fido_cbor_info_t * 285 fido_cbor_info_new(void) 286 { 287 return (calloc(1, sizeof(fido_cbor_info_t))); 288 } 289 290 static void 291 free_str_array(fido_str_array_t *sa) 292 { 293 for (size_t i = 0; i < sa->len; i++) 294 free(sa->ptr[i]); 295 296 free(sa->ptr); 297 sa->ptr = NULL; 298 sa->len = 0; 299 } 300 301 static void 302 free_opt_array(fido_opt_array_t *oa) 303 { 304 for (size_t i = 0; i < oa->len; i++) 305 free(oa->name[i]); 306 307 free(oa->name); 308 free(oa->value); 309 oa->name = NULL; 310 oa->value = NULL; 311 } 312 313 static void 314 free_byte_array(fido_byte_array_t *ba) 315 { 316 free(ba->ptr); 317 318 ba->ptr = NULL; 319 ba->len = 0; 320 } 321 322 void 323 fido_cbor_info_free(fido_cbor_info_t **ci_p) 324 { 325 fido_cbor_info_t *ci; 326 327 if (ci_p == NULL || (ci = *ci_p) == NULL) 328 return; 329 330 free_str_array(&ci->versions); 331 free_str_array(&ci->extensions); 332 free_opt_array(&ci->options); 333 free_byte_array(&ci->protocols); 334 free(ci); 335 336 *ci_p = NULL; 337 } 338 339 char ** 340 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) 341 { 342 return (ci->versions.ptr); 343 } 344 345 size_t 346 fido_cbor_info_versions_len(const fido_cbor_info_t *ci) 347 { 348 return (ci->versions.len); 349 } 350 351 char ** 352 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) 353 { 354 return (ci->extensions.ptr); 355 } 356 357 size_t 358 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) 359 { 360 return (ci->extensions.len); 361 } 362 363 const unsigned char * 364 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) 365 { 366 return (ci->aaguid); 367 } 368 369 size_t 370 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) 371 { 372 return (sizeof(ci->aaguid)); 373 } 374 375 char ** 376 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) 377 { 378 return (ci->options.name); 379 } 380 381 const bool * 382 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) 383 { 384 return (ci->options.value); 385 } 386 387 size_t 388 fido_cbor_info_options_len(const fido_cbor_info_t *ci) 389 { 390 return (ci->options.len); 391 } 392 393 uint64_t 394 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) 395 { 396 return (ci->maxmsgsiz); 397 } 398 399 const uint8_t * 400 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) 401 { 402 return (ci->protocols.ptr); 403 } 404 405 size_t 406 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) 407 { 408 return (ci->protocols.len); 409 } 410