diff options
Diffstat (limited to 'src/bio.c')
-rw-r--r-- | src/bio.c | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/src/bio.c b/src/bio.c new file mode 100644 index 0000000..74814b9 --- /dev/null +++ b/src/bio.c | |||
@@ -0,0 +1,844 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2019 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 | |||
9 | #include "fido.h" | ||
10 | #include "fido/bio.h" | ||
11 | #include "fido/es256.h" | ||
12 | |||
13 | #define CMD_ENROLL_BEGIN 0x01 | ||
14 | #define CMD_ENROLL_NEXT 0x02 | ||
15 | #define CMD_ENROLL_CANCEL 0x03 | ||
16 | #define CMD_ENUM 0x04 | ||
17 | #define CMD_SET_NAME 0x05 | ||
18 | #define CMD_ENROLL_REMOVE 0x06 | ||
19 | #define CMD_GET_INFO 0x07 | ||
20 | |||
21 | static int | ||
22 | bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc, | ||
23 | cbor_item_t **param, fido_blob_t *hmac_data) | ||
24 | { | ||
25 | const uint8_t prefix[2] = { 0x01 /* modality */, cmd }; | ||
26 | int ok = -1; | ||
27 | size_t cbor_alloc_len; | ||
28 | size_t cbor_len; | ||
29 | unsigned char *cbor = NULL; | ||
30 | |||
31 | if (argv == NULL || param == NULL) | ||
32 | return (fido_blob_set(hmac_data, prefix, sizeof(prefix))); | ||
33 | |||
34 | if ((*param = cbor_flatten_vector(argv, argc)) == NULL) { | ||
35 | fido_log_debug("%s: cbor_flatten_vector", __func__); | ||
36 | goto fail; | ||
37 | } | ||
38 | |||
39 | if ((cbor_len = cbor_serialize_alloc(*param, &cbor, | ||
40 | &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) { | ||
41 | fido_log_debug("%s: cbor_serialize_alloc", __func__); | ||
42 | goto fail; | ||
43 | } | ||
44 | |||
45 | if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) { | ||
46 | fido_log_debug("%s: malloc", __func__); | ||
47 | goto fail; | ||
48 | } | ||
49 | |||
50 | memcpy(hmac_data->ptr, prefix, sizeof(prefix)); | ||
51 | memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len); | ||
52 | hmac_data->len = cbor_len + sizeof(prefix); | ||
53 | |||
54 | ok = 0; | ||
55 | fail: | ||
56 | free(cbor); | ||
57 | |||
58 | return (ok); | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc, | ||
63 | const char *pin, const fido_blob_t *token) | ||
64 | { | ||
65 | cbor_item_t *argv[5]; | ||
66 | es256_pk_t *pk = NULL; | ||
67 | fido_blob_t *ecdh = NULL; | ||
68 | fido_blob_t f; | ||
69 | fido_blob_t hmac; | ||
70 | int r = FIDO_ERR_INTERNAL; | ||
71 | |||
72 | memset(&f, 0, sizeof(f)); | ||
73 | memset(&hmac, 0, sizeof(hmac)); | ||
74 | memset(&argv, 0, sizeof(argv)); | ||
75 | |||
76 | /* modality, subCommand */ | ||
77 | if ((argv[0] = cbor_build_uint8(1)) == NULL || | ||
78 | (argv[1] = cbor_build_uint8(cmd)) == NULL) { | ||
79 | fido_log_debug("%s: cbor encode", __func__); | ||
80 | goto fail; | ||
81 | } | ||
82 | |||
83 | /* subParams */ | ||
84 | if (pin || token) { | ||
85 | if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2], | ||
86 | &hmac) < 0) { | ||
87 | fido_log_debug("%s: bio_prepare_hmac", __func__); | ||
88 | goto fail; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /* pinProtocol, pinAuth */ | ||
93 | if (pin) { | ||
94 | if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { | ||
95 | fido_log_debug("%s: fido_do_ecdh", __func__); | ||
96 | goto fail; | ||
97 | } | ||
98 | if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin, | ||
99 | &argv[4], &argv[3])) != FIDO_OK) { | ||
100 | fido_log_debug("%s: cbor_add_pin_params", __func__); | ||
101 | goto fail; | ||
102 | } | ||
103 | } else if (token) { | ||
104 | if ((argv[3] = cbor_encode_pin_opt()) == NULL || | ||
105 | (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) { | ||
106 | fido_log_debug("%s: encode pin", __func__); | ||
107 | goto fail; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* framing and transmission */ | ||
112 | if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 || | ||
113 | fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { | ||
114 | fido_log_debug("%s: fido_tx", __func__); | ||
115 | r = FIDO_ERR_TX; | ||
116 | goto fail; | ||
117 | } | ||
118 | |||
119 | r = FIDO_OK; | ||
120 | fail: | ||
121 | cbor_vector_free(argv, nitems(argv)); | ||
122 | es256_pk_free(&pk); | ||
123 | fido_blob_free(&ecdh); | ||
124 | free(f.ptr); | ||
125 | free(hmac.ptr); | ||
126 | |||
127 | return (r); | ||
128 | } | ||
129 | |||
130 | static void | ||
131 | bio_reset_template(fido_bio_template_t *t) | ||
132 | { | ||
133 | free(t->name); | ||
134 | free(t->id.ptr); | ||
135 | t->name = NULL; | ||
136 | memset(&t->id, 0, sizeof(t->id)); | ||
137 | } | ||
138 | |||
139 | static void | ||
140 | bio_reset_template_array(fido_bio_template_array_t *ta) | ||
141 | { | ||
142 | for (size_t i = 0; i < ta->n_alloc; i++) | ||
143 | bio_reset_template(&ta->ptr[i]); | ||
144 | |||
145 | free(ta->ptr); | ||
146 | ta->ptr = NULL; | ||
147 | memset(ta, 0, sizeof(*ta)); | ||
148 | } | ||
149 | |||
150 | static int | ||
151 | decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
152 | { | ||
153 | fido_bio_template_t *t = arg; | ||
154 | |||
155 | if (cbor_isa_uint(key) == false || | ||
156 | cbor_int_get_width(key) != CBOR_INT_8) { | ||
157 | fido_log_debug("%s: cbor type", __func__); | ||
158 | return (0); /* ignore */ | ||
159 | } | ||
160 | |||
161 | switch (cbor_get_uint8(key)) { | ||
162 | case 1: /* id */ | ||
163 | return (fido_blob_decode(val, &t->id)); | ||
164 | case 2: /* name */ | ||
165 | return (cbor_string_copy(val, &t->name)); | ||
166 | } | ||
167 | |||
168 | return (0); /* ignore */ | ||
169 | } | ||
170 | |||
171 | static int | ||
172 | decode_template_array(const cbor_item_t *item, void *arg) | ||
173 | { | ||
174 | fido_bio_template_array_t *ta = arg; | ||
175 | |||
176 | if (cbor_isa_map(item) == false || | ||
177 | cbor_map_is_definite(item) == false) { | ||
178 | fido_log_debug("%s: cbor type", __func__); | ||
179 | return (-1); | ||
180 | } | ||
181 | |||
182 | if (ta->n_rx >= ta->n_alloc) { | ||
183 | fido_log_debug("%s: n_rx >= n_alloc", __func__); | ||
184 | return (-1); | ||
185 | } | ||
186 | |||
187 | if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) { | ||
188 | fido_log_debug("%s: decode_template", __func__); | ||
189 | return (-1); | ||
190 | } | ||
191 | |||
192 | ta->n_rx++; | ||
193 | |||
194 | return (0); | ||
195 | } | ||
196 | |||
197 | static int | ||
198 | bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val, | ||
199 | void *arg) | ||
200 | { | ||
201 | fido_bio_template_array_t *ta = arg; | ||
202 | |||
203 | if (cbor_isa_uint(key) == false || | ||
204 | cbor_int_get_width(key) != CBOR_INT_8 || | ||
205 | cbor_get_uint8(key) != 7) { | ||
206 | fido_log_debug("%s: cbor type", __func__); | ||
207 | return (0); /* ignore */ | ||
208 | } | ||
209 | |||
210 | if (cbor_isa_array(val) == false || | ||
211 | cbor_array_is_definite(val) == false) { | ||
212 | fido_log_debug("%s: cbor type", __func__); | ||
213 | return (-1); | ||
214 | } | ||
215 | |||
216 | if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) { | ||
217 | fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0", | ||
218 | __func__); | ||
219 | return (-1); | ||
220 | } | ||
221 | |||
222 | if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL) | ||
223 | return (-1); | ||
224 | |||
225 | ta->n_alloc = cbor_array_size(val); | ||
226 | |||
227 | if (cbor_array_iter(val, ta, decode_template_array) < 0) { | ||
228 | fido_log_debug("%s: decode_template_array", __func__); | ||
229 | return (-1); | ||
230 | } | ||
231 | |||
232 | return (0); | ||
233 | } | ||
234 | |||
235 | static int | ||
236 | bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms) | ||
237 | { | ||
238 | const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; | ||
239 | unsigned char reply[2048]; | ||
240 | int reply_len; | ||
241 | int r; | ||
242 | |||
243 | bio_reset_template_array(ta); | ||
244 | |||
245 | if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { | ||
246 | fido_log_debug("%s: fido_rx", __func__); | ||
247 | return (FIDO_ERR_RX); | ||
248 | } | ||
249 | |||
250 | if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta, | ||
251 | bio_parse_template_array)) != FIDO_OK) { | ||
252 | fido_log_debug("%s: bio_parse_template_array" , __func__); | ||
253 | return (r); | ||
254 | } | ||
255 | |||
256 | return (FIDO_OK); | ||
257 | } | ||
258 | |||
259 | static int | ||
260 | bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta, | ||
261 | const char *pin, int ms) | ||
262 | { | ||
263 | int r; | ||
264 | |||
265 | if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK || | ||
266 | (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK) | ||
267 | return (r); | ||
268 | |||
269 | return (FIDO_OK); | ||
270 | } | ||
271 | |||
272 | int | ||
273 | fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, | ||
274 | const char *pin) | ||
275 | { | ||
276 | if (pin == NULL) | ||
277 | return (FIDO_ERR_INVALID_ARGUMENT); | ||
278 | |||
279 | return (bio_get_template_array_wait(dev, ta, pin, -1)); | ||
280 | } | ||
281 | |||
282 | static int | ||
283 | bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t, | ||
284 | const char *pin, int ms) | ||
285 | { | ||
286 | cbor_item_t *argv[2]; | ||
287 | int r = FIDO_ERR_INTERNAL; | ||
288 | |||
289 | memset(&argv, 0, sizeof(argv)); | ||
290 | |||
291 | if ((argv[0] = fido_blob_encode(&t->id)) == NULL || | ||
292 | (argv[1] = cbor_build_string(t->name)) == NULL) { | ||
293 | fido_log_debug("%s: cbor encode", __func__); | ||
294 | goto fail; | ||
295 | } | ||
296 | |||
297 | if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK || | ||
298 | (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { | ||
299 | fido_log_debug("%s: tx/rx", __func__); | ||
300 | goto fail; | ||
301 | } | ||
302 | |||
303 | r = FIDO_OK; | ||
304 | fail: | ||
305 | cbor_vector_free(argv, nitems(argv)); | ||
306 | |||
307 | return (r); | ||
308 | } | ||
309 | |||
310 | int | ||
311 | fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t, | ||
312 | const char *pin) | ||
313 | { | ||
314 | if (pin == NULL || t->name == NULL) | ||
315 | return (FIDO_ERR_INVALID_ARGUMENT); | ||
316 | |||
317 | return (bio_set_template_name_wait(dev, t, pin, -1)); | ||
318 | } | ||
319 | |||
320 | static void | ||
321 | bio_reset_enroll(fido_bio_enroll_t *e) | ||
322 | { | ||
323 | e->remaining_samples = 0; | ||
324 | e->last_status = 0; | ||
325 | |||
326 | if (e->token) | ||
327 | fido_blob_free(&e->token); | ||
328 | } | ||
329 | |||
330 | static int | ||
331 | bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val, | ||
332 | void *arg) | ||
333 | { | ||
334 | fido_bio_enroll_t *e = arg; | ||
335 | uint64_t x; | ||
336 | |||
337 | if (cbor_isa_uint(key) == false || | ||
338 | cbor_int_get_width(key) != CBOR_INT_8) { | ||
339 | fido_log_debug("%s: cbor type", __func__); | ||
340 | return (0); /* ignore */ | ||
341 | } | ||
342 | |||
343 | switch (cbor_get_uint8(key)) { | ||
344 | case 5: | ||
345 | if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { | ||
346 | fido_log_debug("%s: cbor_decode_uint64", __func__); | ||
347 | return (-1); | ||
348 | } | ||
349 | e->last_status = (uint8_t)x; | ||
350 | break; | ||
351 | case 6: | ||
352 | if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { | ||
353 | fido_log_debug("%s: cbor_decode_uint64", __func__); | ||
354 | return (-1); | ||
355 | } | ||
356 | e->remaining_samples = (uint8_t)x; | ||
357 | break; | ||
358 | default: | ||
359 | return (0); /* ignore */ | ||
360 | } | ||
361 | |||
362 | return (0); | ||
363 | } | ||
364 | |||
365 | static int | ||
366 | bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val, | ||
367 | void *arg) | ||
368 | { | ||
369 | fido_blob_t *id = arg; | ||
370 | |||
371 | if (cbor_isa_uint(key) == false || | ||
372 | cbor_int_get_width(key) != CBOR_INT_8 || | ||
373 | cbor_get_uint8(key) != 4) { | ||
374 | fido_log_debug("%s: cbor type", __func__); | ||
375 | return (0); /* ignore */ | ||
376 | } | ||
377 | |||
378 | return (fido_blob_decode(val, id)); | ||
379 | } | ||
380 | |||
381 | static int | ||
382 | bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, | ||
383 | fido_bio_enroll_t *e, int ms) | ||
384 | { | ||
385 | const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; | ||
386 | unsigned char reply[2048]; | ||
387 | int reply_len; | ||
388 | int r; | ||
389 | |||
390 | bio_reset_template(t); | ||
391 | |||
392 | e->remaining_samples = 0; | ||
393 | e->last_status = 0; | ||
394 | |||
395 | if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { | ||
396 | fido_log_debug("%s: fido_rx", __func__); | ||
397 | return (FIDO_ERR_RX); | ||
398 | } | ||
399 | |||
400 | if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, | ||
401 | bio_parse_enroll_status)) != FIDO_OK) { | ||
402 | fido_log_debug("%s: bio_parse_enroll_status", __func__); | ||
403 | return (r); | ||
404 | } | ||
405 | if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id, | ||
406 | bio_parse_template_id)) != FIDO_OK) { | ||
407 | fido_log_debug("%s: bio_parse_template_id", __func__); | ||
408 | return (r); | ||
409 | } | ||
410 | |||
411 | return (FIDO_OK); | ||
412 | } | ||
413 | |||
414 | static int | ||
415 | bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t, | ||
416 | fido_bio_enroll_t *e, uint32_t timo_ms, int ms) | ||
417 | { | ||
418 | cbor_item_t *argv[3]; | ||
419 | const uint8_t cmd = CMD_ENROLL_BEGIN; | ||
420 | int r = FIDO_ERR_INTERNAL; | ||
421 | |||
422 | memset(&argv, 0, sizeof(argv)); | ||
423 | |||
424 | if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) { | ||
425 | fido_log_debug("%s: cbor encode", __func__); | ||
426 | goto fail; | ||
427 | } | ||
428 | |||
429 | if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK || | ||
430 | (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) { | ||
431 | fido_log_debug("%s: tx/rx", __func__); | ||
432 | goto fail; | ||
433 | } | ||
434 | |||
435 | r = FIDO_OK; | ||
436 | fail: | ||
437 | cbor_vector_free(argv, nitems(argv)); | ||
438 | |||
439 | return (r); | ||
440 | } | ||
441 | |||
442 | int | ||
443 | fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, | ||
444 | fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin) | ||
445 | { | ||
446 | es256_pk_t *pk = NULL; | ||
447 | fido_blob_t *ecdh = NULL; | ||
448 | fido_blob_t *token = NULL; | ||
449 | int r; | ||
450 | |||
451 | if (pin == NULL || e->token != NULL) | ||
452 | return (FIDO_ERR_INVALID_ARGUMENT); | ||
453 | |||
454 | if ((token = fido_blob_new()) == NULL) { | ||
455 | r = FIDO_ERR_INTERNAL; | ||
456 | goto fail; | ||
457 | } | ||
458 | |||
459 | if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { | ||
460 | fido_log_debug("%s: fido_do_ecdh", __func__); | ||
461 | goto fail; | ||
462 | } | ||
463 | |||
464 | if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) { | ||
465 | fido_log_debug("%s: fido_dev_get_pin_token", __func__); | ||
466 | goto fail; | ||
467 | } | ||
468 | |||
469 | e->token = token; | ||
470 | token = NULL; | ||
471 | fail: | ||
472 | es256_pk_free(&pk); | ||
473 | fido_blob_free(&ecdh); | ||
474 | fido_blob_free(&token); | ||
475 | |||
476 | if (r != FIDO_OK) | ||
477 | return (r); | ||
478 | |||
479 | return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1)); | ||
480 | } | ||
481 | |||
482 | static int | ||
483 | bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms) | ||
484 | { | ||
485 | const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; | ||
486 | unsigned char reply[2048]; | ||
487 | int reply_len; | ||
488 | int r; | ||
489 | |||
490 | e->remaining_samples = 0; | ||
491 | e->last_status = 0; | ||
492 | |||
493 | if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { | ||
494 | fido_log_debug("%s: fido_rx", __func__); | ||
495 | return (FIDO_ERR_RX); | ||
496 | } | ||
497 | |||
498 | if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, | ||
499 | bio_parse_enroll_status)) != FIDO_OK) { | ||
500 | fido_log_debug("%s: bio_parse_enroll_status", __func__); | ||
501 | return (r); | ||
502 | } | ||
503 | |||
504 | return (FIDO_OK); | ||
505 | } | ||
506 | |||
507 | static int | ||
508 | bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t, | ||
509 | fido_bio_enroll_t *e, uint32_t timo_ms, int ms) | ||
510 | { | ||
511 | cbor_item_t *argv[3]; | ||
512 | const uint8_t cmd = CMD_ENROLL_NEXT; | ||
513 | int r = FIDO_ERR_INTERNAL; | ||
514 | |||
515 | memset(&argv, 0, sizeof(argv)); | ||
516 | |||
517 | if ((argv[0] = fido_blob_encode(&t->id)) == NULL || | ||
518 | (argv[2] = cbor_build_uint32(timo_ms)) == NULL) { | ||
519 | fido_log_debug("%s: cbor encode", __func__); | ||
520 | goto fail; | ||
521 | } | ||
522 | |||
523 | if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK || | ||
524 | (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) { | ||
525 | fido_log_debug("%s: tx/rx", __func__); | ||
526 | goto fail; | ||
527 | } | ||
528 | |||
529 | r = FIDO_OK; | ||
530 | fail: | ||
531 | cbor_vector_free(argv, nitems(argv)); | ||
532 | |||
533 | return (r); | ||
534 | } | ||
535 | |||
536 | int | ||
537 | fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t, | ||
538 | fido_bio_enroll_t *e, uint32_t timo_ms) | ||
539 | { | ||
540 | if (e->token == NULL) | ||
541 | return (FIDO_ERR_INVALID_ARGUMENT); | ||
542 | |||
543 | return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1)); | ||
544 | } | ||
545 | |||
546 | static int | ||
547 | bio_enroll_cancel_wait(fido_dev_t *dev, int ms) | ||
548 | { | ||
549 | const uint8_t cmd = CMD_ENROLL_CANCEL; | ||
550 | int r; | ||
551 | |||
552 | if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK || | ||
553 | (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { | ||
554 | fido_log_debug("%s: tx/rx", __func__); | ||
555 | return (r); | ||
556 | } | ||
557 | |||
558 | return (FIDO_OK); | ||
559 | } | ||
560 | |||
561 | int | ||
562 | fido_bio_dev_enroll_cancel(fido_dev_t *dev) | ||
563 | { | ||
564 | return (bio_enroll_cancel_wait(dev, -1)); | ||
565 | } | ||
566 | |||
567 | static int | ||
568 | bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t, | ||
569 | const char *pin, int ms) | ||
570 | { | ||
571 | cbor_item_t *argv[1]; | ||
572 | const uint8_t cmd = CMD_ENROLL_REMOVE; | ||
573 | int r = FIDO_ERR_INTERNAL; | ||
574 | |||
575 | memset(&argv, 0, sizeof(argv)); | ||
576 | |||
577 | if ((argv[0] = fido_blob_encode(&t->id)) == NULL) { | ||
578 | fido_log_debug("%s: cbor encode", __func__); | ||
579 | goto fail; | ||
580 | } | ||
581 | |||
582 | if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK || | ||
583 | (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { | ||
584 | fido_log_debug("%s: tx/rx", __func__); | ||
585 | goto fail; | ||
586 | } | ||
587 | |||
588 | r = FIDO_OK; | ||
589 | fail: | ||
590 | cbor_vector_free(argv, nitems(argv)); | ||
591 | |||
592 | return (r); | ||
593 | } | ||
594 | |||
595 | int | ||
596 | fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t, | ||
597 | const char *pin) | ||
598 | { | ||
599 | return (bio_enroll_remove_wait(dev, t, pin, -1)); | ||
600 | } | ||
601 | |||
602 | static void | ||
603 | bio_reset_info(fido_bio_info_t *i) | ||
604 | { | ||
605 | i->type = 0; | ||
606 | i->max_samples = 0; | ||
607 | } | ||
608 | |||
609 | static int | ||
610 | bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
611 | { | ||
612 | fido_bio_info_t *i = arg; | ||
613 | uint64_t x; | ||
614 | |||
615 | if (cbor_isa_uint(key) == false || | ||
616 | cbor_int_get_width(key) != CBOR_INT_8) { | ||
617 | fido_log_debug("%s: cbor type", __func__); | ||
618 | return (0); /* ignore */ | ||
619 | } | ||
620 | |||
621 | switch (cbor_get_uint8(key)) { | ||
622 | case 2: | ||
623 | if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { | ||
624 | fido_log_debug("%s: cbor_decode_uint64", __func__); | ||
625 | return (-1); | ||
626 | } | ||
627 | i->type = (uint8_t)x; | ||
628 | break; | ||
629 | case 3: | ||
630 | if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { | ||
631 | fido_log_debug("%s: cbor_decode_uint64", __func__); | ||
632 | return (-1); | ||
633 | } | ||
634 | i->max_samples = (uint8_t)x; | ||
635 | break; | ||
636 | default: | ||
637 | return (0); /* ignore */ | ||
638 | } | ||
639 | |||
640 | return (0); | ||
641 | } | ||
642 | |||
643 | static int | ||
644 | bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms) | ||
645 | { | ||
646 | const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; | ||
647 | unsigned char reply[2048]; | ||
648 | int reply_len; | ||
649 | int r; | ||
650 | |||
651 | bio_reset_info(i); | ||
652 | |||
653 | if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { | ||
654 | fido_log_debug("%s: fido_rx", __func__); | ||
655 | return (FIDO_ERR_RX); | ||
656 | } | ||
657 | |||
658 | if ((r = cbor_parse_reply(reply, (size_t)reply_len, i, | ||
659 | bio_parse_info)) != FIDO_OK) { | ||
660 | fido_log_debug("%s: bio_parse_info" , __func__); | ||
661 | return (r); | ||
662 | } | ||
663 | |||
664 | return (FIDO_OK); | ||
665 | } | ||
666 | |||
667 | static int | ||
668 | bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms) | ||
669 | { | ||
670 | int r; | ||
671 | |||
672 | if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK || | ||
673 | (r = bio_rx_info(dev, i, ms)) != FIDO_OK) { | ||
674 | fido_log_debug("%s: tx/rx", __func__); | ||
675 | return (r); | ||
676 | } | ||
677 | |||
678 | return (FIDO_OK); | ||
679 | } | ||
680 | |||
681 | int | ||
682 | fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i) | ||
683 | { | ||
684 | return (bio_get_info_wait(dev, i, -1)); | ||
685 | } | ||
686 | |||
687 | const char * | ||
688 | fido_bio_template_name(const fido_bio_template_t *t) | ||
689 | { | ||
690 | return (t->name); | ||
691 | } | ||
692 | |||
693 | const unsigned char * | ||
694 | fido_bio_template_id_ptr(const fido_bio_template_t *t) | ||
695 | { | ||
696 | return (t->id.ptr); | ||
697 | } | ||
698 | |||
699 | size_t | ||
700 | fido_bio_template_id_len(const fido_bio_template_t *t) | ||
701 | { | ||
702 | return (t->id.len); | ||
703 | } | ||
704 | |||
705 | size_t | ||
706 | fido_bio_template_array_count(const fido_bio_template_array_t *ta) | ||
707 | { | ||
708 | return (ta->n_rx); | ||
709 | } | ||
710 | |||
711 | fido_bio_template_array_t * | ||
712 | fido_bio_template_array_new(void) | ||
713 | { | ||
714 | return (calloc(1, sizeof(fido_bio_template_array_t))); | ||
715 | } | ||
716 | |||
717 | fido_bio_template_t * | ||
718 | fido_bio_template_new(void) | ||
719 | { | ||
720 | return (calloc(1, sizeof(fido_bio_template_t))); | ||
721 | } | ||
722 | |||
723 | void | ||
724 | fido_bio_template_array_free(fido_bio_template_array_t **tap) | ||
725 | { | ||
726 | fido_bio_template_array_t *ta; | ||
727 | |||
728 | if (tap == NULL || (ta = *tap) == NULL) | ||
729 | return; | ||
730 | |||
731 | bio_reset_template_array(ta); | ||
732 | free(ta); | ||
733 | *tap = NULL; | ||
734 | } | ||
735 | |||
736 | void | ||
737 | fido_bio_template_free(fido_bio_template_t **tp) | ||
738 | { | ||
739 | fido_bio_template_t *t; | ||
740 | |||
741 | if (tp == NULL || (t = *tp) == NULL) | ||
742 | return; | ||
743 | |||
744 | bio_reset_template(t); | ||
745 | free(t); | ||
746 | *tp = NULL; | ||
747 | } | ||
748 | |||
749 | int | ||
750 | fido_bio_template_set_name(fido_bio_template_t *t, const char *name) | ||
751 | { | ||
752 | free(t->name); | ||
753 | t->name = NULL; | ||
754 | |||
755 | if (name && (t->name = strdup(name)) == NULL) | ||
756 | return (FIDO_ERR_INTERNAL); | ||
757 | |||
758 | return (FIDO_OK); | ||
759 | } | ||
760 | |||
761 | int | ||
762 | fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr, | ||
763 | size_t len) | ||
764 | { | ||
765 | free(t->id.ptr); | ||
766 | t->id.ptr = NULL; | ||
767 | t->id.len = 0; | ||
768 | |||
769 | if (ptr && fido_blob_set(&t->id, ptr, len) < 0) | ||
770 | return (FIDO_ERR_INTERNAL); | ||
771 | |||
772 | return (FIDO_OK); | ||
773 | } | ||
774 | |||
775 | const fido_bio_template_t * | ||
776 | fido_bio_template(const fido_bio_template_array_t *ta, size_t idx) | ||
777 | { | ||
778 | if (idx >= ta->n_alloc) | ||
779 | return (NULL); | ||
780 | |||
781 | return (&ta->ptr[idx]); | ||
782 | } | ||
783 | |||
784 | fido_bio_enroll_t * | ||
785 | fido_bio_enroll_new(void) | ||
786 | { | ||
787 | return (calloc(1, sizeof(fido_bio_enroll_t))); | ||
788 | } | ||
789 | |||
790 | fido_bio_info_t * | ||
791 | fido_bio_info_new(void) | ||
792 | { | ||
793 | return (calloc(1, sizeof(fido_bio_info_t))); | ||
794 | } | ||
795 | |||
796 | uint8_t | ||
797 | fido_bio_info_type(const fido_bio_info_t *i) | ||
798 | { | ||
799 | return (i->type); | ||
800 | } | ||
801 | |||
802 | uint8_t | ||
803 | fido_bio_info_max_samples(const fido_bio_info_t *i) | ||
804 | { | ||
805 | return (i->max_samples); | ||
806 | } | ||
807 | |||
808 | void | ||
809 | fido_bio_enroll_free(fido_bio_enroll_t **ep) | ||
810 | { | ||
811 | fido_bio_enroll_t *e; | ||
812 | |||
813 | if (ep == NULL || (e = *ep) == NULL) | ||
814 | return; | ||
815 | |||
816 | bio_reset_enroll(e); | ||
817 | |||
818 | free(e); | ||
819 | *ep = NULL; | ||
820 | } | ||
821 | |||
822 | void | ||
823 | fido_bio_info_free(fido_bio_info_t **ip) | ||
824 | { | ||
825 | fido_bio_info_t *i; | ||
826 | |||
827 | if (ip == NULL || (i = *ip) == NULL) | ||
828 | return; | ||
829 | |||
830 | free(i); | ||
831 | *ip = NULL; | ||
832 | } | ||
833 | |||
834 | uint8_t | ||
835 | fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e) | ||
836 | { | ||
837 | return (e->remaining_samples); | ||
838 | } | ||
839 | |||
840 | uint8_t | ||
841 | fido_bio_enroll_last_status(const fido_bio_enroll_t *e) | ||
842 | { | ||
843 | return (e->last_status); | ||
844 | } | ||