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