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