1 /* $OpenBSD: ssh-sk.c,v 1.16 2019/11/19 22:23:19 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 struct sk_enroll_response **enroll_response); 53 54 /* Sign a challenge */ 55 int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, 56 const char *application, 57 const uint8_t *key_handle, size_t key_handle_len, 58 uint8_t flags, struct sk_sign_response **sign_response); 59 }; 60 61 /* Built-in version */ 62 int ssh_sk_enroll(int alg, const uint8_t *challenge, 63 size_t challenge_len, const char *application, uint8_t flags, 64 struct sk_enroll_response **enroll_response); 65 int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 66 const char *application, 67 const uint8_t *key_handle, size_t key_handle_len, 68 uint8_t flags, struct sk_sign_response **sign_response); 69 70 static void 71 sshsk_free(struct sshsk_provider *p) 72 { 73 if (p == NULL) 74 return; 75 free(p->path); 76 if (p->dlhandle != NULL) 77 dlclose(p->dlhandle); 78 free(p); 79 } 80 81 static struct sshsk_provider * 82 sshsk_open(const char *path) 83 { 84 struct sshsk_provider *ret = NULL; 85 uint32_t version; 86 87 if ((ret = calloc(1, sizeof(*ret))) == NULL) { 88 error("%s: calloc failed", __func__); 89 return NULL; 90 } 91 if ((ret->path = strdup(path)) == NULL) { 92 error("%s: strdup failed", __func__); 93 goto fail; 94 } 95 /* Skip the rest if we're using the linked in middleware */ 96 if (strcasecmp(ret->path, "internal") == 0) { 97 ret->sk_enroll = ssh_sk_enroll; 98 ret->sk_sign = ssh_sk_sign; 99 return ret; 100 } 101 if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { 102 error("Security key provider %s dlopen failed: %s", 103 path, dlerror()); 104 goto fail; 105 } 106 if ((ret->sk_api_version = dlsym(ret->dlhandle, 107 "sk_api_version")) == NULL) { 108 error("Security key provider %s dlsym(sk_api_version) " 109 "failed: %s", path, dlerror()); 110 goto fail; 111 } 112 version = ret->sk_api_version(); 113 debug("%s: provider %s implements version 0x%08lx", __func__, 114 ret->path, (u_long)version); 115 if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { 116 error("Security key provider %s implements unsupported version " 117 "0x%08lx (supported: 0x%08lx)", path, (u_long)version, 118 (u_long)SSH_SK_VERSION_MAJOR); 119 goto fail; 120 } 121 if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { 122 error("Security key provider %s dlsym(sk_enroll) " 123 "failed: %s", path, dlerror()); 124 goto fail; 125 } 126 if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { 127 error("Security key provider %s dlsym(sk_sign) failed: %s", 128 path, dlerror()); 129 goto fail; 130 } 131 /* success */ 132 return ret; 133 fail: 134 sshsk_free(ret); 135 return NULL; 136 } 137 138 static void 139 sshsk_free_enroll_response(struct sk_enroll_response *r) 140 { 141 if (r == NULL) 142 return; 143 freezero(r->key_handle, r->key_handle_len); 144 freezero(r->public_key, r->public_key_len); 145 freezero(r->signature, r->signature_len); 146 freezero(r->attestation_cert, r->attestation_cert_len); 147 freezero(r, sizeof(*r)); 148 }; 149 150 static void 151 sshsk_free_sign_response(struct sk_sign_response *r) 152 { 153 if (r == NULL) 154 return; 155 freezero(r->sig_r, r->sig_r_len); 156 freezero(r->sig_s, r->sig_s_len); 157 freezero(r, sizeof(*r)); 158 }; 159 160 #ifdef WITH_OPENSSL 161 /* Assemble key from response */ 162 static int 163 sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 164 { 165 struct sshkey *key = NULL; 166 struct sshbuf *b = NULL; 167 EC_POINT *q = NULL; 168 int r; 169 170 *keyp = NULL; 171 if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { 172 error("%s: sshkey_new failed", __func__); 173 r = SSH_ERR_ALLOC_FAIL; 174 goto out; 175 } 176 key->ecdsa_nid = NID_X9_62_prime256v1; 177 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || 178 (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || 179 (b = sshbuf_new()) == NULL) { 180 error("%s: allocation failed", __func__); 181 r = SSH_ERR_ALLOC_FAIL; 182 goto out; 183 } 184 if ((r = sshbuf_put_string(b, 185 resp->public_key, resp->public_key_len)) != 0) { 186 error("%s: buffer error: %s", __func__, ssh_err(r)); 187 goto out; 188 } 189 if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { 190 error("%s: parse key: %s", __func__, ssh_err(r)); 191 r = SSH_ERR_INVALID_FORMAT; 192 goto out; 193 } 194 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { 195 error("Security key returned invalid ECDSA key"); 196 r = SSH_ERR_KEY_INVALID_EC_VALUE; 197 goto out; 198 } 199 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { 200 /* XXX assume it is a allocation error */ 201 error("%s: allocation failed", __func__); 202 r = SSH_ERR_ALLOC_FAIL; 203 goto out; 204 } 205 /* success */ 206 *keyp = key; 207 key = NULL; /* transferred */ 208 r = 0; 209 out: 210 EC_POINT_free(q); 211 sshkey_free(key); 212 sshbuf_free(b); 213 return r; 214 } 215 #endif /* WITH_OPENSSL */ 216 217 static int 218 sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 219 { 220 struct sshkey *key = NULL; 221 int r; 222 223 *keyp = NULL; 224 if (resp->public_key_len != ED25519_PK_SZ) { 225 error("%s: invalid size: %zu", __func__, resp->public_key_len); 226 r = SSH_ERR_INVALID_FORMAT; 227 goto out; 228 } 229 if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { 230 error("%s: sshkey_new failed", __func__); 231 r = SSH_ERR_ALLOC_FAIL; 232 goto out; 233 } 234 if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { 235 error("%s: malloc failed", __func__); 236 r = SSH_ERR_ALLOC_FAIL; 237 goto out; 238 } 239 memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); 240 /* success */ 241 *keyp = key; 242 key = NULL; /* transferred */ 243 r = 0; 244 out: 245 sshkey_free(key); 246 return r; 247 } 248 249 int 250 sshsk_enroll(int type, const char *provider_path, const char *application, 251 uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp, 252 struct sshbuf *attest) 253 { 254 struct sshsk_provider *skp = NULL; 255 struct sshkey *key = NULL; 256 u_char randchall[32]; 257 const u_char *challenge; 258 size_t challenge_len; 259 struct sk_enroll_response *resp = NULL; 260 int r = SSH_ERR_INTERNAL_ERROR; 261 int alg; 262 263 debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, " 264 "challenge len %zu", __func__, provider_path, application, 265 flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf)); 266 267 *keyp = NULL; 268 if (attest) 269 sshbuf_reset(attest); 270 switch (type) { 271 #ifdef WITH_OPENSSL 272 case KEY_ECDSA_SK: 273 alg = SSH_SK_ECDSA; 274 break; 275 #endif /* WITH_OPENSSL */ 276 case KEY_ED25519_SK: 277 alg = SSH_SK_ED25519; 278 break; 279 default: 280 error("%s: unsupported key type", __func__); 281 r = SSH_ERR_INVALID_ARGUMENT; 282 goto out; 283 } 284 if (provider_path == NULL) { 285 error("%s: missing provider", __func__); 286 r = SSH_ERR_INVALID_ARGUMENT; 287 goto out; 288 } 289 if (application == NULL || *application == '\0') { 290 error("%s: missing application", __func__); 291 r = SSH_ERR_INVALID_ARGUMENT; 292 goto out; 293 } 294 if (challenge_buf == NULL) { 295 debug("%s: using random challenge", __func__); 296 arc4random_buf(randchall, sizeof(randchall)); 297 challenge = randchall; 298 challenge_len = sizeof(randchall); 299 } else if (sshbuf_len(challenge_buf) == 0) { 300 error("Missing enrollment challenge"); 301 r = SSH_ERR_INVALID_ARGUMENT; 302 goto out; 303 } else { 304 challenge = sshbuf_ptr(challenge_buf); 305 challenge_len = sshbuf_len(challenge_buf); 306 debug3("%s: using explicit challenge len=%zd", 307 __func__, challenge_len); 308 } 309 if ((skp = sshsk_open(provider_path)) == NULL) { 310 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 311 goto out; 312 } 313 /* XXX validate flags? */ 314 /* enroll key */ 315 if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, 316 flags, &resp)) != 0) { 317 error("Security key provider %s returned failure %d", 318 provider_path, r); 319 r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */ 320 goto out; 321 } 322 /* Check response validity */ 323 if (resp->public_key == NULL || resp->key_handle == NULL || 324 resp->signature == NULL || 325 (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) { 326 error("%s: sk_enroll response invalid", __func__); 327 r = SSH_ERR_INVALID_FORMAT; 328 goto out; 329 } 330 switch (type) { 331 #ifdef WITH_OPENSSL 332 case KEY_ECDSA_SK: 333 if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) 334 goto out; 335 break; 336 #endif /* WITH_OPENSSL */ 337 case KEY_ED25519_SK: 338 if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) 339 goto out; 340 break; 341 } 342 key->sk_flags = flags; 343 if ((key->sk_key_handle = sshbuf_new()) == NULL || 344 (key->sk_reserved = sshbuf_new()) == NULL) { 345 error("%s: allocation failed", __func__); 346 r = SSH_ERR_ALLOC_FAIL; 347 goto out; 348 } 349 if ((key->sk_application = strdup(application)) == NULL) { 350 error("%s: strdup application failed", __func__); 351 r = SSH_ERR_ALLOC_FAIL; 352 goto out; 353 } 354 if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, 355 resp->key_handle_len)) != 0) { 356 error("%s: buffer error: %s", __func__, ssh_err(r)); 357 goto out; 358 } 359 /* Optionally fill in the attestation information */ 360 if (attest != NULL) { 361 if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 || 362 (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */ 363 (r = sshbuf_put_string(attest, 364 resp->attestation_cert, resp->attestation_cert_len)) != 0 || 365 (r = sshbuf_put_string(attest, 366 resp->signature, resp->signature_len)) != 0 || 367 (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */ 368 (r = sshbuf_put_string(attest, NULL, 0)) != 0) { 369 error("%s: buffer error: %s", __func__, ssh_err(r)); 370 goto out; 371 } 372 } 373 /* success */ 374 *keyp = key; 375 key = NULL; /* transferred */ 376 r = 0; 377 out: 378 sshsk_free(skp); 379 sshkey_free(key); 380 sshsk_free_enroll_response(resp); 381 explicit_bzero(randchall, sizeof(randchall)); 382 return r; 383 } 384 385 #ifdef WITH_OPENSSL 386 static int 387 sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) 388 { 389 struct sshbuf *inner_sig = NULL; 390 int r = SSH_ERR_INTERNAL_ERROR; 391 392 /* Check response validity */ 393 if (resp->sig_r == NULL || resp->sig_s == NULL) { 394 error("%s: sk_sign response invalid", __func__); 395 r = SSH_ERR_INVALID_FORMAT; 396 goto out; 397 } 398 if ((inner_sig = sshbuf_new()) == NULL) { 399 r = SSH_ERR_ALLOC_FAIL; 400 goto out; 401 } 402 /* Prepare and append inner signature object */ 403 if ((r = sshbuf_put_bignum2_bytes(inner_sig, 404 resp->sig_r, resp->sig_r_len)) != 0 || 405 (r = sshbuf_put_bignum2_bytes(inner_sig, 406 resp->sig_s, resp->sig_s_len)) != 0) { 407 debug("%s: buffer error: %s", __func__, ssh_err(r)); 408 goto out; 409 } 410 if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || 411 (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 412 (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 413 debug("%s: buffer error: %s", __func__, ssh_err(r)); 414 goto out; 415 } 416 #ifdef DEBUG_SK 417 fprintf(stderr, "%s: sig_r:\n", __func__); 418 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 419 fprintf(stderr, "%s: sig_s:\n", __func__); 420 sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); 421 fprintf(stderr, "%s: inner:\n", __func__); 422 sshbuf_dump(inner_sig, stderr); 423 #endif 424 r = 0; 425 out: 426 sshbuf_free(inner_sig); 427 return r; 428 } 429 #endif /* WITH_OPENSSL */ 430 431 static int 432 sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) 433 { 434 int r = SSH_ERR_INTERNAL_ERROR; 435 436 /* Check response validity */ 437 if (resp->sig_r == NULL) { 438 error("%s: sk_sign response invalid", __func__); 439 r = SSH_ERR_INVALID_FORMAT; 440 goto out; 441 } 442 if ((r = sshbuf_put_string(sig, 443 resp->sig_r, resp->sig_r_len)) != 0 || 444 (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 445 (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 446 debug("%s: buffer error: %s", __func__, ssh_err(r)); 447 goto out; 448 } 449 #ifdef DEBUG_SK 450 fprintf(stderr, "%s: sig_r:\n", __func__); 451 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 452 #endif 453 r = 0; 454 out: 455 return 0; 456 } 457 458 int 459 sshsk_sign(const char *provider_path, const struct sshkey *key, 460 u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 461 u_int compat) 462 { 463 struct sshsk_provider *skp = NULL; 464 int r = SSH_ERR_INTERNAL_ERROR; 465 int type, alg; 466 struct sk_sign_response *resp = NULL; 467 struct sshbuf *inner_sig = NULL, *sig = NULL; 468 uint8_t message[32]; 469 470 debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__, 471 provider_path, sshkey_type(key), key->sk_flags); 472 473 if (sigp != NULL) 474 *sigp = NULL; 475 if (lenp != NULL) 476 *lenp = 0; 477 type = sshkey_type_plain(key->type); 478 switch (type) { 479 #ifdef WITH_OPENSSL 480 case KEY_ECDSA_SK: 481 alg = SSH_SK_ECDSA; 482 break; 483 #endif /* WITH_OPENSSL */ 484 case KEY_ED25519_SK: 485 alg = SSH_SK_ED25519; 486 break; 487 default: 488 return SSH_ERR_INVALID_ARGUMENT; 489 } 490 if (provider_path == NULL || 491 key->sk_key_handle == NULL || 492 key->sk_application == NULL || *key->sk_application == '\0') { 493 r = SSH_ERR_INVALID_ARGUMENT; 494 goto out; 495 } 496 if ((skp = sshsk_open(provider_path)) == NULL) { 497 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 498 goto out; 499 } 500 501 /* hash data to be signed before it goes to the security key */ 502 if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen, 503 message, sizeof(message))) != 0) { 504 error("%s: hash application failed: %s", __func__, ssh_err(r)); 505 r = SSH_ERR_INTERNAL_ERROR; 506 goto out; 507 } 508 if ((r = skp->sk_sign(alg, message, sizeof(message), 509 key->sk_application, 510 sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), 511 key->sk_flags, &resp)) != 0) { 512 debug("%s: sk_sign failed with code %d", __func__, r); 513 goto out; 514 } 515 /* Assemble signature */ 516 if ((sig = sshbuf_new()) == NULL) { 517 r = SSH_ERR_ALLOC_FAIL; 518 goto out; 519 } 520 if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { 521 debug("%s: buffer error (outer): %s", __func__, ssh_err(r)); 522 goto out; 523 } 524 switch (type) { 525 #ifdef WITH_OPENSSL 526 case KEY_ECDSA_SK: 527 if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) 528 goto out; 529 break; 530 #endif /* WITH_OPENSSL */ 531 case KEY_ED25519_SK: 532 if ((r = sshsk_ed25519_sig(resp, sig)) != 0) 533 goto out; 534 break; 535 } 536 #ifdef DEBUG_SK 537 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 538 __func__, resp->flags, resp->counter); 539 fprintf(stderr, "%s: hashed message:\n", __func__); 540 sshbuf_dump_data(message, sizeof(message), stderr); 541 fprintf(stderr, "%s: sigbuf:\n", __func__); 542 sshbuf_dump(sig, stderr); 543 #endif 544 if (sigp != NULL) { 545 if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { 546 r = SSH_ERR_ALLOC_FAIL; 547 goto out; 548 } 549 memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); 550 } 551 if (lenp != NULL) 552 *lenp = sshbuf_len(sig); 553 /* success */ 554 r = 0; 555 out: 556 explicit_bzero(message, sizeof(message)); 557 sshsk_free(skp); 558 sshsk_free_sign_response(resp); 559 sshbuf_free(sig); 560 sshbuf_free(inner_sig); 561 return r; 562 } 563