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