Home | History | Annotate | Line # | Download | only in tools
      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 *
     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 *
     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 *
     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
    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
    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
    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 *
    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
    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 *
    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
    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
    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 *
    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
    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 *
    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
    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
    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
    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 *
    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 *
    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
    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
    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 *
    623 plural(size_t x)
    624 {
    625 	return x == 1 ? "" : "s";
    626 }
    627 
    628 int
    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