xref: /freebsd/contrib/libfido2/src/info.c (revision 8f76bb7dad48538c6832c2fb466a433d2a3f8cd5)
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
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
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
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
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
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
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
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
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
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
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
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 14: /* fwVersion */
272 		return (cbor_decode_uint64(val, &ci->fwversion));
273 	case 15: /* maxCredBlobLen */
274 		return (cbor_decode_uint64(val, &ci->maxcredbloblen));
275 	default: /* ignore */
276 		fido_log_debug("%s: cbor type", __func__);
277 		return (0);
278 	}
279 }
280 
281 static int
282 fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
283 {
284 	const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
285 
286 	fido_log_debug("%s: dev=%p", __func__, (void *)dev);
287 
288 	if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
289 		fido_log_debug("%s: fido_tx", __func__);
290 		return (FIDO_ERR_TX);
291 	}
292 
293 	return (FIDO_OK);
294 }
295 
296 static int
297 fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
298 {
299 	unsigned char	reply[FIDO_MAXMSG];
300 	int		reply_len;
301 
302 	fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
303 	    (void *)ci, *ms);
304 
305 	fido_cbor_info_reset(ci);
306 
307 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
308 	    ms)) < 0) {
309 		fido_log_debug("%s: fido_rx", __func__);
310 		return (FIDO_ERR_RX);
311 	}
312 
313 	return (cbor_parse_reply(reply, (size_t)reply_len, ci,
314 	    parse_reply_element));
315 }
316 
317 int
318 fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
319 {
320 	int r;
321 
322 #ifdef USE_WINHELLO
323 	if (dev->flags & FIDO_DEV_WINHELLO)
324 		return (fido_winhello_get_cbor_info(dev, ci));
325 #endif
326 	if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK ||
327 	    (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
328 		return (r);
329 
330 	return (FIDO_OK);
331 }
332 
333 int
334 fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
335 {
336 	int ms = dev->timeout_ms;
337 
338 	return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
339 }
340 
341 /*
342  * get/set functions for fido_cbor_info_t; always at the end of the file
343  */
344 
345 fido_cbor_info_t *
346 fido_cbor_info_new(void)
347 {
348 	return (calloc(1, sizeof(fido_cbor_info_t)));
349 }
350 
351 void
352 fido_cbor_info_reset(fido_cbor_info_t *ci)
353 {
354 	fido_str_array_free(&ci->versions);
355 	fido_str_array_free(&ci->extensions);
356 	fido_str_array_free(&ci->transports);
357 	fido_opt_array_free(&ci->options);
358 	fido_byte_array_free(&ci->protocols);
359 	fido_algo_array_free(&ci->algorithms);
360 }
361 
362 void
363 fido_cbor_info_free(fido_cbor_info_t **ci_p)
364 {
365 	fido_cbor_info_t *ci;
366 
367 	if (ci_p == NULL || (ci = *ci_p) ==  NULL)
368 		return;
369 	fido_cbor_info_reset(ci);
370 	free(ci);
371 	*ci_p = NULL;
372 }
373 
374 char **
375 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
376 {
377 	return (ci->versions.ptr);
378 }
379 
380 size_t
381 fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
382 {
383 	return (ci->versions.len);
384 }
385 
386 char **
387 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
388 {
389 	return (ci->extensions.ptr);
390 }
391 
392 size_t
393 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
394 {
395 	return (ci->extensions.len);
396 }
397 
398 char **
399 fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
400 {
401 	return (ci->transports.ptr);
402 }
403 
404 size_t
405 fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
406 {
407 	return (ci->transports.len);
408 }
409 
410 const unsigned char *
411 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
412 {
413 	return (ci->aaguid);
414 }
415 
416 size_t
417 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
418 {
419 	return (sizeof(ci->aaguid));
420 }
421 
422 char **
423 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
424 {
425 	return (ci->options.name);
426 }
427 
428 const bool *
429 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
430 {
431 	return (ci->options.value);
432 }
433 
434 size_t
435 fido_cbor_info_options_len(const fido_cbor_info_t *ci)
436 {
437 	return (ci->options.len);
438 }
439 
440 uint64_t
441 fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
442 {
443 	return (ci->maxcredbloblen);
444 }
445 
446 uint64_t
447 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
448 {
449 	return (ci->maxmsgsiz);
450 }
451 
452 uint64_t
453 fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
454 {
455 	return (ci->maxcredcntlst);
456 }
457 
458 uint64_t
459 fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
460 {
461 	return (ci->maxcredidlen);
462 }
463 
464 uint64_t
465 fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
466 {
467 	return (ci->fwversion);
468 }
469 
470 const uint8_t *
471 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
472 {
473 	return (ci->protocols.ptr);
474 }
475 
476 size_t
477 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
478 {
479 	return (ci->protocols.len);
480 }
481 
482 size_t
483 fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
484 {
485 	return (ci->algorithms.len);
486 }
487 
488 const char *
489 fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx)
490 {
491 	if (idx >= ci->algorithms.len)
492 		return (NULL);
493 
494 	return (ci->algorithms.ptr[idx].type);
495 }
496 
497 int
498 fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx)
499 {
500 	if (idx >= ci->algorithms.len)
501 		return (0);
502 
503 	return (ci->algorithms.ptr[idx].cose);
504 }
505