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