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
37char *
38get_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;
59out:
60	if (ok < 0) {
61		freezero(pin, PINBUF_LEN);
62		pin = NULL;
63	}
64
65	return pin;
66}
67
68FILE *
69open_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
84FILE *
85open_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
104int
105base10(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
123void
124xxd(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
141int
142string_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
162fido_dev_t *
163open_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
178int
179get_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;
210out:
211	fido_cbor_info_free(&cbor_info);
212
213	return (ok);
214}
215
216EC_KEY *
217read_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
237fail:
238	if (fp) {
239		fclose(fp);
240	}
241	if (pkey) {
242		EVP_PKEY_free(pkey);
243	}
244
245	return (ec);
246}
247
248int
249write_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;
276fail:
277	es256_pk_free(&pk);
278
279	if (pkey != NULL) {
280		EVP_PKEY_free(pkey);
281	}
282
283	return (ok);
284}
285
286int
287write_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;
314fail:
315	es384_pk_free(&pk);
316
317	if (pkey != NULL) {
318		EVP_PKEY_free(pkey);
319	}
320
321	return (ok);
322}
323
324RSA *
325read_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
345fail:
346	if (fp) {
347		fclose(fp);
348	}
349	if (pkey) {
350		EVP_PKEY_free(pkey);
351	}
352
353	return (rsa);
354}
355
356int
357write_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;
384fail:
385	rs256_pk_free(&pk);
386
387	if (pkey != NULL) {
388		EVP_PKEY_free(pkey);
389	}
390
391	return (ok);
392}
393
394EVP_PKEY *
395read_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
410fail:
411	if (fp) {
412		fclose(fp);
413	}
414
415	return (pkey);
416}
417
418int
419write_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;
446fail:
447	eddsa_pk_free(&pk);
448
449	if (pkey != NULL) {
450		EVP_PKEY_free(pkey);
451	}
452
453	return (ok);
454}
455
456void
457print_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
492int
493cose_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
511const char *
512cose_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
528const char *
529prot_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
543int
544read_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;
580fail:
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
593int
594write_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;
613fail:
614	if (fd != -1) {
615		close(fd);
616	}
617
618	return ok;
619}
620
621const char *
622plural(size_t x)
623{
624	return x == 1 ? "" : "s";
625}
626
627int
628should_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