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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <fido.h>
9#include <fido/bio.h>
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "../openbsd-compat/openbsd-compat.h"
19#include "extern.h"
20
21static int
22print_template(const fido_bio_template_array_t *ta, size_t idx)
23{
24	const fido_bio_template_t *t = NULL;
25	char *id = NULL;
26
27	if ((t = fido_bio_template(ta, idx)) == NULL) {
28		warnx("fido_bio_template");
29		return -1;
30	}
31	if (base64_encode(fido_bio_template_id_ptr(t),
32	    fido_bio_template_id_len(t), &id) < 0) {
33		warnx("output error");
34		return -1;
35	}
36
37	printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t));
38	free(id);
39
40	return 0;
41}
42
43int
44bio_list(const char *path)
45{
46	fido_bio_template_array_t *ta = NULL;
47	fido_dev_t *dev = NULL;
48	char *pin = NULL;
49	int r, ok = 1;
50
51	if ((ta = fido_bio_template_array_new()) == NULL)
52		errx(1, "fido_bio_template_array_new");
53	dev = open_dev(path);
54	if ((pin = get_pin(path)) == NULL)
55		goto out;
56	r = fido_bio_dev_get_template_array(dev, ta, pin);
57	freezero(pin, PINBUF_LEN);
58	pin = NULL;
59	if (r != FIDO_OK) {
60		warnx("fido_bio_dev_get_template_array: %s", fido_strerr(r));
61		goto out;
62	}
63	for (size_t i = 0; i < fido_bio_template_array_count(ta); i++)
64		if (print_template(ta, i) < 0)
65			goto out;
66
67	ok = 0;
68out:
69	fido_bio_template_array_free(&ta);
70	fido_dev_close(dev);
71	fido_dev_free(&dev);
72
73	exit(ok);
74}
75
76int
77bio_set_name(const char *path, const char *id, const char *name)
78{
79	fido_bio_template_t *t = NULL;
80	fido_dev_t *dev = NULL;
81	char *pin = NULL;
82	void *id_blob_ptr = NULL;
83	size_t id_blob_len = 0;
84	int r, ok = 1;
85
86	if ((t = fido_bio_template_new()) == NULL)
87		errx(1, "fido_bio_template_new");
88	if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
89		errx(1, "base64_decode");
90	if ((r = fido_bio_template_set_name(t, name)) != FIDO_OK)
91		errx(1, "fido_bio_template_set_name: %s", fido_strerr(r));
92	if ((r = fido_bio_template_set_id(t, id_blob_ptr,
93	    id_blob_len)) != FIDO_OK)
94		errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
95
96	dev = open_dev(path);
97	if ((pin = get_pin(path)) == NULL)
98		goto out;
99	r = fido_bio_dev_set_template_name(dev, t, pin);
100	freezero(pin, PINBUF_LEN);
101	pin = NULL;
102	if (r != FIDO_OK) {
103		warnx("fido_bio_dev_set_template_name: %s", fido_strerr(r));
104		goto out;
105	}
106
107	ok = 0;
108out:
109	free(id_blob_ptr);
110	fido_bio_template_free(&t);
111	fido_dev_close(dev);
112	fido_dev_free(&dev);
113
114	exit(ok);
115}
116
117static const char *
118enroll_strerr(uint8_t n)
119{
120	switch (n) {
121	case FIDO_BIO_ENROLL_FP_GOOD:
122		return "Sample ok";
123	case FIDO_BIO_ENROLL_FP_TOO_HIGH:
124		return "Sample too high";
125	case FIDO_BIO_ENROLL_FP_TOO_LOW:
126		return "Sample too low";
127	case FIDO_BIO_ENROLL_FP_TOO_LEFT:
128		return "Sample too left";
129	case FIDO_BIO_ENROLL_FP_TOO_RIGHT:
130		return "Sample too right";
131	case FIDO_BIO_ENROLL_FP_TOO_FAST:
132		return "Sample too fast";
133	case FIDO_BIO_ENROLL_FP_TOO_SLOW:
134		return "Sample too slow";
135	case FIDO_BIO_ENROLL_FP_POOR_QUALITY:
136		return "Poor quality sample";
137	case FIDO_BIO_ENROLL_FP_TOO_SKEWED:
138		return "Sample too skewed";
139	case FIDO_BIO_ENROLL_FP_TOO_SHORT:
140		return "Sample too short";
141	case FIDO_BIO_ENROLL_FP_MERGE_FAILURE:
142		return "Sample merge failure";
143	case FIDO_BIO_ENROLL_FP_EXISTS:
144		return "Sample exists";
145	case FIDO_BIO_ENROLL_FP_DATABASE_FULL:
146		return "Fingerprint database full";
147	case FIDO_BIO_ENROLL_NO_USER_ACTIVITY:
148		return "No user activity";
149	case FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION:
150		return "No user presence transition";
151	default:
152		return "Unknown error";
153	}
154}
155
156int
157bio_enroll(const char *path)
158{
159	fido_bio_template_t *t = NULL;
160	fido_bio_enroll_t *e = NULL;
161	fido_dev_t *dev = NULL;
162	char *pin = NULL;
163	int r, ok = 1;
164
165	if ((t = fido_bio_template_new()) == NULL)
166		errx(1, "fido_bio_template_new");
167	if ((e = fido_bio_enroll_new()) == NULL)
168		errx(1, "fido_bio_enroll_new");
169
170	dev = open_dev(path);
171	if ((pin = get_pin(path)) == NULL)
172		goto out;
173	printf("Touch your security key.\n");
174	r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin);
175	freezero(pin, PINBUF_LEN);
176	pin = NULL;
177	if (r != FIDO_OK) {
178		warnx("fido_bio_dev_enroll_begin: %s", fido_strerr(r));
179		goto out;
180	}
181	printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
182
183	while (fido_bio_enroll_remaining_samples(e) > 0) {
184		printf("Touch your security key (%u sample%s left).\n",
185		    (unsigned)fido_bio_enroll_remaining_samples(e),
186		    plural(fido_bio_enroll_remaining_samples(e)));
187		if ((r = fido_bio_dev_enroll_continue(dev, t, e,
188		    10000)) != FIDO_OK) {
189			fido_dev_cancel(dev);
190			warnx("fido_bio_dev_enroll_continue: %s",
191			    fido_strerr(r));
192			goto out;
193		}
194		printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
195	}
196
197	ok = 0;
198out:
199	fido_bio_template_free(&t);
200	fido_bio_enroll_free(&e);
201	fido_dev_close(dev);
202	fido_dev_free(&dev);
203
204	exit(ok);
205}
206
207int
208bio_delete(const char *path, const char *id)
209{
210	fido_bio_template_t *t = NULL;
211	fido_dev_t *dev = NULL;
212	char *pin = NULL;
213	void *id_blob_ptr = NULL;
214	size_t id_blob_len = 0;
215	int r, ok = 1;
216
217	if ((t = fido_bio_template_new()) == NULL)
218		errx(1, "fido_bio_template_new");
219	if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
220		errx(1, "base64_decode");
221	if ((r = fido_bio_template_set_id(t, id_blob_ptr,
222	    id_blob_len)) != FIDO_OK)
223		errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
224
225	dev = open_dev(path);
226	if ((pin = get_pin(path)) == NULL)
227		goto out;
228	r = fido_bio_dev_enroll_remove(dev, t, pin);
229	freezero(pin, PINBUF_LEN);
230	pin = NULL;
231	if (r != FIDO_OK) {
232		warnx("fido_bio_dev_enroll_remove: %s", fido_strerr(r));
233		goto out;
234	}
235
236	ok = 0;
237out:
238	free(id_blob_ptr);
239	fido_bio_template_free(&t);
240	fido_dev_close(dev);
241	fido_dev_free(&dev);
242
243	exit(ok);
244}
245
246static const char *
247type_str(uint8_t t)
248{
249	switch (t) {
250	case 1:
251		return "touch";
252	case 2:
253		return "swipe";
254	default:
255		return "unknown";
256	}
257}
258
259void
260bio_info(fido_dev_t *dev)
261{
262	fido_bio_info_t	*i = NULL;
263
264	if ((i = fido_bio_info_new()) == NULL) {
265		warnx("fido_bio_info_new");
266		return;
267	}
268	if (fido_bio_dev_get_info(dev, i) != FIDO_OK) {
269		fido_bio_info_free(&i);
270		return;
271	}
272
273	printf("sensor type: %u (%s)\n", (unsigned)fido_bio_info_type(i),
274	    type_str(fido_bio_info_type(i)));
275	printf("max samples: %u\n", (unsigned)fido_bio_info_max_samples(i));
276
277	fido_bio_info_free(&i);
278}
279