1 /* 2 * Copyright (c) 2019 Markus Friedl 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stdint.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <stdio.h> 21 #include <stddef.h> 22 #include <stdarg.h> 23 24 #ifdef WITH_OPENSSL 25 #include <openssl/opensslv.h> 26 #include <openssl/crypto.h> 27 #include <openssl/bn.h> 28 #include <openssl/ec.h> 29 #include <openssl/ecdsa.h> 30 #endif /* WITH_OPENSSL */ 31 32 #include <fido.h> 33 #include <fido/credman.h> 34 35 #ifndef SK_STANDALONE 36 # include "log.h" 37 # include "xmalloc.h" 38 /* 39 * If building as part of OpenSSH, then rename exported functions. 40 * This must be done before including sk-api.h. 41 */ 42 # define sk_api_version ssh_sk_api_version 43 # define sk_enroll ssh_sk_enroll 44 # define sk_sign ssh_sk_sign 45 # define sk_load_resident_keys ssh_sk_load_resident_keys 46 #endif /* !SK_STANDALONE */ 47 48 #include "sk-api.h" 49 50 /* #define SK_DEBUG 1 */ 51 52 #define MAX_FIDO_DEVICES 256 53 54 /* Compatibility with OpenSSH 1.0.x */ 55 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 56 #define ECDSA_SIG_get0(sig, pr, ps) \ 57 do { \ 58 (*pr) = sig->r; \ 59 (*ps) = sig->s; \ 60 } while (0) 61 #endif 62 63 /* Return the version of the middleware API */ 64 uint32_t sk_api_version(void); 65 66 /* Enroll a U2F key (private key generation) */ 67 int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 68 const char *application, uint8_t flags, const char *pin, 69 struct sk_option **options, struct sk_enroll_response **enroll_response); 70 71 /* Sign a challenge */ 72 int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, 73 const char *application, const uint8_t *key_handle, size_t key_handle_len, 74 uint8_t flags, const char *pin, struct sk_option **options, 75 struct sk_sign_response **sign_response); 76 77 /* Load resident keys */ 78 int sk_load_resident_keys(const char *pin, struct sk_option **options, 79 struct sk_resident_key ***rks, size_t *nrks); 80 81 static void skdebug(const char *func, const char *fmt, ...) 82 __attribute__((__format__ (printf, 2, 3))); 83 84 static void 85 skdebug(const char *func, const char *fmt, ...) 86 { 87 #if !defined(SK_STANDALONE) 88 char *msg; 89 va_list ap; 90 91 va_start(ap, fmt); 92 xvasprintf(&msg, fmt, ap); 93 va_end(ap); 94 debug("%s: %s", func, msg); 95 free(msg); 96 #elif defined(SK_DEBUG) 97 va_list ap; 98 99 va_start(ap, fmt); 100 fprintf(stderr, "%s: ", func); 101 vfprintf(stderr, fmt, ap); 102 fputc('\n', stderr); 103 va_end(ap); 104 #else 105 (void)func; /* XXX */ 106 (void)fmt; /* XXX */ 107 #endif 108 } 109 110 uint32_t 111 sk_api_version(void) 112 { 113 return SSH_SK_VERSION_MAJOR; 114 } 115 116 /* Select the first identified FIDO device attached to the system */ 117 static char * 118 pick_first_device(void) 119 { 120 char *ret = NULL; 121 fido_dev_info_t *devlist = NULL; 122 size_t olen = 0; 123 int r; 124 const fido_dev_info_t *di; 125 126 if ((devlist = fido_dev_info_new(1)) == NULL) { 127 skdebug(__func__, "fido_dev_info_new failed"); 128 goto out; 129 } 130 if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { 131 skdebug(__func__, "fido_dev_info_manifest failed: %s", 132 fido_strerr(r)); 133 goto out; 134 } 135 if (olen != 1) { 136 skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); 137 goto out; 138 } 139 di = fido_dev_info_ptr(devlist, 0); 140 if ((ret = strdup(fido_dev_info_path(di))) == NULL) { 141 skdebug(__func__, "fido_dev_info_path failed"); 142 goto out; 143 } 144 out: 145 fido_dev_info_free(&devlist, 1); 146 return ret; 147 } 148 149 /* Check if the specified key handle exists on a given device. */ 150 static int 151 try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, 152 const char *application, const uint8_t *key_handle, size_t key_handle_len) 153 { 154 fido_assert_t *assert = NULL; 155 int r = FIDO_ERR_INTERNAL; 156 157 if ((assert = fido_assert_new()) == NULL) { 158 skdebug(__func__, "fido_assert_new failed"); 159 goto out; 160 } 161 if ((r = fido_assert_set_clientdata_hash(assert, message, 162 message_len)) != FIDO_OK) { 163 skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 164 fido_strerr(r)); 165 goto out; 166 } 167 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 168 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 169 goto out; 170 } 171 if ((r = fido_assert_allow_cred(assert, key_handle, 172 key_handle_len)) != FIDO_OK) { 173 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 174 goto out; 175 } 176 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { 177 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); 178 goto out; 179 } 180 r = fido_dev_get_assert(dev, assert, NULL); 181 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 182 if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { 183 /* U2F tokens may return this */ 184 r = FIDO_OK; 185 } 186 out: 187 fido_assert_free(&assert); 188 189 return r != FIDO_OK ? -1 : 0; 190 } 191 192 /* Iterate over configured devices looking for a specific key handle */ 193 static fido_dev_t * 194 find_device(const char *path, const uint8_t *message, size_t message_len, 195 const char *application, const uint8_t *key_handle, size_t key_handle_len) 196 { 197 fido_dev_info_t *devlist = NULL; 198 fido_dev_t *dev = NULL; 199 size_t devlist_len = 0, i; 200 int r; 201 202 if (path != NULL) { 203 if ((dev = fido_dev_new()) == NULL) { 204 skdebug(__func__, "fido_dev_new failed"); 205 return NULL; 206 } 207 if ((r = fido_dev_open(dev, path)) != FIDO_OK) { 208 skdebug(__func__, "fido_dev_open failed"); 209 fido_dev_free(&dev); 210 return NULL; 211 } 212 return dev; 213 } 214 215 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 216 skdebug(__func__, "fido_dev_info_new failed"); 217 goto out; 218 } 219 if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, 220 &devlist_len)) != FIDO_OK) { 221 skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); 222 goto out; 223 } 224 225 skdebug(__func__, "found %zu device(s)", devlist_len); 226 227 for (i = 0; i < devlist_len; i++) { 228 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); 229 230 if (di == NULL) { 231 skdebug(__func__, "fido_dev_info_ptr %zu failed", i); 232 continue; 233 } 234 if ((path = fido_dev_info_path(di)) == NULL) { 235 skdebug(__func__, "fido_dev_info_path %zu failed", i); 236 continue; 237 } 238 skdebug(__func__, "trying device %zu: %s", i, path); 239 if ((dev = fido_dev_new()) == NULL) { 240 skdebug(__func__, "fido_dev_new failed"); 241 continue; 242 } 243 if ((r = fido_dev_open(dev, path)) != FIDO_OK) { 244 skdebug(__func__, "fido_dev_open failed"); 245 fido_dev_free(&dev); 246 continue; 247 } 248 if (try_device(dev, message, message_len, application, 249 key_handle, key_handle_len) == 0) { 250 skdebug(__func__, "found key"); 251 break; 252 } 253 fido_dev_close(dev); 254 fido_dev_free(&dev); 255 } 256 257 out: 258 if (devlist != NULL) 259 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 260 261 return dev; 262 } 263 264 #ifdef WITH_OPENSSL 265 /* 266 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, 267 * but the API expects a SEC1 octet string. 268 */ 269 static int 270 pack_public_key_ecdsa(const fido_cred_t *cred, 271 struct sk_enroll_response *response) 272 { 273 const uint8_t *ptr; 274 BIGNUM *x = NULL, *y = NULL; 275 EC_POINT *q = NULL; 276 EC_GROUP *g = NULL; 277 int ret = -1; 278 279 response->public_key = NULL; 280 response->public_key_len = 0; 281 282 if ((x = BN_new()) == NULL || 283 (y = BN_new()) == NULL || 284 (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 285 (q = EC_POINT_new(g)) == NULL) { 286 skdebug(__func__, "libcrypto setup failed"); 287 goto out; 288 } 289 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 290 skdebug(__func__, "fido_cred_pubkey_ptr failed"); 291 goto out; 292 } 293 if (fido_cred_pubkey_len(cred) != 64) { 294 skdebug(__func__, "bad fido_cred_pubkey_len %zu", 295 fido_cred_pubkey_len(cred)); 296 goto out; 297 } 298 299 if (BN_bin2bn(ptr, 32, x) == NULL || 300 BN_bin2bn(ptr + 32, 32, y) == NULL) { 301 skdebug(__func__, "BN_bin2bn failed"); 302 goto out; 303 } 304 if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { 305 skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); 306 goto out; 307 } 308 response->public_key_len = EC_POINT_point2oct(g, q, 309 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); 310 if (response->public_key_len == 0 || response->public_key_len > 2048) { 311 skdebug(__func__, "bad pubkey length %zu", 312 response->public_key_len); 313 goto out; 314 } 315 if ((response->public_key = malloc(response->public_key_len)) == NULL) { 316 skdebug(__func__, "malloc pubkey failed"); 317 goto out; 318 } 319 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, 320 response->public_key, response->public_key_len, NULL) == 0) { 321 skdebug(__func__, "EC_POINT_point2oct failed"); 322 goto out; 323 } 324 /* success */ 325 ret = 0; 326 out: 327 if (ret != 0 && response->public_key != NULL) { 328 memset(response->public_key, 0, response->public_key_len); 329 free(response->public_key); 330 response->public_key = NULL; 331 } 332 EC_POINT_free(q); 333 EC_GROUP_free(g); 334 BN_clear_free(x); 335 BN_clear_free(y); 336 return ret; 337 } 338 #endif /* WITH_OPENSSL */ 339 340 static int 341 pack_public_key_ed25519(const fido_cred_t *cred, 342 struct sk_enroll_response *response) 343 { 344 const uint8_t *ptr; 345 size_t len; 346 int ret = -1; 347 348 response->public_key = NULL; 349 response->public_key_len = 0; 350 351 if ((len = fido_cred_pubkey_len(cred)) != 32) { 352 skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); 353 goto out; 354 } 355 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 356 skdebug(__func__, "fido_cred_pubkey_ptr failed"); 357 goto out; 358 } 359 response->public_key_len = len; 360 if ((response->public_key = malloc(response->public_key_len)) == NULL) { 361 skdebug(__func__, "malloc pubkey failed"); 362 goto out; 363 } 364 memcpy(response->public_key, ptr, len); 365 ret = 0; 366 out: 367 if (ret != 0) 368 free(response->public_key); 369 return ret; 370 } 371 372 static int 373 pack_public_key(uint32_t alg, const fido_cred_t *cred, 374 struct sk_enroll_response *response) 375 { 376 switch(alg) { 377 #ifdef WITH_OPENSSL 378 case SSH_SK_ECDSA: 379 return pack_public_key_ecdsa(cred, response); 380 #endif /* WITH_OPENSSL */ 381 case SSH_SK_ED25519: 382 return pack_public_key_ed25519(cred, response); 383 default: 384 return -1; 385 } 386 } 387 388 static int 389 fidoerr_to_skerr(int fidoerr) 390 { 391 switch (fidoerr) { 392 case FIDO_ERR_UNSUPPORTED_OPTION: 393 case FIDO_ERR_UNSUPPORTED_ALGORITHM: 394 return SSH_SK_ERR_UNSUPPORTED; 395 case FIDO_ERR_PIN_REQUIRED: 396 case FIDO_ERR_PIN_INVALID: 397 return SSH_SK_ERR_PIN_REQUIRED; 398 default: 399 return -1; 400 } 401 } 402 403 static int 404 check_enroll_options(struct sk_option **options, char **devicep, 405 uint8_t *user_id, size_t user_id_len) 406 { 407 size_t i; 408 409 if (options == NULL) 410 return 0; 411 for (i = 0; options[i] != NULL; i++) { 412 if (strcmp(options[i]->name, "device") == 0) { 413 if ((*devicep = strdup(options[i]->value)) == NULL) { 414 skdebug(__func__, "strdup device failed"); 415 return -1; 416 } 417 skdebug(__func__, "requested device %s", *devicep); 418 } else if (strcmp(options[i]->name, "user") == 0) { 419 if (strlcpy(user_id, options[i]->value, user_id_len) >= 420 user_id_len) { 421 skdebug(__func__, "user too long"); 422 return -1; 423 } 424 skdebug(__func__, "requested user %s", 425 (char *)user_id); 426 } else { 427 skdebug(__func__, "requested unsupported option %s", 428 options[i]->name); 429 if (options[i]->required) { 430 skdebug(__func__, "unknown required option"); 431 return -1; 432 } 433 } 434 } 435 return 0; 436 } 437 438 int 439 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 440 const char *application, uint8_t flags, const char *pin, 441 struct sk_option **options, struct sk_enroll_response **enroll_response) 442 { 443 fido_cred_t *cred = NULL; 444 fido_dev_t *dev = NULL; 445 const uint8_t *ptr; 446 uint8_t user_id[32]; 447 struct sk_enroll_response *response = NULL; 448 size_t len; 449 int cose_alg; 450 int ret = SSH_SK_ERR_GENERAL; 451 int r; 452 char *device = NULL; 453 454 #ifdef SK_DEBUG 455 fido_init(FIDO_DEBUG); 456 #endif 457 if (enroll_response == NULL) { 458 skdebug(__func__, "enroll_response == NULL"); 459 goto out; 460 } 461 memset(user_id, 0, sizeof(user_id)); 462 if (check_enroll_options(options, &device, 463 user_id, sizeof(user_id)) != 0) 464 goto out; /* error already logged */ 465 466 *enroll_response = NULL; 467 switch(alg) { 468 #ifdef WITH_OPENSSL 469 case SSH_SK_ECDSA: 470 cose_alg = COSE_ES256; 471 break; 472 #endif /* WITH_OPENSSL */ 473 case SSH_SK_ED25519: 474 cose_alg = COSE_EDDSA; 475 break; 476 default: 477 skdebug(__func__, "unsupported key type %d", alg); 478 goto out; 479 } 480 if (device == NULL && (device = pick_first_device()) == NULL) { 481 ret = SSH_SK_ERR_DEVICE_NOT_FOUND; 482 skdebug(__func__, "pick_first_device failed"); 483 goto out; 484 } 485 skdebug(__func__, "using device %s", device); 486 if ((cred = fido_cred_new()) == NULL) { 487 skdebug(__func__, "fido_cred_new failed"); 488 goto out; 489 } 490 if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { 491 skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); 492 goto out; 493 } 494 if ((r = fido_cred_set_clientdata_hash(cred, challenge, 495 challenge_len)) != FIDO_OK) { 496 skdebug(__func__, "fido_cred_set_clientdata_hash: %s", 497 fido_strerr(r)); 498 goto out; 499 } 500 if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ? 501 FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { 502 skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); 503 goto out; 504 } 505 if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), 506 "openssh", "openssh", NULL)) != FIDO_OK) { 507 skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); 508 goto out; 509 } 510 if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { 511 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); 512 goto out; 513 } 514 if ((dev = fido_dev_new()) == NULL) { 515 skdebug(__func__, "fido_dev_new failed"); 516 goto out; 517 } 518 if ((r = fido_dev_open(dev, device)) != FIDO_OK) { 519 skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); 520 goto out; 521 } 522 if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { 523 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); 524 ret = fidoerr_to_skerr(r); 525 goto out; 526 } 527 if (fido_cred_x5c_ptr(cred) != NULL) { 528 if ((r = fido_cred_verify(cred)) != FIDO_OK) { 529 skdebug(__func__, "fido_cred_verify: %s", 530 fido_strerr(r)); 531 goto out; 532 } 533 } else { 534 skdebug(__func__, "self-attested credential"); 535 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { 536 skdebug(__func__, "fido_cred_verify_self: %s", 537 fido_strerr(r)); 538 goto out; 539 } 540 } 541 if ((response = calloc(1, sizeof(*response))) == NULL) { 542 skdebug(__func__, "calloc response failed"); 543 goto out; 544 } 545 if (pack_public_key(alg, cred, response) != 0) { 546 skdebug(__func__, "pack_public_key failed"); 547 goto out; 548 } 549 if ((ptr = fido_cred_id_ptr(cred)) != NULL) { 550 len = fido_cred_id_len(cred); 551 if ((response->key_handle = calloc(1, len)) == NULL) { 552 skdebug(__func__, "calloc key handle failed"); 553 goto out; 554 } 555 memcpy(response->key_handle, ptr, len); 556 response->key_handle_len = len; 557 } 558 if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { 559 len = fido_cred_sig_len(cred); 560 if ((response->signature = calloc(1, len)) == NULL) { 561 skdebug(__func__, "calloc signature failed"); 562 goto out; 563 } 564 memcpy(response->signature, ptr, len); 565 response->signature_len = len; 566 } 567 if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { 568 len = fido_cred_x5c_len(cred); 569 debug3("%s: attestation cert len=%zu", __func__, len); 570 if ((response->attestation_cert = calloc(1, len)) == NULL) { 571 skdebug(__func__, "calloc attestation cert failed"); 572 goto out; 573 } 574 memcpy(response->attestation_cert, ptr, len); 575 response->attestation_cert_len = len; 576 } 577 *enroll_response = response; 578 response = NULL; 579 ret = 0; 580 out: 581 free(device); 582 if (response != NULL) { 583 free(response->public_key); 584 free(response->key_handle); 585 free(response->signature); 586 free(response->attestation_cert); 587 free(response); 588 } 589 if (dev != NULL) { 590 fido_dev_close(dev); 591 fido_dev_free(&dev); 592 } 593 if (cred != NULL) { 594 fido_cred_free(&cred); 595 } 596 return ret; 597 } 598 599 #ifdef WITH_OPENSSL 600 static int 601 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) 602 { 603 ECDSA_SIG *sig = NULL; 604 const BIGNUM *sig_r, *sig_s; 605 const unsigned char *cp; 606 size_t sig_len; 607 int ret = -1; 608 609 cp = fido_assert_sig_ptr(assert, 0); 610 sig_len = fido_assert_sig_len(assert, 0); 611 if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { 612 skdebug(__func__, "d2i_ECDSA_SIG failed"); 613 goto out; 614 } 615 ECDSA_SIG_get0(sig, &sig_r, &sig_s); 616 response->sig_r_len = BN_num_bytes(sig_r); 617 response->sig_s_len = BN_num_bytes(sig_s); 618 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || 619 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { 620 skdebug(__func__, "calloc signature failed"); 621 goto out; 622 } 623 BN_bn2bin(sig_r, response->sig_r); 624 BN_bn2bin(sig_s, response->sig_s); 625 ret = 0; 626 out: 627 ECDSA_SIG_free(sig); 628 if (ret != 0) { 629 free(response->sig_r); 630 free(response->sig_s); 631 response->sig_r = NULL; 632 response->sig_s = NULL; 633 } 634 return ret; 635 } 636 #endif /* WITH_OPENSSL */ 637 638 static int 639 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) 640 { 641 const unsigned char *ptr; 642 size_t len; 643 int ret = -1; 644 645 ptr = fido_assert_sig_ptr(assert, 0); 646 len = fido_assert_sig_len(assert, 0); 647 if (len != 64) { 648 skdebug(__func__, "bad length %zu", len); 649 goto out; 650 } 651 response->sig_r_len = len; 652 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { 653 skdebug(__func__, "calloc signature failed"); 654 goto out; 655 } 656 memcpy(response->sig_r, ptr, len); 657 ret = 0; 658 out: 659 if (ret != 0) { 660 free(response->sig_r); 661 response->sig_r = NULL; 662 } 663 return ret; 664 } 665 666 static int 667 pack_sig(uint32_t alg, fido_assert_t *assert, 668 struct sk_sign_response *response) 669 { 670 switch(alg) { 671 #ifdef WITH_OPENSSL 672 case SSH_SK_ECDSA: 673 return pack_sig_ecdsa(assert, response); 674 #endif /* WITH_OPENSSL */ 675 case SSH_SK_ED25519: 676 return pack_sig_ed25519(assert, response); 677 default: 678 return -1; 679 } 680 } 681 682 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */ 683 static int 684 check_sign_load_resident_options(struct sk_option **options, char **devicep) 685 { 686 size_t i; 687 688 if (options == NULL) 689 return 0; 690 for (i = 0; options[i] != NULL; i++) { 691 if (strcmp(options[i]->name, "device") == 0) { 692 if ((*devicep = strdup(options[i]->value)) == NULL) { 693 skdebug(__func__, "strdup device failed"); 694 return -1; 695 } 696 skdebug(__func__, "requested device %s", *devicep); 697 } else { 698 skdebug(__func__, "requested unsupported option %s", 699 options[i]->name); 700 if (options[i]->required) { 701 skdebug(__func__, "unknown required option"); 702 return -1; 703 } 704 } 705 } 706 return 0; 707 } 708 709 int 710 sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, 711 const char *application, 712 const uint8_t *key_handle, size_t key_handle_len, 713 uint8_t flags, const char *pin, struct sk_option **options, 714 struct sk_sign_response **sign_response) 715 { 716 fido_assert_t *assert = NULL; 717 char *device = NULL; 718 fido_dev_t *dev = NULL; 719 struct sk_sign_response *response = NULL; 720 int ret = SSH_SK_ERR_GENERAL; 721 int r; 722 723 #ifdef SK_DEBUG 724 fido_init(FIDO_DEBUG); 725 #endif 726 727 if (sign_response == NULL) { 728 skdebug(__func__, "sign_response == NULL"); 729 goto out; 730 } 731 *sign_response = NULL; 732 if (check_sign_load_resident_options(options, &device) != 0) 733 goto out; /* error already logged */ 734 if ((dev = find_device(device, message, message_len, 735 application, key_handle, key_handle_len)) == NULL) { 736 skdebug(__func__, "couldn't find device for key handle"); 737 goto out; 738 } 739 if ((assert = fido_assert_new()) == NULL) { 740 skdebug(__func__, "fido_assert_new failed"); 741 goto out; 742 } 743 if ((r = fido_assert_set_clientdata_hash(assert, message, 744 message_len)) != FIDO_OK) { 745 skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 746 fido_strerr(r)); 747 goto out; 748 } 749 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 750 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 751 goto out; 752 } 753 if ((r = fido_assert_allow_cred(assert, key_handle, 754 key_handle_len)) != FIDO_OK) { 755 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 756 goto out; 757 } 758 if ((r = fido_assert_set_up(assert, 759 (flags & SSH_SK_USER_PRESENCE_REQD) ? 760 FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { 761 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); 762 goto out; 763 } 764 if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { 765 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 766 goto out; 767 } 768 if ((response = calloc(1, sizeof(*response))) == NULL) { 769 skdebug(__func__, "calloc response failed"); 770 goto out; 771 } 772 response->flags = fido_assert_flags(assert, 0); 773 response->counter = fido_assert_sigcount(assert, 0); 774 if (pack_sig(alg, assert, response) != 0) { 775 skdebug(__func__, "pack_sig failed"); 776 goto out; 777 } 778 *sign_response = response; 779 response = NULL; 780 ret = 0; 781 out: 782 free(device); 783 if (response != NULL) { 784 free(response->sig_r); 785 free(response->sig_s); 786 free(response); 787 } 788 if (dev != NULL) { 789 fido_dev_close(dev); 790 fido_dev_free(&dev); 791 } 792 if (assert != NULL) { 793 fido_assert_free(&assert); 794 } 795 return ret; 796 } 797 798 static int 799 read_rks(const char *devpath, const char *pin, 800 struct sk_resident_key ***rksp, size_t *nrksp) 801 { 802 int ret = SSH_SK_ERR_GENERAL, r = -1; 803 fido_dev_t *dev = NULL; 804 fido_credman_metadata_t *metadata = NULL; 805 fido_credman_rp_t *rp = NULL; 806 fido_credman_rk_t *rk = NULL; 807 size_t i, j, nrp, nrk; 808 const fido_cred_t *cred; 809 struct sk_resident_key *srk = NULL, **tmp; 810 811 if ((dev = fido_dev_new()) == NULL) { 812 skdebug(__func__, "fido_dev_new failed"); 813 return ret; 814 } 815 if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) { 816 skdebug(__func__, "fido_dev_open %s failed: %s", 817 devpath, fido_strerr(r)); 818 fido_dev_free(&dev); 819 return ret; 820 } 821 if ((metadata = fido_credman_metadata_new()) == NULL) { 822 skdebug(__func__, "alloc failed"); 823 goto out; 824 } 825 826 if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) { 827 if (r == FIDO_ERR_INVALID_COMMAND) { 828 skdebug(__func__, "device %s does not support " 829 "resident keys", devpath); 830 ret = 0; 831 goto out; 832 } 833 skdebug(__func__, "get metadata for %s failed: %s", 834 devpath, fido_strerr(r)); 835 ret = fidoerr_to_skerr(r); 836 goto out; 837 } 838 skdebug(__func__, "existing %llu, remaining %llu", 839 (unsigned long long)fido_credman_rk_existing(metadata), 840 (unsigned long long)fido_credman_rk_remaining(metadata)); 841 if ((rp = fido_credman_rp_new()) == NULL) { 842 skdebug(__func__, "alloc rp failed"); 843 goto out; 844 } 845 if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) { 846 skdebug(__func__, "get RPs for %s failed: %s", 847 devpath, fido_strerr(r)); 848 goto out; 849 } 850 nrp = fido_credman_rp_count(rp); 851 skdebug(__func__, "Device %s has resident keys for %zu RPs", 852 devpath, nrp); 853 854 /* Iterate over RP IDs that have resident keys */ 855 for (i = 0; i < nrp; i++) { 856 skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", 857 i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i), 858 fido_credman_rp_id_hash_len(rp, i)); 859 860 /* Skip non-SSH RP IDs */ 861 if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) 862 continue; 863 864 fido_credman_rk_free(&rk); 865 if ((rk = fido_credman_rk_new()) == NULL) { 866 skdebug(__func__, "alloc rk failed"); 867 goto out; 868 } 869 if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i), 870 rk, pin)) != 0) { 871 skdebug(__func__, "get RKs for %s slot %zu failed: %s", 872 devpath, i, fido_strerr(r)); 873 goto out; 874 } 875 nrk = fido_credman_rk_count(rk); 876 skdebug(__func__, "RP \"%s\" has %zu resident keys", 877 fido_credman_rp_id(rp, i), nrk); 878 879 /* Iterate over resident keys for this RP ID */ 880 for (j = 0; j < nrk; j++) { 881 if ((cred = fido_credman_rk(rk, j)) == NULL) { 882 skdebug(__func__, "no RK in slot %zu", j); 883 continue; 884 } 885 skdebug(__func__, "Device %s RP \"%s\" slot %zu: " 886 "type %d", devpath, fido_credman_rp_id(rp, i), j, 887 fido_cred_type(cred)); 888 889 /* build response entry */ 890 if ((srk = calloc(1, sizeof(*srk))) == NULL || 891 (srk->key.key_handle = calloc(1, 892 fido_cred_id_len(cred))) == NULL || 893 (srk->application = strdup(fido_credman_rp_id(rp, 894 i))) == NULL) { 895 skdebug(__func__, "alloc sk_resident_key"); 896 goto out; 897 } 898 899 srk->key.key_handle_len = fido_cred_id_len(cred); 900 memcpy(srk->key.key_handle, 901 fido_cred_id_ptr(cred), 902 srk->key.key_handle_len); 903 904 switch (fido_cred_type(cred)) { 905 case COSE_ES256: 906 srk->alg = SSH_SK_ECDSA; 907 break; 908 case COSE_EDDSA: 909 srk->alg = SSH_SK_ED25519; 910 break; 911 default: 912 skdebug(__func__, "unsupported key type %d", 913 fido_cred_type(cred)); 914 goto out; /* XXX free rk and continue */ 915 } 916 917 if ((r = pack_public_key(srk->alg, cred, 918 &srk->key)) != 0) { 919 skdebug(__func__, "pack public key failed"); 920 goto out; 921 } 922 /* append */ 923 if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, 924 sizeof(**rksp))) == NULL) { 925 skdebug(__func__, "alloc rksp"); 926 goto out; 927 } 928 *rksp = tmp; 929 (*rksp)[(*nrksp)++] = srk; 930 srk = NULL; 931 } 932 } 933 /* Success */ 934 ret = 0; 935 out: 936 if (srk != NULL) { 937 free(srk->application); 938 freezero(srk->key.public_key, srk->key.public_key_len); 939 freezero(srk->key.key_handle, srk->key.key_handle_len); 940 freezero(srk, sizeof(*srk)); 941 } 942 fido_credman_rp_free(&rp); 943 fido_credman_rk_free(&rk); 944 fido_dev_close(dev); 945 fido_dev_free(&dev); 946 fido_credman_metadata_free(&metadata); 947 return ret; 948 } 949 950 int 951 sk_load_resident_keys(const char *pin, struct sk_option **options, 952 struct sk_resident_key ***rksp, size_t *nrksp) 953 { 954 int ret = SSH_SK_ERR_GENERAL, r = -1; 955 fido_dev_info_t *devlist = NULL; 956 size_t i, ndev = 0, nrks = 0; 957 const fido_dev_info_t *di; 958 struct sk_resident_key **rks = NULL; 959 char *device = NULL; 960 *rksp = NULL; 961 *nrksp = 0; 962 963 if (check_sign_load_resident_options(options, &device) != 0) 964 goto out; /* error already logged */ 965 if (device != NULL) { 966 skdebug(__func__, "trying %s", device); 967 if ((r = read_rks(device, pin, &rks, &nrks)) != 0) { 968 skdebug(__func__, "read_rks failed for %s", device); 969 ret = r; 970 goto out; 971 } 972 } else { 973 /* Try all devices */ 974 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 975 skdebug(__func__, "fido_dev_info_new failed"); 976 goto out; 977 } 978 if ((r = fido_dev_info_manifest(devlist, 979 MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { 980 skdebug(__func__, "fido_dev_info_manifest failed: %s", 981 fido_strerr(r)); 982 goto out; 983 } 984 for (i = 0; i < ndev; i++) { 985 if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { 986 skdebug(__func__, "no dev info at %zu", i); 987 continue; 988 } 989 skdebug(__func__, "trying %s", fido_dev_info_path(di)); 990 if ((r = read_rks(fido_dev_info_path(di), pin, 991 &rks, &nrks)) != 0) { 992 skdebug(__func__, "read_rks failed for %s", 993 fido_dev_info_path(di)); 994 /* remember last error */ 995 ret = r; 996 continue; 997 } 998 } 999 } 1000 /* success, unless we have no keys but a specific error */ 1001 if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) 1002 ret = 0; 1003 *rksp = rks; 1004 *nrksp = nrks; 1005 rks = NULL; 1006 nrks = 0; 1007 out: 1008 free(device); 1009 for (i = 0; i < nrks; i++) { 1010 free(rks[i]->application); 1011 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 1012 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 1013 freezero(rks[i], sizeof(*rks[i])); 1014 } 1015 free(rks); 1016 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 1017 return ret; 1018 } 1019 1020