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