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