1/*
2 * Copyright (c) 2018-2022 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "fido.h"
9
10static int
11decode_string(const cbor_item_t *item, void *arg)
12{
13	fido_str_array_t	*a = arg;
14	const size_t		 i = a->len;
15
16	/* keep ptr[x] and len consistent */
17	if (cbor_string_copy(item, &a->ptr[i]) < 0) {
18		fido_log_debug("%s: cbor_string_copy", __func__);
19		return (-1);
20	}
21
22	a->len++;
23
24	return (0);
25}
26
27static int
28decode_string_array(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_string) < 0) {
44		fido_log_debug("%s: decode_string", __func__);
45		return (-1);
46	}
47
48	return (0);
49}
50
51static int
52decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
53{
54	if (cbor_isa_bytestring(item) == false ||
55	    cbor_bytestring_is_definite(item) == false ||
56	    cbor_bytestring_length(item) != aaguid_len) {
57		fido_log_debug("%s: cbor type", __func__);
58		return (-1);
59	}
60
61	memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
62
63	return (0);
64}
65
66static int
67decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
68{
69	fido_opt_array_t	*o = arg;
70	const size_t		 i = o->len;
71
72	if (cbor_decode_bool(val, NULL) < 0) {
73		fido_log_debug("%s: cbor_decode_bool", __func__);
74		return (0); /* ignore */
75	}
76
77	if (cbor_string_copy(key, &o->name[i]) < 0) {
78		fido_log_debug("%s: cbor_string_copy", __func__);
79		return (0); /* ignore */
80	}
81
82	/* keep name/value and len consistent */
83	o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
84	o->len++;
85
86	return (0);
87}
88
89static int
90decode_options(const cbor_item_t *item, fido_opt_array_t *o)
91{
92	o->name = NULL;
93	o->value = NULL;
94	o->len = 0;
95
96	if (cbor_isa_map(item) == false ||
97	    cbor_map_is_definite(item) == false) {
98		fido_log_debug("%s: cbor type", __func__);
99		return (-1);
100	}
101
102	o->name = calloc(cbor_map_size(item), sizeof(char *));
103	o->value = calloc(cbor_map_size(item), sizeof(bool));
104	if (o->name == NULL || o->value == NULL)
105		return (-1);
106
107	return (cbor_map_iter(item, o, decode_option));
108}
109
110static int
111decode_protocol(const cbor_item_t *item, void *arg)
112{
113	fido_byte_array_t	*p = arg;
114	const size_t		 i = p->len;
115
116	if (cbor_isa_uint(item) == false ||
117	    cbor_int_get_width(item) != CBOR_INT_8) {
118		fido_log_debug("%s: cbor type", __func__);
119		return (-1);
120	}
121
122	/* keep ptr[x] and len consistent */
123	p->ptr[i] = cbor_get_uint8(item);
124	p->len++;
125
126	return (0);
127}
128
129static int
130decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
131{
132	p->ptr = NULL;
133	p->len = 0;
134
135	if (cbor_isa_array(item) == false ||
136	    cbor_array_is_definite(item) == false) {
137		fido_log_debug("%s: cbor type", __func__);
138		return (-1);
139	}
140
141	p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
142	if (p->ptr == NULL)
143		return (-1);
144
145	if (cbor_array_iter(item, p, decode_protocol) < 0) {
146		fido_log_debug("%s: decode_protocol", __func__);
147		return (-1);
148	}
149
150	return (0);
151}
152
153static int
154decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val,
155    void *arg)
156{
157	fido_algo_t *alg = arg;
158	char *name = NULL;
159	int ok = -1;
160
161	if (cbor_string_copy(key, &name) < 0) {
162		fido_log_debug("%s: cbor type", __func__);
163		ok = 0; /* ignore */
164		goto out;
165	}
166
167	if (!strcmp(name, "alg")) {
168		if (cbor_isa_negint(val) == false ||
169		    cbor_get_int(val) > INT_MAX || alg->cose != 0) {
170			fido_log_debug("%s: alg", __func__);
171			goto out;
172		}
173		alg->cose = -(int)cbor_get_int(val) - 1;
174	} else if (!strcmp(name, "type")) {
175		if (cbor_string_copy(val, &alg->type) < 0) {
176			fido_log_debug("%s: type", __func__);
177			goto out;
178		}
179	}
180
181	ok = 0;
182out:
183	free(name);
184
185	return (ok);
186}
187
188static int
189decode_algorithm(const cbor_item_t *item, void *arg)
190{
191	fido_algo_array_t *aa = arg;
192	const size_t i = aa->len;
193
194	if (cbor_isa_map(item) == false ||
195	    cbor_map_is_definite(item) == false) {
196		fido_log_debug("%s: cbor type", __func__);
197		return (-1);
198	}
199
200	memset(&aa->ptr[i], 0, sizeof(aa->ptr[i]));
201
202	if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) {
203		fido_log_debug("%s: decode_algorithm_entry", __func__);
204		fido_algo_free(&aa->ptr[i]);
205		return (-1);
206	}
207
208	/* keep ptr[x] and len consistent */
209	aa->len++;
210
211	return (0);
212}
213
214static int
215decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa)
216{
217	aa->ptr = NULL;
218	aa->len = 0;
219
220	if (cbor_isa_array(item) == false ||
221	    cbor_array_is_definite(item) == false) {
222		fido_log_debug("%s: cbor type", __func__);
223		return (-1);
224	}
225
226	aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t));
227	if (aa->ptr == NULL)
228		return (-1);
229
230	if (cbor_array_iter(item, aa, decode_algorithm) < 0) {
231		fido_log_debug("%s: decode_algorithm", __func__);
232		return (-1);
233	}
234
235	return (0);
236}
237
238static int
239decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg)
240{
241	fido_cert_array_t	*c = arg;
242	const size_t		 i = c->len;
243
244	if (cbor_is_int(val) == false) {
245		fido_log_debug("%s: cbor_is_int", __func__);
246		return (0); /* ignore */
247	}
248
249	if (cbor_string_copy(key, &c->name[i]) < 0) {
250		fido_log_debug("%s: cbor_string_copy", __func__);
251		return (0); /* ignore */
252	}
253
254	/* keep name/value and len consistent */
255	c->value[i] = cbor_get_int(val);
256	c->len++;
257
258	return (0);
259}
260
261static int
262decode_certs(const cbor_item_t *item, fido_cert_array_t *c)
263{
264	c->name = NULL;
265	c->value = NULL;
266	c->len = 0;
267
268	if (cbor_isa_map(item) == false ||
269	    cbor_map_is_definite(item) == false) {
270		fido_log_debug("%s: cbor type", __func__);
271		return (-1);
272	}
273
274	c->name = calloc(cbor_map_size(item), sizeof(char *));
275	c->value = calloc(cbor_map_size(item), sizeof(uint64_t));
276	if (c->name == NULL || c->value == NULL)
277		return (-1);
278
279	return (cbor_map_iter(item, c, decode_cert));
280}
281
282static int
283parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
284{
285	fido_cbor_info_t *ci = arg;
286	uint64_t x;
287
288	if (cbor_isa_uint(key) == false ||
289	    cbor_int_get_width(key) != CBOR_INT_8) {
290		fido_log_debug("%s: cbor type", __func__);
291		return (0); /* ignore */
292	}
293
294	switch (cbor_get_uint8(key)) {
295	case 1: /* versions */
296		return (decode_string_array(val, &ci->versions));
297	case 2: /* extensions */
298		return (decode_string_array(val, &ci->extensions));
299	case 3: /* aaguid */
300		return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
301	case 4: /* options */
302		return (decode_options(val, &ci->options));
303	case 5: /* maxMsgSize */
304		return (cbor_decode_uint64(val, &ci->maxmsgsiz));
305	case 6: /* pinProtocols */
306		return (decode_protocols(val, &ci->protocols));
307	case 7: /* maxCredentialCountInList */
308		return (cbor_decode_uint64(val, &ci->maxcredcntlst));
309	case 8: /* maxCredentialIdLength */
310		return (cbor_decode_uint64(val, &ci->maxcredidlen));
311	case 9: /* transports */
312		return (decode_string_array(val, &ci->transports));
313	case 10: /* algorithms */
314		return (decode_algorithms(val, &ci->algorithms));
315	case 11: /* maxSerializedLargeBlobArray */
316		return (cbor_decode_uint64(val, &ci->maxlargeblob));
317	case 12: /* forcePINChange */
318		return (cbor_decode_bool(val, &ci->new_pin_reqd));
319	case 13: /* minPINLength */
320		return (cbor_decode_uint64(val, &ci->minpinlen));
321	case 14: /* fwVersion */
322		return (cbor_decode_uint64(val, &ci->fwversion));
323	case 15: /* maxCredBlobLen */
324		return (cbor_decode_uint64(val, &ci->maxcredbloblen));
325	case 16: /* maxRPIDsForSetMinPINLength */
326		return (cbor_decode_uint64(val, &ci->maxrpid_minlen));
327	case 17: /* preferredPlatformUvAttempts */
328		return (cbor_decode_uint64(val, &ci->uv_attempts));
329	case 18: /* uvModality */
330		return (cbor_decode_uint64(val, &ci->uv_modality));
331	case 19: /* certifications */
332		return (decode_certs(val, &ci->certs));
333	case 20: /* remainingDiscoverableCredentials */
334		if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) {
335			fido_log_debug("%s: cbor_decode_uint64", __func__);
336			return (-1);
337		}
338		ci->rk_remaining = (int64_t)x;
339		return (0);
340	default: /* ignore */
341		fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key));
342		return (0);
343	}
344}
345
346static int
347fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
348{
349	const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
350
351	fido_log_debug("%s: dev=%p", __func__, (void *)dev);
352
353	if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
354		fido_log_debug("%s: fido_tx", __func__);
355		return (FIDO_ERR_TX);
356	}
357
358	return (FIDO_OK);
359}
360
361static int
362fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
363{
364	unsigned char	*msg;
365	int		 msglen;
366	int		 r;
367
368	fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
369	    (void *)ci, *ms);
370
371	fido_cbor_info_reset(ci);
372
373	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
374		r = FIDO_ERR_INTERNAL;
375		goto out;
376	}
377
378	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
379		fido_log_debug("%s: fido_rx", __func__);
380		r = FIDO_ERR_RX;
381		goto out;
382	}
383
384	r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element);
385out:
386	freezero(msg, FIDO_MAXMSG);
387
388	return (r);
389}
390
391int
392fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
393{
394	int r;
395
396#ifdef USE_WINHELLO
397	if (dev->flags & FIDO_DEV_WINHELLO)
398		return (fido_winhello_get_cbor_info(dev, ci));
399#endif
400	if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK ||
401	    (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
402		return (r);
403
404	return (FIDO_OK);
405}
406
407int
408fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
409{
410	int ms = dev->timeout_ms;
411
412	return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
413}
414
415/*
416 * get/set functions for fido_cbor_info_t; always at the end of the file
417 */
418
419fido_cbor_info_t *
420fido_cbor_info_new(void)
421{
422	fido_cbor_info_t *ci;
423
424	if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL)
425		return (NULL);
426
427	fido_cbor_info_reset(ci);
428
429	return (ci);
430}
431
432void
433fido_cbor_info_reset(fido_cbor_info_t *ci)
434{
435	fido_str_array_free(&ci->versions);
436	fido_str_array_free(&ci->extensions);
437	fido_str_array_free(&ci->transports);
438	fido_opt_array_free(&ci->options);
439	fido_byte_array_free(&ci->protocols);
440	fido_algo_array_free(&ci->algorithms);
441	fido_cert_array_free(&ci->certs);
442	ci->rk_remaining = -1;
443}
444
445void
446fido_cbor_info_free(fido_cbor_info_t **ci_p)
447{
448	fido_cbor_info_t *ci;
449
450	if (ci_p == NULL || (ci = *ci_p) ==  NULL)
451		return;
452	fido_cbor_info_reset(ci);
453	free(ci);
454	*ci_p = NULL;
455}
456
457char **
458fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
459{
460	return (ci->versions.ptr);
461}
462
463size_t
464fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
465{
466	return (ci->versions.len);
467}
468
469char **
470fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
471{
472	return (ci->extensions.ptr);
473}
474
475size_t
476fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
477{
478	return (ci->extensions.len);
479}
480
481char **
482fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
483{
484	return (ci->transports.ptr);
485}
486
487size_t
488fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
489{
490	return (ci->transports.len);
491}
492
493const unsigned char *
494fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
495{
496	return (ci->aaguid);
497}
498
499size_t
500fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
501{
502	return (sizeof(ci->aaguid));
503}
504
505char **
506fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
507{
508	return (ci->options.name);
509}
510
511const bool *
512fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
513{
514	return (ci->options.value);
515}
516
517size_t
518fido_cbor_info_options_len(const fido_cbor_info_t *ci)
519{
520	return (ci->options.len);
521}
522
523uint64_t
524fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
525{
526	return (ci->maxcredbloblen);
527}
528
529uint64_t
530fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
531{
532	return (ci->maxmsgsiz);
533}
534
535uint64_t
536fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
537{
538	return (ci->maxcredcntlst);
539}
540
541uint64_t
542fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
543{
544	return (ci->maxcredidlen);
545}
546
547uint64_t
548fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
549{
550	return (ci->maxlargeblob);
551}
552
553uint64_t
554fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
555{
556	return (ci->fwversion);
557}
558
559uint64_t
560fido_cbor_info_minpinlen(const fido_cbor_info_t *ci)
561{
562	return (ci->minpinlen);
563}
564
565uint64_t
566fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci)
567{
568	return (ci->maxrpid_minlen);
569}
570
571uint64_t
572fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci)
573{
574	return (ci->uv_attempts);
575}
576
577uint64_t
578fido_cbor_info_uv_modality(const fido_cbor_info_t *ci)
579{
580	return (ci->uv_modality);
581}
582
583int64_t
584fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci)
585{
586	return (ci->rk_remaining);
587}
588
589const uint8_t *
590fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
591{
592	return (ci->protocols.ptr);
593}
594
595size_t
596fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
597{
598	return (ci->protocols.len);
599}
600
601size_t
602fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
603{
604	return (ci->algorithms.len);
605}
606
607const char *
608fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx)
609{
610	if (idx >= ci->algorithms.len)
611		return (NULL);
612
613	return (ci->algorithms.ptr[idx].type);
614}
615
616int
617fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx)
618{
619	if (idx >= ci->algorithms.len)
620		return (0);
621
622	return (ci->algorithms.ptr[idx].cose);
623}
624
625bool
626fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci)
627{
628	return (ci->new_pin_reqd);
629}
630
631char **
632fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci)
633{
634	return (ci->certs.name);
635}
636
637const uint64_t *
638fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci)
639{
640	return (ci->certs.value);
641}
642
643size_t
644fido_cbor_info_certs_len(const fido_cbor_info_t *ci)
645{
646	return (ci->certs.len);
647}
648