1 /* 2 * Copyright (c) 2021 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7 #include <sys/types.h> 8 9 #include <stdlib.h> 10 #include <windows.h> 11 #include <webauthn.h> 12 13 #include "fido.h" 14 15 #define MAXCHARS 128 16 #define MAXCREDS 128 17 #define MAXMSEC 6000 * 1000 18 #define VENDORID 0x045e 19 #define PRODID 0x0001 20 21 struct winhello_assert { 22 WEBAUTHN_CLIENT_DATA cd; 23 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS opt; 24 WEBAUTHN_ASSERTION *assert; 25 wchar_t *rp_id; 26 }; 27 28 struct winhello_cred { 29 WEBAUTHN_RP_ENTITY_INFORMATION rp; 30 WEBAUTHN_USER_ENTITY_INFORMATION user; 31 WEBAUTHN_COSE_CREDENTIAL_PARAMETER alg; 32 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose; 33 WEBAUTHN_CLIENT_DATA cd; 34 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS opt; 35 WEBAUTHN_CREDENTIAL_ATTESTATION *att; 36 wchar_t *rp_id; 37 wchar_t *rp_name; 38 wchar_t *user_name; 39 wchar_t *user_icon; 40 wchar_t *display_name; 41 }; 42 43 static wchar_t * 44 to_utf16(const char *utf8) 45 { 46 int nch; 47 wchar_t *utf16; 48 49 if (utf8 == NULL) { 50 fido_log_debug("%s: NULL", __func__); 51 return NULL; 52 } 53 if ((nch = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) < 1 || 54 (size_t)nch > MAXCHARS) { 55 fido_log_debug("%s: MultiByteToWideChar %d", __func__, nch); 56 return NULL; 57 } 58 if ((utf16 = calloc((size_t)nch, sizeof(*utf16))) == NULL) { 59 fido_log_debug("%s: calloc", __func__); 60 return NULL; 61 } 62 if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, nch) != nch) { 63 fido_log_debug("%s: MultiByteToWideChar", __func__); 64 free(utf16); 65 return NULL; 66 } 67 68 return utf16; 69 } 70 71 static char * 72 to_utf8(const wchar_t *utf16) 73 { 74 int nch; 75 char *utf8; 76 77 if (utf16 == NULL) { 78 fido_log_debug("%s: NULL", __func__); 79 return NULL; 80 } 81 if ((nch = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16, 82 -1, NULL, 0, NULL, NULL)) < 1 || (size_t)nch > MAXCHARS) { 83 fido_log_debug("%s: WideCharToMultiByte %d", __func__); 84 return NULL; 85 } 86 if ((utf8 = calloc((size_t)nch, sizeof(*utf8))) == NULL) { 87 fido_log_debug("%s: calloc", __func__); 88 return NULL; 89 } 90 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16, -1, 91 utf8, nch, NULL, NULL) != nch) { 92 fido_log_debug("%s: WideCharToMultiByte", __func__); 93 free(utf8); 94 return NULL; 95 } 96 97 return utf8; 98 } 99 100 static int 101 to_fido_str_array(fido_str_array_t *sa, const char **v, size_t n) 102 { 103 if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) { 104 fido_log_debug("%s: calloc", __func__); 105 return -1; 106 } 107 for (size_t i = 0; i < n; i++) { 108 if ((sa->ptr[i] = strdup(v[i])) == NULL) { 109 fido_log_debug("%s: strdup", __func__); 110 return -1; 111 } 112 sa->len++; 113 } 114 115 return 0; 116 } 117 118 static int 119 to_fido(HRESULT hr) 120 { 121 switch (hr) { 122 case NTE_NOT_SUPPORTED: 123 return FIDO_ERR_UNSUPPORTED_OPTION; 124 case NTE_INVALID_PARAMETER: 125 return FIDO_ERR_INVALID_PARAMETER; 126 case NTE_TOKEN_KEYSET_STORAGE_FULL: 127 return FIDO_ERR_KEY_STORE_FULL; 128 case NTE_DEVICE_NOT_FOUND: 129 case NTE_NOT_FOUND: 130 return FIDO_ERR_NOT_ALLOWED; 131 default: 132 fido_log_debug("%s: hr=0x%x", __func__, hr); 133 return FIDO_ERR_INTERNAL; 134 } 135 } 136 137 static int 138 pack_cd(WEBAUTHN_CLIENT_DATA *out, const fido_blob_t *in) 139 { 140 if (in->ptr == NULL) { 141 fido_log_debug("%s: NULL", __func__); 142 return -1; 143 } 144 if (in->len > ULONG_MAX) { 145 fido_log_debug("%s: in->len=%zu", __func__, in->len); 146 return -1; 147 } 148 out->dwVersion = WEBAUTHN_CLIENT_DATA_CURRENT_VERSION; 149 out->cbClientDataJSON = (DWORD)in->len; 150 out->pbClientDataJSON = in->ptr; 151 out->pwszHashAlgId = WEBAUTHN_HASH_ALGORITHM_SHA_256; 152 153 return 0; 154 } 155 156 static int 157 pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in) 158 { 159 WEBAUTHN_CREDENTIAL *c; 160 161 if (in->len == 0) { 162 return 0; /* nothing to do */ 163 } 164 if (in->len > MAXCREDS) { 165 fido_log_debug("%s: in->len=%zu", __func__, in->len); 166 return -1; 167 } 168 if ((out->pCredentials = calloc(in->len, sizeof(*c))) == NULL) { 169 fido_log_debug("%s: calloc", __func__); 170 return -1; 171 } 172 out->cCredentials = (DWORD)in->len; 173 for (size_t i = 0; i < in->len; i++) { 174 if (in->ptr[i].len > ULONG_MAX) { 175 fido_log_debug("%s: %zu", __func__, in->ptr[i].len); 176 return -1; 177 } 178 c = &out->pCredentials[i]; 179 c->dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION; 180 c->cbId = (DWORD)in->ptr[i].len; 181 c->pbId = in->ptr[i].ptr; 182 c->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 183 } 184 185 return 0; 186 } 187 188 static int 189 set_uv(DWORD *out, fido_opt_t uv, const char *pin) 190 { 191 if (pin) { 192 *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 193 return 0; 194 } 195 196 switch (uv) { 197 case FIDO_OPT_OMIT: 198 *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; 199 break; 200 case FIDO_OPT_FALSE: 201 *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 202 break; 203 case FIDO_OPT_TRUE: 204 *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 205 break; 206 } 207 208 return 0; 209 } 210 211 static int 212 pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, 213 fido_rp_t *in) 214 { 215 /* keep non-const copies of pwsz* for free() */ 216 out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION; 217 if ((out->pwszId = *id = to_utf16(in->id)) == NULL) { 218 fido_log_debug("%s: id", __func__); 219 return -1; 220 } 221 if (in->name && (out->pwszName = *name = to_utf16(in->name)) == NULL) { 222 fido_log_debug("%s: name", __func__); 223 return -1; 224 } 225 return 0; 226 } 227 228 static int 229 pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name, 230 WEBAUTHN_USER_ENTITY_INFORMATION *out, fido_user_t *in) 231 { 232 if (in->id.ptr == NULL || in->id.len > ULONG_MAX) { 233 fido_log_debug("%s: id", __func__); 234 return -1; 235 } 236 out->dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION; 237 out->cbId = (DWORD)in->id.len; 238 out->pbId = in->id.ptr; 239 /* keep non-const copies of pwsz* for free() */ 240 if (in->name != NULL) { 241 if ((out->pwszName = *name = to_utf16(in->name)) == NULL) { 242 fido_log_debug("%s: name", __func__); 243 return -1; 244 } 245 } 246 if (in->icon != NULL) { 247 if ((out->pwszIcon = *icon = to_utf16(in->icon)) == NULL) { 248 fido_log_debug("%s: icon", __func__); 249 return -1; 250 } 251 } 252 if (in->display_name != NULL) { 253 if ((out->pwszDisplayName = *display_name = 254 to_utf16(in->display_name)) == NULL) { 255 fido_log_debug("%s: display_name", __func__); 256 return -1; 257 } 258 } 259 260 return 0; 261 } 262 263 static int 264 pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, 265 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type) 266 { 267 switch (type) { 268 case COSE_ES256: 269 alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256; 270 break; 271 case COSE_EDDSA: 272 alg->lAlg = -8; /* XXX */; 273 break; 274 case COSE_RS256: 275 alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256; 276 break; 277 default: 278 fido_log_debug("%s: type %d", __func__, type); 279 return -1; 280 } 281 alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION; 282 alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 283 cose->cCredentialParameters = 1; 284 cose->pCredentialParameters = alg; 285 286 return 0; 287 } 288 289 static int 290 pack_cred_ext(WEBAUTHN_EXTENSIONS *out, fido_cred_ext_t *in) 291 { 292 WEBAUTHN_EXTENSION *e; 293 WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p; 294 BOOL *b; 295 size_t n = 0, i = 0; 296 297 if (in->mask == 0) { 298 return 0; /* nothing to do */ 299 } 300 if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) { 301 fido_log_debug("%s: mask 0x%x", in->mask); 302 return -1; 303 } 304 if (in->mask & FIDO_EXT_HMAC_SECRET) 305 n++; 306 if (in->mask & FIDO_EXT_CRED_PROTECT) 307 n++; 308 if ((out->pExtensions = calloc(n, sizeof(*e))) == NULL) { 309 fido_log_debug("%s: calloc", __func__); 310 return -1; 311 } 312 out->cExtensions = (DWORD)n; 313 if (in->mask & FIDO_EXT_HMAC_SECRET) { 314 if ((b = calloc(1, sizeof(*b))) == NULL) { 315 fido_log_debug("%s: calloc", __func__); 316 return -1; 317 } 318 *b = true; 319 e = &out->pExtensions[i]; 320 e->pwszExtensionIdentifier = 321 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET; 322 e->pvExtension = b; 323 e->cbExtension = sizeof(*b); 324 i++; 325 } 326 if (in->mask & FIDO_EXT_CRED_PROTECT) { 327 if ((p = calloc(1, sizeof(*p))) == NULL) { 328 fido_log_debug("%s: calloc", __func__); 329 return -1; 330 } 331 p->dwCredProtect = (DWORD)in->prot; 332 p->bRequireCredProtect = true; 333 e = &out->pExtensions[i]; 334 e->pwszExtensionIdentifier = 335 WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT; 336 e->pvExtension = p; 337 e->cbExtension = sizeof(*p); 338 i++; 339 } 340 341 return 0; 342 } 343 344 static int 345 unpack_fmt(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) 346 { 347 char *fmt; 348 int r; 349 350 if ((fmt = to_utf8(att->pwszFormatType)) == NULL) { 351 fido_log_debug("%s: fmt", __func__); 352 return -1; 353 } 354 r = fido_cred_set_fmt(cred, fmt); 355 free(fmt); 356 fmt = NULL; 357 if (r != FIDO_OK) { 358 fido_log_debug("%s: fido_cred_set_fmt: %s", __func__, 359 fido_strerr(r)); 360 return -1; 361 } 362 363 return 0; 364 } 365 366 static int 367 unpack_cred_authdata(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) 368 { 369 int r; 370 371 if (att->cbAuthenticatorData > SIZE_MAX) { 372 fido_log_debug("%s: cbAuthenticatorData", __func__); 373 return -1; 374 } 375 if ((r = fido_cred_set_authdata_raw(cred, att->pbAuthenticatorData, 376 (size_t)att->cbAuthenticatorData)) != FIDO_OK) { 377 fido_log_debug("%s: fido_cred_set_authdata_raw: %s", __func__, 378 fido_strerr(r)); 379 return -1; 380 } 381 382 return 0; 383 } 384 385 static int 386 unpack_cred_sig(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr) 387 { 388 int r; 389 390 if (attr->cbSignature > SIZE_MAX) { 391 fido_log_debug("%s: cbSignature", __func__); 392 return -1; 393 } 394 if ((r = fido_cred_set_sig(cred, attr->pbSignature, 395 (size_t)attr->cbSignature)) != FIDO_OK) { 396 fido_log_debug("%s: fido_cred_set_sig: %s", __func__, 397 fido_strerr(r)); 398 return -1; 399 } 400 401 return 0; 402 } 403 404 static int 405 unpack_x5c(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr) 406 { 407 int r; 408 409 fido_log_debug("%s: %u cert(s)", __func__, attr->cX5c); 410 411 if (attr->cX5c == 0) 412 return 0; /* self-attestation */ 413 if (attr->lAlg != WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256) { 414 fido_log_debug("%s: lAlg %d", __func__, attr->lAlg); 415 return -1; 416 } 417 if (attr->pX5c[0].cbData > SIZE_MAX) { 418 fido_log_debug("%s: cbData", __func__); 419 return -1; 420 } 421 if ((r = fido_cred_set_x509(cred, attr->pX5c[0].pbData, 422 (size_t)attr->pX5c[0].cbData)) != FIDO_OK) { 423 fido_log_debug("%s: fido_cred_set_x509: %s", __func__, 424 fido_strerr(r)); 425 return -1; 426 } 427 428 return 0; 429 } 430 431 static int 432 unpack_assert_authdata(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) 433 { 434 int r; 435 436 if (wa->cbAuthenticatorData > SIZE_MAX) { 437 fido_log_debug("%s: cbAuthenticatorData", __func__); 438 return -1; 439 } 440 if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, 441 (size_t)wa->cbAuthenticatorData)) != FIDO_OK) { 442 fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__, 443 fido_strerr(r)); 444 return -1; 445 } 446 447 return 0; 448 } 449 450 static int 451 unpack_assert_sig(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) 452 { 453 int r; 454 455 if (wa->cbSignature > SIZE_MAX) { 456 fido_log_debug("%s: cbSignature", __func__); 457 return -1; 458 } 459 if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, 460 (size_t)wa->cbSignature)) != FIDO_OK) { 461 fido_log_debug("%s: fido_assert_set_sig: %s", __func__, 462 fido_strerr(r)); 463 return -1; 464 } 465 466 return 0; 467 } 468 469 static int 470 unpack_cred_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) 471 { 472 if (wa->Credential.cbId > SIZE_MAX) { 473 fido_log_debug("%s: Credential.cbId", __func__); 474 return -1; 475 } 476 if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, 477 (size_t)wa->Credential.cbId) < 0) { 478 fido_log_debug("%s: fido_blob_set", __func__); 479 return -1; 480 } 481 482 return 0; 483 } 484 485 static int 486 unpack_user_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) 487 { 488 if (wa->cbUserId == 0) 489 return 0; /* user id absent */ 490 if (wa->cbUserId > SIZE_MAX) { 491 fido_log_debug("%s: cbUserId", __func__); 492 return -1; 493 } 494 if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, 495 (size_t)wa->cbUserId) < 0) { 496 fido_log_debug("%s: fido_blob_set", __func__); 497 return -1; 498 } 499 500 return 0; 501 } 502 503 static int 504 translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert, 505 const char *pin) 506 { 507 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt; 508 509 /* not supported by webauthn.h */ 510 if (assert->up == FIDO_OPT_FALSE) { 511 fido_log_debug("%s: up %d", __func__, assert->up); 512 return FIDO_ERR_UNSUPPORTED_OPTION; 513 } 514 /* not implemented */ 515 if (assert->ext.mask) { 516 fido_log_debug("%s: ext 0x%x", __func__, assert->ext.mask); 517 return FIDO_ERR_UNSUPPORTED_EXTENSION; 518 } 519 if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) { 520 fido_log_debug("%s: rp_id", __func__); 521 return FIDO_ERR_INTERNAL; 522 } 523 if (pack_cd(&ctx->cd, &assert->cd) < 0) { 524 fido_log_debug("%s: pack_cd", __func__); 525 return FIDO_ERR_INTERNAL; 526 } 527 /* options */ 528 opt = &ctx->opt; 529 opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1; 530 opt->dwTimeoutMilliseconds = MAXMSEC; 531 if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { 532 fido_log_debug("%s: pack_credlist", __func__); 533 return FIDO_ERR_INTERNAL; 534 } 535 if (set_uv(&opt->dwUserVerificationRequirement, assert->uv, pin) < 0) { 536 fido_log_debug("%s: set_uv", __func__); 537 return FIDO_ERR_INTERNAL; 538 } 539 540 return FIDO_OK; 541 } 542 543 static int 544 translate_winhello_assert(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) 545 { 546 int r; 547 548 if (assert->stmt_len > 0) { 549 fido_log_debug("%s: stmt_len=%zu", __func__, assert->stmt_len); 550 return FIDO_ERR_INTERNAL; 551 } 552 if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) { 553 fido_log_debug("%s: fido_assert_set_count: %s", __func__, 554 fido_strerr(r)); 555 return FIDO_ERR_INTERNAL; 556 } 557 if (unpack_assert_authdata(assert, wa) < 0) { 558 fido_log_debug("%s: unpack_assert_authdata", __func__); 559 return FIDO_ERR_INTERNAL; 560 } 561 if (unpack_assert_sig(assert, wa) < 0) { 562 fido_log_debug("%s: unpack_assert_sig", __func__); 563 return FIDO_ERR_INTERNAL; 564 } 565 if (unpack_cred_id(assert, wa) < 0) { 566 fido_log_debug("%s: unpack_cred_id", __func__); 567 return FIDO_ERR_INTERNAL; 568 } 569 if (unpack_user_id(assert, wa) < 0) { 570 fido_log_debug("%s: unpack_user_id", __func__); 571 return FIDO_ERR_INTERNAL; 572 } 573 574 return FIDO_OK; 575 } 576 577 static int 578 translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred, 579 const char *pin) 580 { 581 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt; 582 583 if (pack_rp(&ctx->rp_id, &ctx->rp_name, &ctx->rp, &cred->rp) < 0) { 584 fido_log_debug("%s: pack_rp", __func__); 585 return FIDO_ERR_INTERNAL; 586 } 587 if (pack_user(&ctx->user_name, &ctx->user_icon, &ctx->display_name, 588 &ctx->user, &cred->user) < 0) { 589 fido_log_debug("%s: pack_user", __func__); 590 return FIDO_ERR_INTERNAL; 591 } 592 if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) { 593 fido_log_debug("%s: pack_cose", __func__); 594 return FIDO_ERR_INTERNAL; 595 } 596 if (pack_cd(&ctx->cd, &cred->cd) < 0) { 597 fido_log_debug("%s: pack_cd", __func__); 598 return FIDO_ERR_INTERNAL; 599 } 600 /* options */ 601 opt = &ctx->opt; 602 opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1; 603 opt->dwTimeoutMilliseconds = MAXMSEC; 604 if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) { 605 fido_log_debug("%s: pack_credlist", __func__); 606 return FIDO_ERR_INTERNAL; 607 } 608 if (pack_cred_ext(&opt->Extensions, &cred->ext) < 0) { 609 fido_log_debug("%s: pack_cred_ext", __func__); 610 return FIDO_ERR_UNSUPPORTED_EXTENSION; 611 } 612 if (set_uv(&opt->dwUserVerificationRequirement, cred->uv, pin) < 0) { 613 fido_log_debug("%s: set_uv", __func__); 614 return FIDO_ERR_INTERNAL; 615 } 616 if (cred->rk == FIDO_OPT_TRUE) { 617 opt->bRequireResidentKey = true; 618 } 619 620 return FIDO_OK; 621 } 622 623 static int 624 translate_winhello_cred(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) 625 { 626 if (unpack_fmt(cred, att) < 0) { 627 fido_log_debug("%s: unpack_fmt", __func__); 628 return FIDO_ERR_INTERNAL; 629 } 630 if (unpack_cred_authdata(cred, att) < 0) { 631 fido_log_debug("%s: unpack_cred_authdata", __func__); 632 return FIDO_ERR_INTERNAL; 633 } 634 635 switch (att->dwAttestationDecodeType) { 636 case WEBAUTHN_ATTESTATION_DECODE_NONE: 637 if (att->pvAttestationDecode != NULL) { 638 fido_log_debug("%s: pvAttestationDecode", __func__); 639 return FIDO_ERR_INTERNAL; 640 } 641 break; 642 case WEBAUTHN_ATTESTATION_DECODE_COMMON: 643 if (att->pvAttestationDecode == NULL) { 644 fido_log_debug("%s: pvAttestationDecode", __func__); 645 return FIDO_ERR_INTERNAL; 646 } 647 if (unpack_cred_sig(cred, att->pvAttestationDecode) < 0) { 648 fido_log_debug("%s: unpack_cred_sig", __func__); 649 return FIDO_ERR_INTERNAL; 650 } 651 if (unpack_x5c(cred, att->pvAttestationDecode) < 0) { 652 fido_log_debug("%s: unpack_x5c", __func__); 653 return FIDO_ERR_INTERNAL; 654 } 655 break; 656 default: 657 fido_log_debug("%s: dwAttestationDecodeType: %u", __func__, 658 att->dwAttestationDecodeType); 659 return FIDO_ERR_INTERNAL; 660 } 661 662 return FIDO_OK; 663 } 664 665 static int 666 winhello_manifest(BOOL *present) 667 { 668 DWORD n; 669 HRESULT hr; 670 int r = FIDO_OK; 671 672 if ((n = WebAuthNGetApiVersionNumber()) < 1) { 673 fido_log_debug("%s: unsupported api %u", __func__, n); 674 return FIDO_ERR_INTERNAL; 675 } 676 fido_log_debug("%s: api version %u", __func__, n); 677 hr = WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable(present); 678 if (hr != S_OK) { 679 r = to_fido(hr); 680 fido_log_debug("%s: %ls -> %s", __func__, 681 WebAuthNGetErrorName(hr), fido_strerr(r)); 682 } 683 684 return r; 685 } 686 687 static int 688 winhello_get_assert(HWND w, struct winhello_assert *ctx) 689 { 690 HRESULT hr; 691 int r = FIDO_OK; 692 693 hr = WebAuthNAuthenticatorGetAssertion(w, ctx->rp_id, &ctx->cd, 694 &ctx->opt, &ctx->assert); 695 if (hr != S_OK) { 696 r = to_fido(hr); 697 fido_log_debug("%s: %ls -> %s", __func__, 698 WebAuthNGetErrorName(hr), fido_strerr(r)); 699 } 700 701 return r; 702 } 703 704 static int 705 winhello_make_cred(HWND w, struct winhello_cred *ctx) 706 { 707 HRESULT hr; 708 int r = FIDO_OK; 709 710 hr = WebAuthNAuthenticatorMakeCredential(w, &ctx->rp, &ctx->user, 711 &ctx->cose, &ctx->cd, &ctx->opt, &ctx->att); 712 if (hr != S_OK) { 713 r = to_fido(hr); 714 fido_log_debug("%s: %ls -> %s", __func__, 715 WebAuthNGetErrorName(hr), fido_strerr(r)); 716 } 717 718 return r; 719 } 720 721 static void 722 winhello_assert_free(struct winhello_assert *ctx) 723 { 724 if (ctx == NULL) 725 return; 726 if (ctx->assert != NULL) 727 WebAuthNFreeAssertion(ctx->assert); 728 729 free(ctx->rp_id); 730 free(ctx->opt.CredentialList.pCredentials); 731 free(ctx); 732 } 733 734 static void 735 winhello_cred_free(struct winhello_cred *ctx) 736 { 737 if (ctx == NULL) 738 return; 739 if (ctx->att != NULL) 740 WebAuthNFreeCredentialAttestation(ctx->att); 741 742 free(ctx->rp_id); 743 free(ctx->rp_name); 744 free(ctx->user_name); 745 free(ctx->user_icon); 746 free(ctx->display_name); 747 free(ctx->opt.CredentialList.pCredentials); 748 for (size_t i = 0; i < ctx->opt.Extensions.cExtensions; i++) { 749 WEBAUTHN_EXTENSION *e; 750 e = &ctx->opt.Extensions.pExtensions[i]; 751 free(e->pvExtension); 752 } 753 free(ctx->opt.Extensions.pExtensions); 754 free(ctx); 755 } 756 757 int 758 fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 759 { 760 int r; 761 BOOL present; 762 fido_dev_info_t *di; 763 764 if (ilen == 0) { 765 return FIDO_OK; 766 } 767 if (devlist == NULL) { 768 return FIDO_ERR_INVALID_ARGUMENT; 769 } 770 if ((r = winhello_manifest(&present)) != FIDO_OK) { 771 fido_log_debug("%s: winhello_manifest", __func__); 772 return r; 773 } 774 if (present == false) { 775 fido_log_debug("%s: not present", __func__); 776 return FIDO_OK; 777 } 778 779 di = &devlist[*olen]; 780 memset(di, 0, sizeof(*di)); 781 di->path = strdup(FIDO_WINHELLO_PATH); 782 di->manufacturer = strdup("Microsoft Corporation"); 783 di->product = strdup("Windows Hello"); 784 di->vendor_id = VENDORID; 785 di->product_id = PRODID; 786 if (di->path == NULL || di->manufacturer == NULL || 787 di->product == NULL) { 788 free(di->path); 789 free(di->manufacturer); 790 free(di->product); 791 explicit_bzero(di, sizeof(*di)); 792 return FIDO_ERR_INTERNAL; 793 } 794 ++(*olen); 795 796 return FIDO_OK; 797 } 798 799 int 800 fido_winhello_open(fido_dev_t *dev) 801 { 802 if (dev->flags != 0) 803 return FIDO_ERR_INVALID_ARGUMENT; 804 805 dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK; 806 dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET; 807 808 return FIDO_OK; 809 } 810 811 int 812 fido_winhello_close(fido_dev_t *dev) 813 { 814 memset(dev, 0, sizeof(*dev)); 815 816 return FIDO_OK; 817 } 818 819 int 820 fido_winhello_cancel(fido_dev_t *dev) 821 { 822 (void)dev; 823 824 return FIDO_ERR_INTERNAL; 825 } 826 827 int 828 fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, 829 const char *pin) 830 { 831 HWND w; 832 struct winhello_assert *ctx; 833 int r = FIDO_ERR_INTERNAL; 834 835 (void)dev; 836 837 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 838 fido_log_debug("%s: calloc", __func__); 839 goto fail; 840 } 841 if ((w = GetForegroundWindow()) == NULL) { 842 fido_log_debug("%s: GetForegroundWindow", __func__); 843 goto fail; 844 } 845 if ((r = translate_fido_assert(ctx, assert, pin)) != FIDO_OK) { 846 fido_log_debug("%s: translate_fido_assert", __func__); 847 goto fail; 848 } 849 if ((r = winhello_get_assert(w, ctx)) != S_OK) { 850 fido_log_debug("%s: winhello_get_assert", __func__); 851 goto fail; 852 } 853 if ((r = translate_winhello_assert(assert, ctx->assert)) != FIDO_OK) { 854 fido_log_debug("%s: translate_winhello_assert", __func__); 855 goto fail; 856 } 857 858 fail: 859 winhello_assert_free(ctx); 860 861 return r; 862 } 863 864 int 865 fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 866 { 867 const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" }; 868 const char *e[2] = { "credProtect", "hmac-secret" }; 869 const char *t[2] = { "nfc", "usb" }; 870 const char *o[4] = { "rk", "up", "plat", "clientPin" }; 871 872 (void)dev; 873 874 fido_cbor_info_reset(ci); 875 876 if (to_fido_str_array(&ci->versions, v, nitems(v)) < 0 || 877 to_fido_str_array(&ci->extensions, e, nitems(e)) < 0 || 878 to_fido_str_array(&ci->transports, t, nitems(t)) < 0) { 879 fido_log_debug("%s: to_fido_str_array", __func__); 880 return FIDO_ERR_INTERNAL; 881 } 882 if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL || 883 (ci->options.value = calloc(nitems(o), sizeof(bool))) == NULL) { 884 fido_log_debug("%s: calloc", __func__); 885 return FIDO_ERR_INTERNAL; 886 } 887 for (size_t i = 0; i < nitems(o); i++) { 888 if ((ci->options.name[i] = strdup(o[i])) == NULL) { 889 fido_log_debug("%s: strdup", __func__); 890 return FIDO_ERR_INTERNAL; 891 } 892 ci->options.value[i] = true; 893 ci->options.len++; 894 } 895 896 return FIDO_OK; 897 } 898 899 int 900 fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) 901 { 902 HWND w; 903 struct winhello_cred *ctx; 904 int r = FIDO_ERR_INTERNAL; 905 906 (void)dev; 907 908 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 909 fido_log_debug("%s: calloc", __func__); 910 goto fail; 911 } 912 if ((w = GetForegroundWindow()) == NULL) { 913 fido_log_debug("%s: GetForegroundWindow", __func__); 914 goto fail; 915 } 916 if ((r = translate_fido_cred(ctx, cred, pin)) != FIDO_OK) { 917 fido_log_debug("%s: translate_fido_cred", __func__); 918 goto fail; 919 } 920 if ((r = winhello_make_cred(w, ctx)) != FIDO_OK) { 921 fido_log_debug("%s: winhello_make_cred", __func__); 922 goto fail; 923 } 924 if ((r = translate_winhello_cred(cred, ctx->att)) != FIDO_OK) { 925 fido_log_debug("%s: translate_winhello_cred", __func__); 926 goto fail; 927 } 928 929 r = FIDO_OK; 930 fail: 931 winhello_cred_free(ctx); 932 933 return r; 934 } 935