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