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