xref: /freebsd/contrib/libfido2/tools/util.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*
2  * Copyright (c) 2018-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 <sys/types.h>
9 #include <sys/stat.h>
10 
11 #include <openssl/ec.h>
12 #include <openssl/evp.h>
13 #include <openssl/pem.h>
14 
15 #include <fido.h>
16 #include <fido/es256.h>
17 #include <fido/es384.h>
18 #include <fido/rs256.h>
19 #include <fido/eddsa.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "../openbsd-compat/openbsd-compat.h"
31 #ifdef _MSC_VER
32 #include "../openbsd-compat/posix_win.h"
33 #endif
34 
35 #include "extern.h"
36 
37 char *
38 get_pin(const char *path)
39 {
40 	char *pin;
41 	char prompt[1024];
42 	int r, ok = -1;
43 
44 	if ((pin = calloc(1, PINBUF_LEN)) == NULL) {
45 		warn("%s: calloc", __func__);
46 		return NULL;
47 	}
48 	if ((r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
49 	    path)) < 0 || (size_t)r >= sizeof(prompt)) {
50 		warn("%s: snprintf", __func__);
51 		goto out;
52 	}
53 	if (!readpassphrase(prompt, pin, PINBUF_LEN, RPP_ECHO_OFF)) {
54 		warnx("%s: readpassphrase", __func__);
55 		goto out;
56 	}
57 
58 	ok = 0;
59 out:
60 	if (ok < 0) {
61 		freezero(pin, PINBUF_LEN);
62 		pin = NULL;
63 	}
64 
65 	return pin;
66 }
67 
68 FILE *
69 open_write(const char *file)
70 {
71 	int fd;
72 	FILE *f;
73 
74 	if (file == NULL || strcmp(file, "-") == 0)
75 		return (stdout);
76 	if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0)
77 		err(1, "open %s", file);
78 	if ((f = fdopen(fd, "w")) == NULL)
79 		err(1, "fdopen %s", file);
80 
81 	return (f);
82 }
83 
84 FILE *
85 open_read(const char *file)
86 {
87 	int fd;
88 	FILE *f;
89 
90 	if (file == NULL || strcmp(file, "-") == 0) {
91 #ifdef FIDO_FUZZ
92 		setvbuf(stdin, NULL, _IONBF, 0);
93 #endif
94 		return (stdin);
95 	}
96 	if ((fd = open(file, O_RDONLY)) < 0)
97 		err(1, "open %s", file);
98 	if ((f = fdopen(fd, "r")) == NULL)
99 		err(1, "fdopen %s", file);
100 
101 	return (f);
102 }
103 
104 int
105 base10(const char *str)
106 {
107 	char *ep;
108 	long long ll;
109 
110 	ll = strtoll(str, &ep, 10);
111 	if (str == ep || *ep != '\0')
112 		return (-1);
113 	else if (ll == LLONG_MIN && errno == ERANGE)
114 		return (-1);
115 	else if (ll == LLONG_MAX && errno == ERANGE)
116 		return (-1);
117 	else if (ll < 0 || ll > INT_MAX)
118 		return (-1);
119 
120 	return ((int)ll);
121 }
122 
123 void
124 xxd(const void *buf, size_t count)
125 {
126 	const uint8_t	*ptr = buf;
127 	size_t		 i;
128 
129 	fprintf(stderr, "  ");
130 
131 	for (i = 0; i < count; i++) {
132 		fprintf(stderr, "%02x ", *ptr++);
133 		if ((i + 1) % 16 == 0 && i + 1 < count)
134 			fprintf(stderr, "\n  ");
135 	}
136 
137 	fprintf(stderr, "\n");
138 	fflush(stderr);
139 }
140 
141 int
142 string_read(FILE *f, char **out)
143 {
144 	char *line = NULL;
145 	size_t linesize = 0;
146 	ssize_t n;
147 
148 	*out = NULL;
149 
150 	if ((n = getline(&line, &linesize, f)) <= 0 ||
151 	    (size_t)n != strlen(line)) {
152 		free(line);
153 		return (-1);
154 	}
155 
156 	line[n - 1] = '\0'; /* trim \n */
157 	*out = line;
158 
159 	return (0);
160 }
161 
162 fido_dev_t *
163 open_dev(const char *path)
164 {
165 	fido_dev_t *dev;
166 	int r;
167 
168 	if ((dev = fido_dev_new()) == NULL)
169 		errx(1, "fido_dev_new");
170 
171 	r = fido_dev_open(dev, path);
172 	if (r != FIDO_OK)
173 		errx(1, "fido_dev_open %s: %s", path, fido_strerr(r));
174 
175 	return (dev);
176 }
177 
178 int
179 get_devopt(fido_dev_t *dev, const char *name, int *val)
180 {
181 	fido_cbor_info_t *cbor_info;
182 	char * const *names;
183 	const bool *values;
184 	int r, ok = -1;
185 
186 	if ((cbor_info = fido_cbor_info_new()) == NULL) {
187 		warnx("fido_cbor_info_new");
188 		goto out;
189 	}
190 
191 	if ((r = fido_dev_get_cbor_info(dev, cbor_info)) != FIDO_OK) {
192 		warnx("fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
193 		goto out;
194 	}
195 
196 	if ((names = fido_cbor_info_options_name_ptr(cbor_info)) == NULL ||
197 	    (values = fido_cbor_info_options_value_ptr(cbor_info)) == NULL) {
198 		warnx("fido_dev_get_cbor_info: NULL name/value pointer");
199 		goto out;
200 	}
201 
202 	*val = -1;
203 	for (size_t i = 0; i < fido_cbor_info_options_len(cbor_info); i++)
204 		if (strcmp(names[i], name) == 0) {
205 			*val = values[i];
206 			break;
207 		}
208 
209 	ok = 0;
210 out:
211 	fido_cbor_info_free(&cbor_info);
212 
213 	return (ok);
214 }
215 
216 EC_KEY *
217 read_ec_pubkey(const char *path)
218 {
219 	FILE *fp = NULL;
220 	EVP_PKEY *pkey = NULL;
221 	EC_KEY *ec = NULL;
222 
223 	if ((fp = fopen(path, "r")) == NULL) {
224 		warn("fopen");
225 		goto fail;
226 	}
227 
228 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
229 		warnx("PEM_read_PUBKEY");
230 		goto fail;
231 	}
232 	if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
233 		warnx("EVP_PKEY_get1_EC_KEY");
234 		goto fail;
235 	}
236 
237 fail:
238 	if (fp) {
239 		fclose(fp);
240 	}
241 	if (pkey) {
242 		EVP_PKEY_free(pkey);
243 	}
244 
245 	return (ec);
246 }
247 
248 int
249 write_es256_pubkey(FILE *f, const void *ptr, size_t len)
250 {
251 	EVP_PKEY *pkey = NULL;
252 	es256_pk_t *pk = NULL;
253 	int ok = -1;
254 
255 	if ((pk = es256_pk_new()) == NULL) {
256 		warnx("es256_pk_new");
257 		goto fail;
258 	}
259 
260 	if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
261 		warnx("es256_pk_from_ptr");
262 		goto fail;
263 	}
264 
265 	if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
266 		warnx("es256_pk_to_EVP_PKEY");
267 		goto fail;
268 	}
269 
270 	if (PEM_write_PUBKEY(f, pkey) == 0) {
271 		warnx("PEM_write_PUBKEY");
272 		goto fail;
273 	}
274 
275 	ok = 0;
276 fail:
277 	es256_pk_free(&pk);
278 
279 	if (pkey != NULL) {
280 		EVP_PKEY_free(pkey);
281 	}
282 
283 	return (ok);
284 }
285 
286 int
287 write_es384_pubkey(FILE *f, const void *ptr, size_t len)
288 {
289 	EVP_PKEY *pkey = NULL;
290 	es384_pk_t *pk = NULL;
291 	int ok = -1;
292 
293 	if ((pk = es384_pk_new()) == NULL) {
294 		warnx("es384_pk_new");
295 		goto fail;
296 	}
297 
298 	if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
299 		warnx("es384_pk_from_ptr");
300 		goto fail;
301 	}
302 
303 	if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
304 		warnx("es384_pk_to_EVP_PKEY");
305 		goto fail;
306 	}
307 
308 	if (PEM_write_PUBKEY(f, pkey) == 0) {
309 		warnx("PEM_write_PUBKEY");
310 		goto fail;
311 	}
312 
313 	ok = 0;
314 fail:
315 	es384_pk_free(&pk);
316 
317 	if (pkey != NULL) {
318 		EVP_PKEY_free(pkey);
319 	}
320 
321 	return (ok);
322 }
323 
324 RSA *
325 read_rsa_pubkey(const char *path)
326 {
327 	FILE *fp = NULL;
328 	EVP_PKEY *pkey = NULL;
329 	RSA *rsa = NULL;
330 
331 	if ((fp = fopen(path, "r")) == NULL) {
332 		warn("fopen");
333 		goto fail;
334 	}
335 
336 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
337 		warnx("PEM_read_PUBKEY");
338 		goto fail;
339 	}
340 	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
341 		warnx("EVP_PKEY_get1_RSA");
342 		goto fail;
343 	}
344 
345 fail:
346 	if (fp) {
347 		fclose(fp);
348 	}
349 	if (pkey) {
350 		EVP_PKEY_free(pkey);
351 	}
352 
353 	return (rsa);
354 }
355 
356 int
357 write_rsa_pubkey(FILE *f, const void *ptr, size_t len)
358 {
359 	EVP_PKEY *pkey = NULL;
360 	rs256_pk_t *pk = NULL;
361 	int ok = -1;
362 
363 	if ((pk = rs256_pk_new()) == NULL) {
364 		warnx("rs256_pk_new");
365 		goto fail;
366 	}
367 
368 	if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
369 		warnx("rs256_pk_from_ptr");
370 		goto fail;
371 	}
372 
373 	if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
374 		warnx("rs256_pk_to_EVP_PKEY");
375 		goto fail;
376 	}
377 
378 	if (PEM_write_PUBKEY(f, pkey) == 0) {
379 		warnx("PEM_write_PUBKEY");
380 		goto fail;
381 	}
382 
383 	ok = 0;
384 fail:
385 	rs256_pk_free(&pk);
386 
387 	if (pkey != NULL) {
388 		EVP_PKEY_free(pkey);
389 	}
390 
391 	return (ok);
392 }
393 
394 EVP_PKEY *
395 read_eddsa_pubkey(const char *path)
396 {
397 	FILE *fp = NULL;
398 	EVP_PKEY *pkey = NULL;
399 
400 	if ((fp = fopen(path, "r")) == NULL) {
401 		warn("fopen");
402 		goto fail;
403 	}
404 
405 	if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
406 		warnx("PEM_read_PUBKEY");
407 		goto fail;
408 	}
409 
410 fail:
411 	if (fp) {
412 		fclose(fp);
413 	}
414 
415 	return (pkey);
416 }
417 
418 int
419 write_eddsa_pubkey(FILE *f, const void *ptr, size_t len)
420 {
421 	EVP_PKEY *pkey = NULL;
422 	eddsa_pk_t *pk = NULL;
423 	int ok = -1;
424 
425 	if ((pk = eddsa_pk_new()) == NULL) {
426 		warnx("eddsa_pk_new");
427 		goto fail;
428 	}
429 
430 	if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
431 		warnx("eddsa_pk_from_ptr");
432 		goto fail;
433 	}
434 
435 	if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
436 		warnx("eddsa_pk_to_EVP_PKEY");
437 		goto fail;
438 	}
439 
440 	if (PEM_write_PUBKEY(f, pkey) == 0) {
441 		warnx("PEM_write_PUBKEY");
442 		goto fail;
443 	}
444 
445 	ok = 0;
446 fail:
447 	eddsa_pk_free(&pk);
448 
449 	if (pkey != NULL) {
450 		EVP_PKEY_free(pkey);
451 	}
452 
453 	return (ok);
454 }
455 
456 void
457 print_cred(FILE *out_f, int type, const fido_cred_t *cred)
458 {
459 	char *id;
460 	int r;
461 
462 	r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id);
463 	if (r < 0)
464 		errx(1, "output error");
465 
466 	fprintf(out_f, "%s\n", id);
467 
468 	switch (type) {
469 	case COSE_ES256:
470 		write_es256_pubkey(out_f, fido_cred_pubkey_ptr(cred),
471 		    fido_cred_pubkey_len(cred));
472 		break;
473 	case COSE_ES384:
474 		write_es384_pubkey(out_f, fido_cred_pubkey_ptr(cred),
475 		    fido_cred_pubkey_len(cred));
476 		break;
477 	case COSE_RS256:
478 		write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
479 		    fido_cred_pubkey_len(cred));
480 		break;
481 	case COSE_EDDSA:
482 		write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
483 		    fido_cred_pubkey_len(cred));
484 		break;
485 	default:
486 		errx(1, "print_cred: unknown type");
487 	}
488 
489 	free(id);
490 }
491 
492 int
493 cose_type(const char *str, int *type)
494 {
495 	if (strcmp(str, "es256") == 0)
496 		*type = COSE_ES256;
497 	else if (strcmp(str, "es384") == 0)
498 		*type = COSE_ES384;
499 	else if (strcmp(str, "rs256") == 0)
500 		*type = COSE_RS256;
501 	else if (strcmp(str, "eddsa") == 0)
502 		*type = COSE_EDDSA;
503 	else {
504 		*type = 0;
505 		return (-1);
506 	}
507 
508 	return (0);
509 }
510 
511 const char *
512 cose_string(int type)
513 {
514 	switch (type) {
515 	case COSE_ES256:
516 		return ("es256");
517 	case COSE_ES384:
518 		return ("es384");
519 	case COSE_RS256:
520 		return ("rs256");
521 	case COSE_EDDSA:
522 		return ("eddsa");
523 	default:
524 		return ("unknown");
525 	}
526 }
527 
528 const char *
529 prot_string(int prot)
530 {
531 	switch (prot) {
532 	case FIDO_CRED_PROT_UV_OPTIONAL:
533 		return ("uvopt");
534 	case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID:
535 		return ("uvopt+id");
536 	case FIDO_CRED_PROT_UV_REQUIRED:
537 		return ("uvreq");
538 	default:
539 		return ("unknown");
540 	}
541 }
542 
543 int
544 read_file(const char *path, u_char **ptr, size_t *len)
545 {
546 	int fd, ok = -1;
547 	struct stat st;
548 	ssize_t n;
549 
550 	*ptr = NULL;
551 	*len = 0;
552 
553 	if ((fd = open(path, O_RDONLY)) < 0) {
554 		warn("%s: open %s", __func__, path);
555 		goto fail;
556 	}
557 	if (fstat(fd, &st) < 0) {
558 		warn("%s: stat %s", __func__, path);
559 		goto fail;
560 	}
561 	if (st.st_size < 0) {
562 		warnx("%s: stat %s: invalid size", __func__, path);
563 		goto fail;
564 	}
565 	*len = (size_t)st.st_size;
566 	if ((*ptr = malloc(*len)) == NULL) {
567 		warn("%s: malloc", __func__);
568 		goto fail;
569 	}
570 	if ((n = read(fd, *ptr, *len)) < 0) {
571 		warn("%s: read", __func__);
572 		goto fail;
573 	}
574 	if ((size_t)n != *len) {
575 		warnx("%s: read", __func__);
576 		goto fail;
577 	}
578 
579 	ok = 0;
580 fail:
581 	if (fd != -1) {
582 		close(fd);
583 	}
584 	if (ok < 0) {
585 		free(*ptr);
586 		*ptr = NULL;
587 		*len = 0;
588 	}
589 
590 	return ok;
591 }
592 
593 int
594 write_file(const char *path, const u_char *ptr, size_t len)
595 {
596 	int fd, ok = -1;
597 	ssize_t n;
598 
599 	if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
600 		warn("%s: open %s", __func__, path);
601 		goto fail;
602 	}
603 	if ((n = write(fd, ptr, len)) < 0) {
604 		warn("%s: write", __func__);
605 		goto fail;
606 	}
607 	if ((size_t)n != len) {
608 		warnx("%s: write", __func__);
609 		goto fail;
610 	}
611 
612 	ok = 0;
613 fail:
614 	if (fd != -1) {
615 		close(fd);
616 	}
617 
618 	return ok;
619 }
620 
621 const char *
622 plural(size_t x)
623 {
624 	return x == 1 ? "" : "s";
625 }
626 
627 int
628 should_retry_with_pin(const fido_dev_t *dev, int r)
629 {
630 	if (fido_dev_has_pin(dev) == false) {
631 		return 0;
632 	}
633 
634 	switch (r) {
635 	case FIDO_ERR_PIN_REQUIRED:
636 	case FIDO_ERR_UNAUTHORIZED_PERM:
637 	case FIDO_ERR_UV_BLOCKED:
638 	case FIDO_ERR_UV_INVALID:
639 		return 1;
640 	}
641 
642 	return 0;
643 }
644