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