1 /* $OpenBSD: ssh-sk.c,v 1.35 2021/02/26 00:16:58 djm Exp $ */ 2 /* 3 * Copyright (c) 2019 Google LLC 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* #define DEBUG_SK 1 */ 19 20 #include "includes.h" 21 22 #ifdef ENABLE_SK 23 24 #include <dlfcn.h> 25 #include <stddef.h> 26 #ifdef HAVE_STDINT_H 27 # include <stdint.h> 28 #endif 29 #include <string.h> 30 #include <stdio.h> 31 32 #ifdef WITH_OPENSSL 33 #include <openssl/objects.h> 34 #include <openssl/ec.h> 35 #endif /* WITH_OPENSSL */ 36 37 #include "log.h" 38 #include "misc.h" 39 #include "sshbuf.h" 40 #include "sshkey.h" 41 #include "ssherr.h" 42 #include "digest.h" 43 44 #include "ssh-sk.h" 45 #include "sk-api.h" 46 #include "crypto_api.h" 47 48 struct sshsk_provider { 49 char *path; 50 void *dlhandle; 51 52 /* Return the version of the middleware API */ 53 uint32_t (*sk_api_version)(void); 54 55 /* Enroll a U2F key (private key generation) */ 56 int (*sk_enroll)(int alg, const uint8_t *challenge, 57 size_t challenge_len, const char *application, uint8_t flags, 58 const char *pin, struct sk_option **opts, 59 struct sk_enroll_response **enroll_response); 60 61 /* Sign a challenge */ 62 int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, 63 const char *application, 64 const uint8_t *key_handle, size_t key_handle_len, 65 uint8_t flags, const char *pin, struct sk_option **opts, 66 struct sk_sign_response **sign_response); 67 68 /* Enumerate resident keys */ 69 int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, 70 struct sk_resident_key ***rks, size_t *nrks); 71 }; 72 73 /* Built-in version */ 74 int ssh_sk_enroll(int alg, const uint8_t *challenge, 75 size_t challenge_len, const char *application, uint8_t flags, 76 const char *pin, struct sk_option **opts, 77 struct sk_enroll_response **enroll_response); 78 int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 79 const char *application, 80 const uint8_t *key_handle, size_t key_handle_len, 81 uint8_t flags, const char *pin, struct sk_option **opts, 82 struct sk_sign_response **sign_response); 83 int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, 84 struct sk_resident_key ***rks, size_t *nrks); 85 86 static void 87 sshsk_free(struct sshsk_provider *p) 88 { 89 if (p == NULL) 90 return; 91 free(p->path); 92 if (p->dlhandle != NULL) 93 dlclose(p->dlhandle); 94 free(p); 95 } 96 97 static struct sshsk_provider * 98 sshsk_open(const char *path) 99 { 100 struct sshsk_provider *ret = NULL; 101 uint32_t version; 102 103 if (path == NULL || *path == '\0') { 104 error("No FIDO SecurityKeyProvider specified"); 105 return NULL; 106 } 107 if ((ret = calloc(1, sizeof(*ret))) == NULL) { 108 error_f("calloc failed"); 109 return NULL; 110 } 111 if ((ret->path = strdup(path)) == NULL) { 112 error_f("strdup failed"); 113 goto fail; 114 } 115 /* Skip the rest if we're using the linked in middleware */ 116 if (strcasecmp(ret->path, "internal") == 0) { 117 #ifdef ENABLE_SK_INTERNAL 118 ret->sk_enroll = ssh_sk_enroll; 119 ret->sk_sign = ssh_sk_sign; 120 ret->sk_load_resident_keys = ssh_sk_load_resident_keys; 121 #else 122 error("internal security key support not enabled"); 123 #endif 124 return ret; 125 } 126 if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { 127 error("Provider \"%s\" dlopen failed: %s", path, dlerror()); 128 goto fail; 129 } 130 if ((ret->sk_api_version = dlsym(ret->dlhandle, 131 "sk_api_version")) == NULL) { 132 error("Provider \"%s\" dlsym(sk_api_version) failed: %s", 133 path, dlerror()); 134 goto fail; 135 } 136 version = ret->sk_api_version(); 137 debug_f("provider %s implements version 0x%08lx", ret->path, 138 (u_long)version); 139 if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { 140 error("Provider \"%s\" implements unsupported " 141 "version 0x%08lx (supported: 0x%08lx)", 142 path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR); 143 goto fail; 144 } 145 if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { 146 error("Provider %s dlsym(sk_enroll) failed: %s", 147 path, dlerror()); 148 goto fail; 149 } 150 if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { 151 error("Provider \"%s\" dlsym(sk_sign) failed: %s", 152 path, dlerror()); 153 goto fail; 154 } 155 if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, 156 "sk_load_resident_keys")) == NULL) { 157 error("Provider \"%s\" dlsym(sk_load_resident_keys) " 158 "failed: %s", path, dlerror()); 159 goto fail; 160 } 161 /* success */ 162 return ret; 163 fail: 164 sshsk_free(ret); 165 return NULL; 166 } 167 168 static void 169 sshsk_free_enroll_response(struct sk_enroll_response *r) 170 { 171 if (r == NULL) 172 return; 173 freezero(r->key_handle, r->key_handle_len); 174 freezero(r->public_key, r->public_key_len); 175 freezero(r->signature, r->signature_len); 176 freezero(r->attestation_cert, r->attestation_cert_len); 177 freezero(r->authdata, r->authdata_len); 178 freezero(r, sizeof(*r)); 179 } 180 181 static void 182 sshsk_free_sign_response(struct sk_sign_response *r) 183 { 184 if (r == NULL) 185 return; 186 freezero(r->sig_r, r->sig_r_len); 187 freezero(r->sig_s, r->sig_s_len); 188 freezero(r, sizeof(*r)); 189 } 190 191 #ifdef WITH_OPENSSL 192 /* Assemble key from response */ 193 static int 194 sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 195 { 196 struct sshkey *key = NULL; 197 struct sshbuf *b = NULL; 198 EC_POINT *q = NULL; 199 int r; 200 201 *keyp = NULL; 202 if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { 203 error_f("sshkey_new failed"); 204 r = SSH_ERR_ALLOC_FAIL; 205 goto out; 206 } 207 key->ecdsa_nid = NID_X9_62_prime256v1; 208 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || 209 (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || 210 (b = sshbuf_new()) == NULL) { 211 error_f("allocation failed"); 212 r = SSH_ERR_ALLOC_FAIL; 213 goto out; 214 } 215 if ((r = sshbuf_put_string(b, 216 resp->public_key, resp->public_key_len)) != 0) { 217 error_fr(r, "sshbuf_put_string"); 218 goto out; 219 } 220 if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { 221 error_fr(r, "parse"); 222 r = SSH_ERR_INVALID_FORMAT; 223 goto out; 224 } 225 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { 226 error("Authenticator returned invalid ECDSA key"); 227 r = SSH_ERR_KEY_INVALID_EC_VALUE; 228 goto out; 229 } 230 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { 231 /* XXX assume it is a allocation error */ 232 error_f("allocation failed"); 233 r = SSH_ERR_ALLOC_FAIL; 234 goto out; 235 } 236 /* success */ 237 *keyp = key; 238 key = NULL; /* transferred */ 239 r = 0; 240 out: 241 EC_POINT_free(q); 242 sshkey_free(key); 243 sshbuf_free(b); 244 return r; 245 } 246 #endif /* WITH_OPENSSL */ 247 248 static int 249 sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 250 { 251 struct sshkey *key = NULL; 252 int r; 253 254 *keyp = NULL; 255 if (resp->public_key_len != ED25519_PK_SZ) { 256 error_f("invalid size: %zu", resp->public_key_len); 257 r = SSH_ERR_INVALID_FORMAT; 258 goto out; 259 } 260 if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { 261 error_f("sshkey_new failed"); 262 r = SSH_ERR_ALLOC_FAIL; 263 goto out; 264 } 265 if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { 266 error_f("malloc failed"); 267 r = SSH_ERR_ALLOC_FAIL; 268 goto out; 269 } 270 memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); 271 /* success */ 272 *keyp = key; 273 key = NULL; /* transferred */ 274 r = 0; 275 out: 276 sshkey_free(key); 277 return r; 278 } 279 280 static int 281 sshsk_key_from_response(int alg, const char *application, uint8_t flags, 282 struct sk_enroll_response *resp, struct sshkey **keyp) 283 { 284 struct sshkey *key = NULL; 285 int r = SSH_ERR_INTERNAL_ERROR; 286 287 *keyp = NULL; 288 289 /* Check response validity */ 290 if (resp->public_key == NULL || resp->key_handle == NULL) { 291 error_f("sk_enroll response invalid"); 292 r = SSH_ERR_INVALID_FORMAT; 293 goto out; 294 } 295 switch (alg) { 296 #ifdef WITH_OPENSSL 297 case SSH_SK_ECDSA: 298 if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) 299 goto out; 300 break; 301 #endif /* WITH_OPENSSL */ 302 case SSH_SK_ED25519: 303 if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) 304 goto out; 305 break; 306 default: 307 error_f("unsupported algorithm %d", alg); 308 r = SSH_ERR_INVALID_ARGUMENT; 309 goto out; 310 } 311 key->sk_flags = flags; 312 if ((key->sk_key_handle = sshbuf_new()) == NULL || 313 (key->sk_reserved = sshbuf_new()) == NULL) { 314 error_f("allocation failed"); 315 r = SSH_ERR_ALLOC_FAIL; 316 goto out; 317 } 318 if ((key->sk_application = strdup(application)) == NULL) { 319 error_f("strdup application failed"); 320 r = SSH_ERR_ALLOC_FAIL; 321 goto out; 322 } 323 if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, 324 resp->key_handle_len)) != 0) { 325 error_fr(r, "put key handle"); 326 goto out; 327 } 328 /* success */ 329 r = 0; 330 *keyp = key; 331 key = NULL; 332 out: 333 sshkey_free(key); 334 return r; 335 } 336 337 static int 338 skerr_to_ssherr(int skerr) 339 { 340 switch (skerr) { 341 case SSH_SK_ERR_UNSUPPORTED: 342 return SSH_ERR_FEATURE_UNSUPPORTED; 343 case SSH_SK_ERR_PIN_REQUIRED: 344 return SSH_ERR_KEY_WRONG_PASSPHRASE; 345 case SSH_SK_ERR_DEVICE_NOT_FOUND: 346 return SSH_ERR_DEVICE_NOT_FOUND; 347 case SSH_SK_ERR_GENERAL: 348 default: 349 return SSH_ERR_INVALID_FORMAT; 350 } 351 } 352 353 static void 354 sshsk_free_options(struct sk_option **opts) 355 { 356 size_t i; 357 358 if (opts == NULL) 359 return; 360 for (i = 0; opts[i] != NULL; i++) { 361 free(opts[i]->name); 362 free(opts[i]->value); 363 free(opts[i]); 364 } 365 free(opts); 366 } 367 368 static int 369 sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, 370 const char *name, const char *value, uint8_t required) 371 { 372 struct sk_option **opts = *optsp; 373 size_t nopts = *noptsp; 374 375 if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ 376 sizeof(*opts))) == NULL) { 377 error_f("array alloc failed"); 378 return SSH_ERR_ALLOC_FAIL; 379 } 380 *optsp = opts; 381 *noptsp = nopts + 1; 382 if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { 383 error_f("alloc failed"); 384 return SSH_ERR_ALLOC_FAIL; 385 } 386 if ((opts[nopts]->name = strdup(name)) == NULL || 387 (opts[nopts]->value = strdup(value)) == NULL) { 388 error_f("alloc failed"); 389 return SSH_ERR_ALLOC_FAIL; 390 } 391 opts[nopts]->required = required; 392 return 0; 393 } 394 395 static int 396 make_options(const char *device, const char *user_id, 397 struct sk_option ***optsp) 398 { 399 struct sk_option **opts = NULL; 400 size_t nopts = 0; 401 int r, ret = SSH_ERR_INTERNAL_ERROR; 402 403 if (device != NULL && 404 (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { 405 ret = r; 406 goto out; 407 } 408 if (user_id != NULL && 409 (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { 410 ret = r; 411 goto out; 412 } 413 /* success */ 414 *optsp = opts; 415 opts = NULL; 416 nopts = 0; 417 ret = 0; 418 out: 419 sshsk_free_options(opts); 420 return ret; 421 } 422 423 424 static int 425 fill_attestation_blob(const struct sk_enroll_response *resp, 426 struct sshbuf *attest) 427 { 428 int r; 429 430 if (attest == NULL) 431 return 0; /* nothing to do */ 432 if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || 433 (r = sshbuf_put_string(attest, 434 resp->attestation_cert, resp->attestation_cert_len)) != 0 || 435 (r = sshbuf_put_string(attest, 436 resp->signature, resp->signature_len)) != 0 || 437 (r = sshbuf_put_string(attest, 438 resp->authdata, resp->authdata_len)) != 0 || 439 (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ 440 (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { 441 error_fr(r, "compose"); 442 return r; 443 } 444 /* success */ 445 return 0; 446 } 447 448 int 449 sshsk_enroll(int type, const char *provider_path, const char *device, 450 const char *application, const char *userid, uint8_t flags, 451 const char *pin, struct sshbuf *challenge_buf, 452 struct sshkey **keyp, struct sshbuf *attest) 453 { 454 struct sshsk_provider *skp = NULL; 455 struct sshkey *key = NULL; 456 u_char randchall[32]; 457 const u_char *challenge; 458 size_t challenge_len; 459 struct sk_enroll_response *resp = NULL; 460 struct sk_option **opts = NULL; 461 int r = SSH_ERR_INTERNAL_ERROR; 462 int alg; 463 464 debug_f("provider \"%s\", device \"%s\", application \"%s\", " 465 "userid \"%s\", flags 0x%02x, challenge len %zu%s", 466 provider_path, device, application, userid, flags, 467 challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), 468 (pin != NULL && *pin != '\0') ? " with-pin" : ""); 469 470 *keyp = NULL; 471 if (attest) 472 sshbuf_reset(attest); 473 474 if ((r = make_options(device, userid, &opts)) != 0) 475 goto out; 476 477 switch (type) { 478 #ifdef WITH_OPENSSL 479 case KEY_ECDSA_SK: 480 alg = SSH_SK_ECDSA; 481 break; 482 #endif /* WITH_OPENSSL */ 483 case KEY_ED25519_SK: 484 alg = SSH_SK_ED25519; 485 break; 486 default: 487 error_f("unsupported key type"); 488 r = SSH_ERR_INVALID_ARGUMENT; 489 goto out; 490 } 491 if (provider_path == NULL) { 492 error_f("missing provider"); 493 r = SSH_ERR_INVALID_ARGUMENT; 494 goto out; 495 } 496 if (application == NULL || *application == '\0') { 497 error_f("missing application"); 498 r = SSH_ERR_INVALID_ARGUMENT; 499 goto out; 500 } 501 if (challenge_buf == NULL) { 502 debug_f("using random challenge"); 503 arc4random_buf(randchall, sizeof(randchall)); 504 challenge = randchall; 505 challenge_len = sizeof(randchall); 506 } else if (sshbuf_len(challenge_buf) == 0) { 507 error("Missing enrollment challenge"); 508 r = SSH_ERR_INVALID_ARGUMENT; 509 goto out; 510 } else { 511 challenge = sshbuf_ptr(challenge_buf); 512 challenge_len = sshbuf_len(challenge_buf); 513 debug3_f("using explicit challenge len=%zd", challenge_len); 514 } 515 if ((skp = sshsk_open(provider_path)) == NULL) { 516 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 517 goto out; 518 } 519 /* XXX validate flags? */ 520 /* enroll key */ 521 if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, 522 flags, pin, opts, &resp)) != 0) { 523 debug_f("provider \"%s\" failure %d", provider_path, r); 524 r = skerr_to_ssherr(r); 525 goto out; 526 } 527 528 if ((r = sshsk_key_from_response(alg, application, flags, 529 resp, &key)) != 0) 530 goto out; 531 532 /* Optionally fill in the attestation information */ 533 if ((r = fill_attestation_blob(resp, attest)) != 0) 534 goto out; 535 536 /* success */ 537 *keyp = key; 538 key = NULL; /* transferred */ 539 r = 0; 540 out: 541 sshsk_free_options(opts); 542 sshsk_free(skp); 543 sshkey_free(key); 544 sshsk_free_enroll_response(resp); 545 explicit_bzero(randchall, sizeof(randchall)); 546 return r; 547 } 548 549 #ifdef WITH_OPENSSL 550 static int 551 sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) 552 { 553 struct sshbuf *inner_sig = NULL; 554 int r = SSH_ERR_INTERNAL_ERROR; 555 556 /* Check response validity */ 557 if (resp->sig_r == NULL || resp->sig_s == NULL) { 558 error_f("sk_sign response invalid"); 559 r = SSH_ERR_INVALID_FORMAT; 560 goto out; 561 } 562 if ((inner_sig = sshbuf_new()) == NULL) { 563 r = SSH_ERR_ALLOC_FAIL; 564 goto out; 565 } 566 /* Prepare and append inner signature object */ 567 if ((r = sshbuf_put_bignum2_bytes(inner_sig, 568 resp->sig_r, resp->sig_r_len)) != 0 || 569 (r = sshbuf_put_bignum2_bytes(inner_sig, 570 resp->sig_s, resp->sig_s_len)) != 0) { 571 error_fr(r, "compose inner"); 572 goto out; 573 } 574 if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || 575 (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 576 (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 577 error_fr(r, "compose"); 578 goto out; 579 } 580 #ifdef DEBUG_SK 581 fprintf(stderr, "%s: sig_r:\n", __func__); 582 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 583 fprintf(stderr, "%s: sig_s:\n", __func__); 584 sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); 585 fprintf(stderr, "%s: inner:\n", __func__); 586 sshbuf_dump(inner_sig, stderr); 587 #endif 588 r = 0; 589 out: 590 sshbuf_free(inner_sig); 591 return r; 592 } 593 #endif /* WITH_OPENSSL */ 594 595 static int 596 sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) 597 { 598 int r = SSH_ERR_INTERNAL_ERROR; 599 600 /* Check response validity */ 601 if (resp->sig_r == NULL) { 602 error_f("sk_sign response invalid"); 603 r = SSH_ERR_INVALID_FORMAT; 604 goto out; 605 } 606 if ((r = sshbuf_put_string(sig, 607 resp->sig_r, resp->sig_r_len)) != 0 || 608 (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 609 (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 610 error_fr(r, "compose"); 611 goto out; 612 } 613 #ifdef DEBUG_SK 614 fprintf(stderr, "%s: sig_r:\n", __func__); 615 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 616 #endif 617 r = 0; 618 out: 619 return r; 620 } 621 622 int 623 sshsk_sign(const char *provider_path, struct sshkey *key, 624 u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 625 u_int compat, const char *pin) 626 { 627 struct sshsk_provider *skp = NULL; 628 int r = SSH_ERR_INTERNAL_ERROR; 629 int type, alg; 630 struct sk_sign_response *resp = NULL; 631 struct sshbuf *inner_sig = NULL, *sig = NULL; 632 struct sk_option **opts = NULL; 633 634 debug_f("provider \"%s\", key %s, flags 0x%02x%s", 635 provider_path, sshkey_type(key), key->sk_flags, 636 (pin != NULL && *pin != '\0') ? " with-pin" : ""); 637 638 if (sigp != NULL) 639 *sigp = NULL; 640 if (lenp != NULL) 641 *lenp = 0; 642 type = sshkey_type_plain(key->type); 643 switch (type) { 644 #ifdef WITH_OPENSSL 645 case KEY_ECDSA_SK: 646 alg = SSH_SK_ECDSA; 647 break; 648 #endif /* WITH_OPENSSL */ 649 case KEY_ED25519_SK: 650 alg = SSH_SK_ED25519; 651 break; 652 default: 653 return SSH_ERR_INVALID_ARGUMENT; 654 } 655 if (provider_path == NULL || 656 key->sk_key_handle == NULL || 657 key->sk_application == NULL || *key->sk_application == '\0') { 658 r = SSH_ERR_INVALID_ARGUMENT; 659 goto out; 660 } 661 if ((skp = sshsk_open(provider_path)) == NULL) { 662 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 663 goto out; 664 } 665 #ifdef DEBUG_SK 666 fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n", 667 __func__, key->sk_flags, key->sk_application); 668 fprintf(stderr, "%s: sk_key_handle:\n", __func__); 669 sshbuf_dump(key->sk_key_handle, stderr); 670 #endif 671 if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, 672 sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), 673 key->sk_flags, pin, opts, &resp)) != 0) { 674 debug_f("sk_sign failed with code %d", r); 675 r = skerr_to_ssherr(r); 676 goto out; 677 } 678 /* Assemble signature */ 679 if ((sig = sshbuf_new()) == NULL) { 680 r = SSH_ERR_ALLOC_FAIL; 681 goto out; 682 } 683 if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { 684 error_fr(r, "compose outer"); 685 goto out; 686 } 687 switch (type) { 688 #ifdef WITH_OPENSSL 689 case KEY_ECDSA_SK: 690 if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) 691 goto out; 692 break; 693 #endif /* WITH_OPENSSL */ 694 case KEY_ED25519_SK: 695 if ((r = sshsk_ed25519_sig(resp, sig)) != 0) 696 goto out; 697 break; 698 } 699 #ifdef DEBUG_SK 700 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 701 __func__, resp->flags, resp->counter); 702 fprintf(stderr, "%s: data to sign:\n", __func__); 703 sshbuf_dump_data(data, datalen, stderr); 704 fprintf(stderr, "%s: sigbuf:\n", __func__); 705 sshbuf_dump(sig, stderr); 706 #endif 707 if (sigp != NULL) { 708 if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { 709 r = SSH_ERR_ALLOC_FAIL; 710 goto out; 711 } 712 memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); 713 } 714 if (lenp != NULL) 715 *lenp = sshbuf_len(sig); 716 /* success */ 717 r = 0; 718 out: 719 sshsk_free_options(opts); 720 sshsk_free(skp); 721 sshsk_free_sign_response(resp); 722 sshbuf_free(sig); 723 sshbuf_free(inner_sig); 724 return r; 725 } 726 727 static void 728 sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) 729 { 730 size_t i; 731 732 if (nrks == 0 || rks == NULL) 733 return; 734 for (i = 0; i < nrks; i++) { 735 free(rks[i]->application); 736 freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 737 freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 738 freezero(rks[i]->key.signature, rks[i]->key.signature_len); 739 freezero(rks[i]->key.attestation_cert, 740 rks[i]->key.attestation_cert_len); 741 freezero(rks[i], sizeof(**rks)); 742 } 743 free(rks); 744 } 745 746 int 747 sshsk_load_resident(const char *provider_path, const char *device, 748 const char *pin, struct sshkey ***keysp, size_t *nkeysp) 749 { 750 struct sshsk_provider *skp = NULL; 751 int r = SSH_ERR_INTERNAL_ERROR; 752 struct sk_resident_key **rks = NULL; 753 size_t i, nrks = 0, nkeys = 0; 754 struct sshkey *key = NULL, **keys = NULL, **tmp; 755 uint8_t flags; 756 struct sk_option **opts = NULL; 757 758 debug_f("provider \"%s\"%s", provider_path, 759 (pin != NULL && *pin != '\0') ? ", have-pin": ""); 760 761 if (keysp == NULL || nkeysp == NULL) 762 return SSH_ERR_INVALID_ARGUMENT; 763 *keysp = NULL; 764 *nkeysp = 0; 765 766 if ((r = make_options(device, NULL, &opts)) != 0) 767 goto out; 768 if ((skp = sshsk_open(provider_path)) == NULL) { 769 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 770 goto out; 771 } 772 if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { 773 error("Provider \"%s\" returned failure %d", provider_path, r); 774 r = skerr_to_ssherr(r); 775 goto out; 776 } 777 for (i = 0; i < nrks; i++) { 778 debug3_f("rk %zu: slot = %zu, alg = %d, application = \"%s\"", 779 i, rks[i]->slot, rks[i]->alg, rks[i]->application); 780 /* XXX need better filter here */ 781 if (strncmp(rks[i]->application, "ssh:", 4) != 0) 782 continue; 783 switch (rks[i]->alg) { 784 case SSH_SK_ECDSA: 785 case SSH_SK_ED25519: 786 break; 787 default: 788 continue; 789 } 790 flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; 791 if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD)) 792 flags |= SSH_SK_USER_VERIFICATION_REQD; 793 if ((r = sshsk_key_from_response(rks[i]->alg, 794 rks[i]->application, flags, &rks[i]->key, &key)) != 0) 795 goto out; 796 if ((tmp = recallocarray(keys, nkeys, nkeys + 1, 797 sizeof(*tmp))) == NULL) { 798 error_f("recallocarray failed"); 799 r = SSH_ERR_ALLOC_FAIL; 800 goto out; 801 } 802 keys = tmp; 803 keys[nkeys++] = key; 804 key = NULL; 805 /* XXX synthesise comment */ 806 } 807 /* success */ 808 *keysp = keys; 809 *nkeysp = nkeys; 810 keys = NULL; 811 nkeys = 0; 812 r = 0; 813 out: 814 sshsk_free_options(opts); 815 sshsk_free(skp); 816 sshsk_free_sk_resident_keys(rks, nrks); 817 sshkey_free(key); 818 if (nkeys != 0) { 819 for (i = 0; i < nkeys; i++) 820 sshkey_free(keys[i]); 821 free(keys); 822 } 823 return r; 824 } 825 826 #endif /* ENABLE_SK */ 827