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