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