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