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