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