WinWebAuthnService.cpp (50113B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WinWebAuthnService.h" 8 9 #include "WebAuthnAutoFillEntry.h" 10 #include "WebAuthnEnumStrings.h" 11 #include "WebAuthnResult.h" 12 #include "WebAuthnTransportIdentifiers.h" 13 #include "mozilla/Assertions.h" 14 #include "mozilla/MozPromise.h" 15 #include "mozilla/Preferences.h" 16 #include "mozilla/ScopeExit.h" 17 #include "mozilla/StaticMutex.h" 18 #include "mozilla/dom/PWebAuthnTransactionParent.h" 19 #include "mozilla/ipc/BackgroundParent.h" 20 #include "nsTextFormatter.h" 21 #include "nsWindowsHelpers.h" 22 #include "winwebauthn/webauthn.h" 23 24 namespace mozilla::dom { 25 26 namespace { 27 StaticRWLock gWinWebAuthnModuleLock; 28 29 static bool gWinWebAuthnModuleUnusable = false; 30 static HMODULE gWinWebAuthnModule = 0; 31 32 static const LPCWSTR gWebAuthnHintStrings[3] = { 33 WEBAUTHN_CREDENTIAL_HINT_SECURITY_KEY, 34 WEBAUTHN_CREDENTIAL_HINT_CLIENT_DEVICE, WEBAUTHN_CREDENTIAL_HINT_HYBRID}; 35 36 static decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)* 37 gWinWebauthnIsUVPAA = nullptr; 38 static decltype(WebAuthNAuthenticatorMakeCredential)* 39 gWinWebauthnMakeCredential = nullptr; 40 static decltype(WebAuthNFreeCredentialAttestation)* 41 gWinWebauthnFreeCredentialAttestation = nullptr; 42 static decltype(WebAuthNAuthenticatorGetAssertion)* gWinWebauthnGetAssertion = 43 nullptr; 44 static decltype(WebAuthNFreeAssertion)* gWinWebauthnFreeAssertion = nullptr; 45 static decltype(WebAuthNGetCancellationId)* gWinWebauthnGetCancellationId = 46 nullptr; 47 static decltype(WebAuthNCancelCurrentOperation)* 48 gWinWebauthnCancelCurrentOperation = nullptr; 49 static decltype(WebAuthNGetErrorName)* gWinWebauthnGetErrorName = nullptr; 50 static decltype(WebAuthNGetApiVersionNumber)* gWinWebauthnGetApiVersionNumber = 51 nullptr; 52 static decltype(WebAuthNGetPlatformCredentialList)* 53 gWinWebauthnGetPlatformCredentialList = nullptr; 54 static decltype(WebAuthNFreePlatformCredentialList)* 55 gWinWebauthnFreePlatformCredentialList = nullptr; 56 } // namespace 57 58 /*********************************************************************** 59 * WinWebAuthnService Implementation 60 **********************************************************************/ 61 62 constexpr uint32_t kMinWinWebAuthNApiVersion = WEBAUTHN_API_VERSION_1; 63 64 NS_IMPL_ISUPPORTS(WinWebAuthnService, nsIWebAuthnService) 65 66 /* static */ 67 nsresult WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() { 68 { 69 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 70 if (gWinWebAuthnModule) { 71 // The module is already loaded. 72 return NS_OK; 73 } 74 if (gWinWebAuthnModuleUnusable) { 75 // A previous attempt to load the module failed. 76 return NS_ERROR_NOT_AVAILABLE; 77 } 78 } 79 80 StaticAutoWriteLock lock(gWinWebAuthnModuleLock); 81 if (gWinWebAuthnModule) { 82 // Another thread successfully loaded the module while we were waiting. 83 return NS_OK; 84 } 85 if (gWinWebAuthnModuleUnusable) { 86 // Another thread failed to load the module while we were waiting. 87 return NS_ERROR_NOT_AVAILABLE; 88 } 89 90 gWinWebAuthnModule = LoadLibrarySystem32(L"webauthn.dll"); 91 auto markModuleUnusable = MakeScopeExit([]() { 92 if (gWinWebAuthnModule) { 93 FreeLibrary(gWinWebAuthnModule); 94 gWinWebAuthnModule = 0; 95 } 96 gWinWebAuthnModuleUnusable = true; 97 }); 98 99 if (!gWinWebAuthnModule) { 100 return NS_ERROR_NOT_AVAILABLE; 101 } 102 103 gWinWebauthnIsUVPAA = reinterpret_cast< 104 decltype(WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)*>( 105 GetProcAddress(gWinWebAuthnModule, 106 "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable")); 107 gWinWebauthnMakeCredential = 108 reinterpret_cast<decltype(WebAuthNAuthenticatorMakeCredential)*>( 109 GetProcAddress(gWinWebAuthnModule, 110 "WebAuthNAuthenticatorMakeCredential")); 111 gWinWebauthnFreeCredentialAttestation = 112 reinterpret_cast<decltype(WebAuthNFreeCredentialAttestation)*>( 113 GetProcAddress(gWinWebAuthnModule, 114 "WebAuthNFreeCredentialAttestation")); 115 gWinWebauthnGetAssertion = 116 reinterpret_cast<decltype(WebAuthNAuthenticatorGetAssertion)*>( 117 GetProcAddress(gWinWebAuthnModule, 118 "WebAuthNAuthenticatorGetAssertion")); 119 gWinWebauthnFreeAssertion = 120 reinterpret_cast<decltype(WebAuthNFreeAssertion)*>( 121 GetProcAddress(gWinWebAuthnModule, "WebAuthNFreeAssertion")); 122 gWinWebauthnGetCancellationId = 123 reinterpret_cast<decltype(WebAuthNGetCancellationId)*>( 124 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetCancellationId")); 125 gWinWebauthnCancelCurrentOperation = 126 reinterpret_cast<decltype(WebAuthNCancelCurrentOperation)*>( 127 GetProcAddress(gWinWebAuthnModule, "WebAuthNCancelCurrentOperation")); 128 gWinWebauthnGetErrorName = reinterpret_cast<decltype(WebAuthNGetErrorName)*>( 129 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetErrorName")); 130 gWinWebauthnGetApiVersionNumber = 131 reinterpret_cast<decltype(WebAuthNGetApiVersionNumber)*>( 132 GetProcAddress(gWinWebAuthnModule, "WebAuthNGetApiVersionNumber")); 133 134 if (!(gWinWebauthnIsUVPAA && gWinWebauthnMakeCredential && 135 gWinWebauthnFreeCredentialAttestation && gWinWebauthnGetAssertion && 136 gWinWebauthnFreeAssertion && gWinWebauthnGetCancellationId && 137 gWinWebauthnCancelCurrentOperation && gWinWebauthnGetErrorName && 138 gWinWebauthnGetApiVersionNumber)) { 139 return NS_ERROR_NOT_AVAILABLE; 140 } 141 142 DWORD version = gWinWebauthnGetApiVersionNumber(); 143 144 if (version >= WEBAUTHN_API_VERSION_4) { 145 gWinWebauthnGetPlatformCredentialList = 146 reinterpret_cast<decltype(WebAuthNGetPlatformCredentialList)*>( 147 GetProcAddress(gWinWebAuthnModule, 148 "WebAuthNGetPlatformCredentialList")); 149 gWinWebauthnFreePlatformCredentialList = 150 reinterpret_cast<decltype(WebAuthNFreePlatformCredentialList)*>( 151 GetProcAddress(gWinWebAuthnModule, 152 "WebAuthNFreePlatformCredentialList")); 153 if (!(gWinWebauthnGetPlatformCredentialList && 154 gWinWebauthnFreePlatformCredentialList)) { 155 return NS_ERROR_NOT_AVAILABLE; 156 } 157 } 158 159 // Bug 1869584: In some of our tests, a content process can end up here due to 160 // a call to WinWebAuthnService::AreWebAuthNApisAvailable. This causes us to 161 // fail an assertion in Preferences::SetBool, which is parent-process only. 162 if (XRE_IsParentProcess()) { 163 NS_DispatchToMainThread(NS_NewRunnableFunction(__func__, [version]() { 164 Preferences::SetBool("security.webauthn.show_ms_settings_link", 165 version >= WEBAUTHN_API_VERSION_7); 166 })); 167 } 168 169 markModuleUnusable.release(); 170 return NS_OK; 171 } 172 173 WinWebAuthnService::~WinWebAuthnService() { 174 StaticAutoWriteLock lock(gWinWebAuthnModuleLock); 175 if (gWinWebAuthnModule) { 176 FreeLibrary(gWinWebAuthnModule); 177 } 178 gWinWebAuthnModule = 0; 179 } 180 181 // static 182 void PrunePublicKeyCredentialHints(const nsTArray<nsString>& aInHints, 183 /* out */ nsTArray<LPCWSTR>& aOutHints) { 184 for (const nsString& inputHint : aInHints) { 185 for (const LPCWSTR knownHint : gWebAuthnHintStrings) { 186 if (inputHint.Equals(knownHint)) { 187 aOutHints.AppendElement(knownHint); 188 } 189 } 190 } 191 } 192 193 // static 194 bool WinWebAuthnService::AreWebAuthNApisAvailable() { 195 nsresult rv = EnsureWinWebAuthnModuleLoaded(); 196 NS_ENSURE_SUCCESS(rv, false); 197 198 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 199 return gWinWebAuthnModule && 200 gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion; 201 } 202 203 NS_IMETHODIMP 204 WinWebAuthnService::GetIsUVPAA(bool* aAvailable) { 205 nsresult rv = EnsureWinWebAuthnModuleLoaded(); 206 NS_ENSURE_SUCCESS(rv, rv); 207 208 if (WinWebAuthnService::AreWebAuthNApisAvailable()) { 209 BOOL isUVPAA = FALSE; 210 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 211 *aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK && 212 isUVPAA == TRUE; 213 } else { 214 *aAvailable = false; 215 } 216 return NS_OK; 217 } 218 219 NS_IMETHODIMP 220 WinWebAuthnService::Cancel(uint64_t aTransactionId) { 221 return NS_ERROR_NOT_IMPLEMENTED; 222 } 223 224 NS_IMETHODIMP 225 WinWebAuthnService::Reset() { 226 // Reset will never be the first function to use gWinWebAuthnModule, so 227 // we shouldn't try to initialize it here. 228 auto guard = mTransactionState.Lock(); 229 if (guard->isSome()) { 230 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 231 if (gWinWebAuthnModule) { 232 const GUID cancellationId = guard->ref().cancellationId; 233 gWinWebauthnCancelCurrentOperation(&cancellationId); 234 } 235 if (guard->ref().pendingSignPromise.isSome()) { 236 // This request was never dispatched to the platform API, so 237 // we need to reject the promise ourselves. 238 guard->ref().pendingSignPromise.ref()->Reject( 239 NS_ERROR_DOM_NOT_ALLOWED_ERR); 240 } 241 guard->reset(); 242 } 243 244 return NS_OK; 245 } 246 247 NS_IMETHODIMP 248 WinWebAuthnService::MakeCredential(uint64_t aTransactionId, 249 uint64_t aBrowsingContextId, 250 nsIWebAuthnRegisterArgs* aArgs, 251 nsIWebAuthnRegisterPromise* aPromise) { 252 nsresult rv = EnsureWinWebAuthnModuleLoaded(); 253 NS_ENSURE_SUCCESS(rv, rv); 254 255 Reset(); 256 auto guard = mTransactionState.Lock(); 257 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 258 GUID cancellationId; 259 if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) { 260 // caller will reject promise 261 return NS_ERROR_DOM_UNKNOWN_ERR; 262 } 263 *guard = Some(TransactionState{ 264 aTransactionId, 265 aBrowsingContextId, 266 Nothing(), 267 Nothing(), 268 cancellationId, 269 }); 270 271 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction( 272 "WinWebAuthnService::MakeCredential", 273 [self = RefPtr{this}, aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise}, 274 cancellationId]() mutable { 275 // Take a read lock on gWinWebAuthnModuleLock to prevent the module from 276 // being unloaded while the operation is in progress. This does not 277 // prevent the operation from being cancelled, so it does not block a 278 // clean shutdown. 279 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 280 if (!gWinWebAuthnModule) { 281 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR); 282 return; 283 } 284 285 // RP Information 286 nsString rpId; 287 (void)aArgs->GetRpId(rpId); 288 WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = { 289 WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, rpId.get(), nullptr, 290 nullptr}; 291 292 // User Information 293 WEBAUTHN_USER_ENTITY_INFORMATION userInfo = { 294 WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, 295 0, 296 nullptr, 297 nullptr, 298 nullptr, 299 nullptr}; 300 301 // Client Data 302 nsCString clientDataJSON; 303 (void)aArgs->GetClientDataJSON(clientDataJSON); 304 WEBAUTHN_CLIENT_DATA WebAuthNClientData = { 305 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, 306 (DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()), 307 WEBAUTHN_HASH_ALGORITHM_SHA_256}; 308 309 // User Verification Requirement 310 DWORD winUserVerificationReq = 311 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; 312 313 // Resident Key Requirement. 314 BOOL winRequireResidentKey = FALSE; // Will be set to TRUE if and only 315 // if residentKey = "required" 316 BOOL winPreferResidentKey = FALSE; // Will be set to TRUE if and only 317 // if residentKey = "preferred" 318 319 // AttestationConveyance 320 DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; 321 322 // Large Blob 323 DWORD largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_NONE; 324 bool largeBlobSupportRequired; 325 nsresult rv = 326 aArgs->GetLargeBlobSupportRequired(&largeBlobSupportRequired); 327 if (rv != NS_ERROR_NOT_AVAILABLE) { 328 if (NS_FAILED(rv)) { 329 aPromise->Reject(rv); 330 return; 331 } 332 if (largeBlobSupportRequired) { 333 largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_REQUIRED; 334 } else { 335 largeBlobSupport = WEBAUTHN_LARGE_BLOB_SUPPORT_PREFERRED; 336 } 337 } 338 339 // Prf 340 BOOL winEnablePrf = FALSE; 341 342 nsString rpName; 343 (void)aArgs->GetRpName(rpName); 344 rpInfo.pwszName = rpName.get(); 345 rpInfo.pwszIcon = nullptr; 346 347 nsTArray<uint8_t> userId; 348 (void)aArgs->GetUserId(userId); 349 userInfo.cbId = static_cast<DWORD>(userId.Length()); 350 userInfo.pbId = const_cast<unsigned char*>(userId.Elements()); 351 352 nsString userName; 353 (void)aArgs->GetUserName(userName); 354 userInfo.pwszName = userName.get(); 355 356 userInfo.pwszIcon = nullptr; 357 358 nsString userDisplayName; 359 (void)aArgs->GetUserDisplayName(userDisplayName); 360 userInfo.pwszDisplayName = userDisplayName.get(); 361 362 // Algorithms 363 nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams; 364 nsTArray<int32_t> coseAlgs; 365 (void)aArgs->GetCoseAlgs(coseAlgs); 366 for (const int32_t& coseAlg : coseAlgs) { 367 WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = { 368 WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, 369 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg}; 370 coseParams.AppendElement(coseAlgorithm); 371 } 372 373 nsString userVerificationReq; 374 (void)aArgs->GetUserVerification(userVerificationReq); 375 // This mapping needs to be reviewed if values are added to the 376 // UserVerificationRequirement enum. 377 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 378 if (userVerificationReq.EqualsLiteral( 379 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { 380 winUserVerificationReq = 381 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 382 } else if (userVerificationReq.EqualsLiteral( 383 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { 384 winUserVerificationReq = 385 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; 386 } else if (userVerificationReq.EqualsLiteral( 387 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { 388 winUserVerificationReq = 389 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 390 } else { 391 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; 392 } 393 394 // Attachment 395 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; 396 nsString authenticatorAttachment; 397 rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment); 398 if (rv != NS_ERROR_NOT_AVAILABLE) { 399 if (NS_FAILED(rv)) { 400 aPromise->Reject(rv); 401 return; 402 } 403 // This mapping needs to be reviewed if values are added to the 404 // AuthenticatorAttachement enum. 405 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 406 if (authenticatorAttachment.EqualsLiteral( 407 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) { 408 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM; 409 } else if ( 410 authenticatorAttachment.EqualsLiteral( 411 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) { 412 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; 413 } else { 414 winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; 415 } 416 } 417 418 nsString residentKey; 419 (void)aArgs->GetResidentKey(residentKey); 420 // This mapping needs to be reviewed if values are added to the 421 // ResidentKeyRequirement enum. 422 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 423 if (residentKey.EqualsLiteral( 424 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) { 425 winRequireResidentKey = TRUE; 426 winPreferResidentKey = FALSE; 427 } else if (residentKey.EqualsLiteral( 428 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED)) { 429 winRequireResidentKey = FALSE; 430 winPreferResidentKey = TRUE; 431 } else if (residentKey.EqualsLiteral( 432 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { 433 winRequireResidentKey = FALSE; 434 winPreferResidentKey = FALSE; 435 } else { 436 // WebAuthnHandler::MakeCredential is supposed to assign one of the 437 // above values, so this shouldn't happen. 438 MOZ_ASSERT_UNREACHABLE(); 439 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR); 440 return; 441 } 442 443 // AttestationConveyance 444 nsString attestation; 445 (void)aArgs->GetAttestationConveyancePreference(attestation); 446 // This mapping needs to be reviewed if values are added to the 447 // AttestationConveyancePreference enum. 448 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 449 if (attestation.EqualsLiteral( 450 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE)) { 451 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE; 452 } else if ( 453 attestation.EqualsLiteral( 454 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT)) { 455 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT; 456 } else if (attestation.EqualsLiteral( 457 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT)) { 458 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; 459 } else { 460 winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; 461 } 462 463 // Extensions that might require an entry in the extensions array: 464 // credProtect, hmac-secret, minPinLength. 465 nsTArray<WEBAUTHN_EXTENSION> rgExtension(3); 466 WEBAUTHN_CRED_PROTECT_EXTENSION_IN winCredProtect = { 467 .dwCredProtect = WEBAUTHN_USER_VERIFICATION_ANY, 468 .bRequireCredProtect = FALSE, 469 }; 470 BOOL winHmacCreateSecret = FALSE; 471 BOOL winMinPinLength = FALSE; 472 473 nsCString credProtectPolicy; 474 if (NS_SUCCEEDED( 475 aArgs->GetCredentialProtectionPolicy(credProtectPolicy))) { 476 Maybe<CredentialProtectionPolicy> policy( 477 StringToEnum<CredentialProtectionPolicy>(credProtectPolicy)); 478 if (policy.isNothing()) { 479 aPromise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 480 return; 481 } 482 switch (policy.ref()) { 483 case CredentialProtectionPolicy::UserVerificationOptional: 484 winCredProtect.dwCredProtect = 485 WEBAUTHN_USER_VERIFICATION_OPTIONAL; 486 break; 487 case CredentialProtectionPolicy:: 488 UserVerificationOptionalWithCredentialIDList: 489 winCredProtect.dwCredProtect = 490 WEBAUTHN_USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST; 491 break; 492 case CredentialProtectionPolicy::UserVerificationRequired: 493 winCredProtect.dwCredProtect = 494 WEBAUTHN_USER_VERIFICATION_REQUIRED; 495 break; 496 } 497 498 bool enforceCredProtectPolicy; 499 if (NS_SUCCEEDED(aArgs->GetEnforceCredentialProtectionPolicy( 500 &enforceCredProtectPolicy)) && 501 enforceCredProtectPolicy) { 502 winCredProtect.bRequireCredProtect = TRUE; 503 } 504 505 rgExtension.AppendElement(WEBAUTHN_EXTENSION{ 506 .pwszExtensionIdentifier = 507 WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT, 508 .cbExtension = sizeof(WEBAUTHN_CRED_PROTECT_EXTENSION_IN), 509 .pvExtension = &winCredProtect, 510 }); 511 } 512 513 bool requestedPrf; 514 bool requestedHmacCreateSecret; 515 if (NS_SUCCEEDED(aArgs->GetPrf(&requestedPrf)) && 516 NS_SUCCEEDED( 517 aArgs->GetHmacCreateSecret(&requestedHmacCreateSecret)) && 518 (requestedPrf || requestedHmacCreateSecret)) { 519 winEnablePrf = requestedPrf ? TRUE : FALSE; 520 winHmacCreateSecret = TRUE; 521 rgExtension.AppendElement(WEBAUTHN_EXTENSION{ 522 .pwszExtensionIdentifier = 523 WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET, 524 .cbExtension = sizeof(BOOL), 525 .pvExtension = &winHmacCreateSecret, 526 }); 527 } 528 529 nsTArray<uint8_t> prfEvalFirst; 530 nsTArray<uint8_t> prfEvalSecond; 531 WEBAUTHN_HMAC_SECRET_SALT prfGlobalEval = {0}; 532 PWEBAUTHN_HMAC_SECRET_SALT pPrfGlobalEval = NULL; 533 if (requestedPrf) { 534 pPrfGlobalEval = &prfGlobalEval; 535 rv = aArgs->GetPrfEvalFirst(prfEvalFirst); 536 if (rv == NS_OK) { 537 prfGlobalEval.cbFirst = prfEvalFirst.Length(); 538 prfGlobalEval.pbFirst = prfEvalFirst.Elements(); 539 } 540 rv = aArgs->GetPrfEvalSecond(prfEvalSecond); 541 if (rv == NS_OK) { 542 prfGlobalEval.cbSecond = prfEvalSecond.Length(); 543 prfGlobalEval.pbSecond = prfEvalSecond.Elements(); 544 } 545 } 546 547 bool requestedMinPinLength; 548 if (NS_SUCCEEDED(aArgs->GetMinPinLength(&requestedMinPinLength)) && 549 requestedMinPinLength) { 550 winMinPinLength = TRUE; 551 rgExtension.AppendElement(WEBAUTHN_EXTENSION{ 552 .pwszExtensionIdentifier = 553 WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH, 554 .cbExtension = sizeof(BOOL), 555 .pvExtension = &winMinPinLength, 556 }); 557 } 558 559 WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = { 560 static_cast<DWORD>(coseParams.Length()), coseParams.Elements()}; 561 562 // Exclude Credentials 563 nsTArray<nsTArray<uint8_t>> excludeList; 564 (void)aArgs->GetExcludeList(excludeList); 565 566 nsTArray<uint8_t> excludeListTransports; 567 (void)aArgs->GetExcludeListTransports(excludeListTransports); 568 569 if (excludeList.Length() != excludeListTransports.Length()) { 570 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR); 571 return; 572 } 573 574 nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials; 575 WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr; 576 nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs; 577 WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0}; 578 WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr; 579 580 for (size_t i = 0; i < excludeList.Length(); i++) { 581 nsTArray<uint8_t>& cred = excludeList[i]; 582 uint8_t& transports = excludeListTransports[i]; 583 DWORD winTransports = 0; 584 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) { 585 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB; 586 } 587 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) { 588 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC; 589 } 590 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) { 591 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE; 592 } 593 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) { 594 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; 595 } 596 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) { 597 winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID; 598 } 599 600 WEBAUTHN_CREDENTIAL_EX credential = { 601 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, 602 static_cast<DWORD>(cred.Length()), (PBYTE)(cred.Elements()), 603 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports}; 604 excludeCredentials.AppendElement(credential); 605 } 606 607 if (!excludeCredentials.IsEmpty()) { 608 pExcludeCredentials = excludeCredentials.Elements(); 609 for (DWORD i = 0; i < excludeCredentials.Length(); i++) { 610 excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]); 611 } 612 excludeCredentialList.cCredentials = excludeCredentials.Length(); 613 excludeCredentialList.ppCredentials = 614 excludeCredentialsPtrs.Elements(); 615 pExcludeCredentialList = &excludeCredentialList; 616 } 617 618 uint32_t timeout_u32; 619 (void)aArgs->GetTimeoutMS(&timeout_u32); 620 DWORD timeout = timeout_u32; 621 622 bool privateBrowsing; 623 (void)aArgs->GetPrivateBrowsing(&privateBrowsing); 624 BOOL winPrivateBrowsing = FALSE; 625 if (privateBrowsing) { 626 winPrivateBrowsing = TRUE; 627 } 628 629 nsTArray<nsString> inputHints; 630 (void)aArgs->GetHints(inputHints); 631 632 nsTArray<LPCWSTR> hints; 633 PrunePublicKeyCredentialHints(inputHints, hints); 634 635 // MakeCredentialOptions 636 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS 637 WebAuthNCredentialOptions = { 638 WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_8, 639 timeout, 640 {0, NULL}, 641 {0, NULL}, 642 winAttachment, 643 winRequireResidentKey, 644 winUserVerificationReq, 645 winAttestation, 646 0, // Flags 647 &cancellationId, // CancellationId 648 pExcludeCredentialList, 649 WEBAUTHN_ENTERPRISE_ATTESTATION_NONE, 650 largeBlobSupport, // LargeBlobSupport 651 winPreferResidentKey, // PreferResidentKey 652 winPrivateBrowsing, // BrowserInPrivateMode 653 winEnablePrf, // EnablePrf 654 NULL, // LinkedDevice 655 0, // size of JsonExt 656 NULL, // JsonExt 657 pPrfGlobalEval, // PRFGlobalEval 658 (DWORD)hints.Length(), // Size of CredentialHints 659 hints.Elements(), // CredentialHints 660 }; 661 662 if (rgExtension.Length() != 0) { 663 WebAuthNCredentialOptions.Extensions.cExtensions = 664 rgExtension.Length(); 665 WebAuthNCredentialOptions.Extensions.pExtensions = 666 rgExtension.Elements(); 667 } 668 669 PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation = 670 nullptr; 671 672 // Bug 1518876: Get Window Handle from Content process for Windows 673 // WebAuthN APIs 674 HWND hWnd = GetForegroundWindow(); 675 676 HRESULT hr = gWinWebauthnMakeCredential( 677 hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters, 678 &WebAuthNClientData, &WebAuthNCredentialOptions, 679 &pWebAuthNCredentialAttestation); 680 681 if (hr == S_OK) { 682 RefPtr<WebAuthnRegisterResult> result = new WebAuthnRegisterResult( 683 clientDataJSON, pWebAuthNCredentialAttestation); 684 685 // WEBAUTHN_CREDENTIAL_ATTESTATION structs of version >= 4 always 686 // include a flag to indicate whether a resident key was created. We 687 // copy that flag to the credProps extension output only if the RP 688 // requested the credProps extension. 689 bool requestedCredProps; 690 (void)aArgs->GetCredProps(&requestedCredProps); 691 if (requestedCredProps && 692 pWebAuthNCredentialAttestation->dwVersion >= 693 WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) { 694 BOOL rk = pWebAuthNCredentialAttestation->bResidentKey; 695 (void)result->SetCredPropsRk(rk == TRUE); 696 } 697 gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation); 698 699 aPromise->Resolve(result); 700 } else { 701 PCWSTR errorName = gWinWebauthnGetErrorName(hr); 702 nsresult aError = NS_ERROR_DOM_ABORT_ERR; 703 704 if (_wcsicmp(errorName, L"InvalidStateError") == 0) { 705 aError = NS_ERROR_DOM_INVALID_STATE_ERR; 706 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 || 707 _wcsicmp(errorName, L"UnknownError") == 0) { 708 aError = NS_ERROR_DOM_UNKNOWN_ERR; 709 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) { 710 aError = NS_ERROR_DOM_INVALID_STATE_ERR; 711 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) { 712 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR; 713 } 714 715 aPromise->Reject(aError); 716 } 717 })); 718 719 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK); 720 return NS_OK; 721 } 722 723 NS_IMETHODIMP 724 WinWebAuthnService::GetAssertion(uint64_t aTransactionId, 725 uint64_t aBrowsingContextId, 726 nsIWebAuthnSignArgs* aArgs, 727 nsIWebAuthnSignPromise* aPromise) { 728 nsresult rv = EnsureWinWebAuthnModuleLoaded(); 729 NS_ENSURE_SUCCESS(rv, rv); 730 731 Reset(); 732 733 auto guard = mTransactionState.Lock(); 734 735 GUID cancellationId; 736 { 737 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 738 if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) { 739 // caller will reject promise 740 return NS_ERROR_DOM_UNKNOWN_ERR; 741 } 742 } 743 744 *guard = Some(TransactionState{ 745 aTransactionId, 746 aBrowsingContextId, 747 Some(RefPtr{aArgs}), 748 Some(RefPtr{aPromise}), 749 cancellationId, 750 }); 751 752 bool conditionallyMediated; 753 (void)aArgs->GetConditionallyMediated(&conditionallyMediated); 754 if (!conditionallyMediated) { 755 DoGetAssertion(Nothing(), guard); 756 } 757 return NS_OK; 758 } 759 760 void WinWebAuthnService::DoGetAssertion( 761 Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId, 762 const TransactionStateMutex::AutoLock& aGuard) { 763 if (aGuard->isNothing() || aGuard->ref().pendingSignArgs.isNothing() || 764 aGuard->ref().pendingSignPromise.isNothing()) { 765 return; 766 } 767 768 // Take the pending Args and Promise to prevent repeated calls to 769 // DoGetAssertion for this transaction. 770 RefPtr<nsIWebAuthnSignArgs> aArgs = aGuard->ref().pendingSignArgs.extract(); 771 RefPtr<nsIWebAuthnSignPromise> aPromise = 772 aGuard->ref().pendingSignPromise.extract(); 773 774 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction( 775 "WinWebAuthnService::MakeCredential", 776 [self = RefPtr{this}, aArgs, aPromise, 777 aSelectedCredentialId = std::move(aSelectedCredentialId), 778 aCancellationId = aGuard->ref().cancellationId]() mutable { 779 // Take a read lock on gWinWebAuthnModuleLock to prevent the module from 780 // being unloaded while the operation is in progress. This does not 781 // prevent the operation from being cancelled, so it does not block a 782 // clean shutdown. 783 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 784 if (!gWinWebAuthnModule) { 785 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR); 786 return; 787 } 788 789 // Attachment 790 DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; 791 792 // AppId 793 BOOL bAppIdUsed = FALSE; 794 BOOL* pbAppIdUsed = nullptr; 795 PCWSTR winAppIdentifier = nullptr; 796 797 // Client Data 798 nsCString clientDataJSON; 799 (void)aArgs->GetClientDataJSON(clientDataJSON); 800 WEBAUTHN_CLIENT_DATA WebAuthNClientData = { 801 WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, 802 (DWORD)clientDataJSON.Length(), (BYTE*)(clientDataJSON.get()), 803 WEBAUTHN_HASH_ALGORITHM_SHA_256}; 804 805 nsString appId; 806 nsresult rv = aArgs->GetAppId(appId); 807 if (rv != NS_ERROR_NOT_AVAILABLE) { 808 if (NS_FAILED(rv)) { 809 aPromise->Reject(rv); 810 return; 811 } 812 winAppIdentifier = appId.get(); 813 pbAppIdUsed = &bAppIdUsed; 814 } 815 816 // RPID 817 nsString rpId; 818 (void)aArgs->GetRpId(rpId); 819 820 // User Verification Requirement 821 nsString userVerificationReq; 822 (void)aArgs->GetUserVerification(userVerificationReq); 823 DWORD winUserVerificationReq; 824 // This mapping needs to be reviewed if values are added to the 825 // UserVerificationRequirement enum. 826 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 827 if (userVerificationReq.EqualsLiteral( 828 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { 829 winUserVerificationReq = 830 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 831 } else if (userVerificationReq.EqualsLiteral( 832 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { 833 winUserVerificationReq = 834 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; 835 } else if (userVerificationReq.EqualsLiteral( 836 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)) { 837 winUserVerificationReq = 838 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 839 } else { 840 winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; 841 } 842 843 // Large Blob 844 DWORD credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE; 845 DWORD credLargeBlobSize = 0; 846 PBYTE credLargeBlob = nullptr; 847 nsTArray<uint8_t> largeBlobWrite; 848 bool largeBlobRead; 849 rv = aArgs->GetLargeBlobRead(&largeBlobRead); 850 if (rv != NS_ERROR_NOT_AVAILABLE) { 851 if (NS_FAILED(rv)) { 852 aPromise->Reject(rv); 853 return; 854 } 855 if (largeBlobRead) { 856 credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET; 857 } else { 858 rv = aArgs->GetLargeBlobWrite(largeBlobWrite); 859 if (rv != NS_ERROR_NOT_AVAILABLE && NS_FAILED(rv)) { 860 aPromise->Reject(rv); 861 return; 862 } 863 credLargeBlobOperation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET; 864 credLargeBlobSize = largeBlobWrite.Length(); 865 credLargeBlob = largeBlobWrite.Elements(); 866 } 867 } 868 869 // PRF inputs 870 WEBAUTHN_HMAC_SECRET_SALT_VALUES* pPrfInputs = nullptr; 871 WEBAUTHN_HMAC_SECRET_SALT_VALUES prfInputs = {0}; 872 WEBAUTHN_HMAC_SECRET_SALT globalHmacSalt = {0}; 873 nsTArray<uint8_t> prfEvalFirst; 874 nsTArray<uint8_t> prfEvalSecond; 875 nsTArray<nsTArray<uint8_t>> prfEvalByCredIds; 876 nsTArray<nsTArray<uint8_t>> prfEvalByCredFirsts; 877 nsTArray<bool> prfEvalByCredSecondMaybes; 878 nsTArray<nsTArray<uint8_t>> prfEvalByCredSeconds; 879 nsTArray<WEBAUTHN_HMAC_SECRET_SALT> hmacSecretSalts; 880 nsTArray<WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT> 881 credWithHmacSecretSaltList; 882 883 bool requestedPrf; 884 (void)aArgs->GetPrf(&requestedPrf); 885 if (requestedPrf) { 886 rv = aArgs->GetPrfEvalFirst(prfEvalFirst); 887 if (rv == NS_OK) { 888 globalHmacSalt.cbFirst = prfEvalFirst.Length(); 889 globalHmacSalt.pbFirst = prfEvalFirst.Elements(); 890 prfInputs.pGlobalHmacSalt = &globalHmacSalt; 891 } 892 rv = aArgs->GetPrfEvalSecond(prfEvalSecond); 893 if (rv == NS_OK) { 894 globalHmacSalt.cbSecond = prfEvalSecond.Length(); 895 globalHmacSalt.pbSecond = prfEvalSecond.Elements(); 896 } 897 if (NS_OK == 898 aArgs->GetPrfEvalByCredentialCredentialId(prfEvalByCredIds) && 899 NS_OK == 900 aArgs->GetPrfEvalByCredentialEvalFirst(prfEvalByCredFirsts) && 901 NS_OK == aArgs->GetPrfEvalByCredentialEvalSecondMaybe( 902 prfEvalByCredSecondMaybes) && 903 NS_OK == aArgs->GetPrfEvalByCredentialEvalSecond( 904 prfEvalByCredSeconds) && 905 prfEvalByCredIds.Length() == prfEvalByCredFirsts.Length() && 906 prfEvalByCredIds.Length() == prfEvalByCredSecondMaybes.Length() && 907 prfEvalByCredIds.Length() == prfEvalByCredSeconds.Length()) { 908 for (size_t i = 0; i < prfEvalByCredIds.Length(); i++) { 909 WEBAUTHN_HMAC_SECRET_SALT salt = {0}; 910 salt.cbFirst = prfEvalByCredFirsts[i].Length(); 911 salt.pbFirst = prfEvalByCredFirsts[i].Elements(); 912 if (prfEvalByCredSecondMaybes[i]) { 913 salt.cbSecond = prfEvalByCredSeconds[i].Length(); 914 salt.pbSecond = prfEvalByCredSeconds[i].Elements(); 915 } 916 hmacSecretSalts.AppendElement(salt); 917 } 918 // The credWithHmacSecretSaltList array will contain raw pointers to 919 // elements of the hmacSecretSalts array, so we must not cause 920 // any re-allocations of hmacSecretSalts from this point. 921 for (size_t i = 0; i < prfEvalByCredIds.Length(); i++) { 922 WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT value = {0}; 923 value.cbCredID = prfEvalByCredIds[i].Length(); 924 value.pbCredID = prfEvalByCredIds[i].Elements(); 925 value.pHmacSecretSalt = &hmacSecretSalts[i]; 926 credWithHmacSecretSaltList.AppendElement(value); 927 } 928 prfInputs.cCredWithHmacSecretSaltList = 929 credWithHmacSecretSaltList.Length(); 930 prfInputs.pCredWithHmacSecretSaltList = 931 credWithHmacSecretSaltList.Elements(); 932 } 933 934 pPrfInputs = &prfInputs; 935 } 936 937 // https://w3c.github.io/webauthn/#prf-extension 938 // "The hmac-secret extension provides two PRFs per credential: one 939 // which is used for requests where user verification is performed and 940 // another for all other requests. This extension [PRF] only exposes a 941 // single PRF per credential and, when implementing on top of 942 // hmac-secret, that PRF MUST be the one used for when user verification 943 // is performed. This overrides the UserVerificationRequirement if 944 // neccessary." 945 if (pPrfInputs && 946 winUserVerificationReq == 947 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED) { 948 winUserVerificationReq = 949 WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; 950 } 951 952 // allow Credentials 953 nsTArray<nsTArray<uint8_t>> allowList; 954 nsTArray<uint8_t> allowListTransports; 955 if (aSelectedCredentialId.isSome()) { 956 allowList.AppendElement(aSelectedCredentialId.extract()); 957 allowListTransports.AppendElement( 958 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL); 959 } else { 960 (void)aArgs->GetAllowList(allowList); 961 (void)aArgs->GetAllowListTransports(allowListTransports); 962 } 963 964 if (allowList.Length() != allowListTransports.Length()) { 965 aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR); 966 return; 967 } 968 969 nsTArray<WEBAUTHN_CREDENTIAL_EX> allowCredentials; 970 WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr; 971 nsTArray<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs; 972 WEBAUTHN_CREDENTIAL_LIST allowCredentialList = {0}; 973 WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr; 974 975 for (size_t i = 0; i < allowList.Length(); i++) { 976 nsTArray<uint8_t>& cred = allowList[i]; 977 uint8_t& transports = allowListTransports[i]; 978 DWORD winTransports = 0; 979 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) { 980 winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB; 981 } 982 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) { 983 winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC; 984 } 985 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) { 986 winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE; 987 } 988 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL) { 989 winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; 990 } 991 if (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID) { 992 winTransports |= WEBAUTHN_CTAP_TRANSPORT_HYBRID; 993 } 994 995 WEBAUTHN_CREDENTIAL_EX credential = { 996 WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, 997 static_cast<DWORD>(cred.Length()), (PBYTE)(cred.Elements()), 998 WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports}; 999 allowCredentials.AppendElement(credential); 1000 } 1001 1002 if (allowCredentials.Length()) { 1003 pAllowCredentials = allowCredentials.Elements(); 1004 for (DWORD i = 0; i < allowCredentials.Length(); i++) { 1005 allowCredentialsPtrs.AppendElement(&pAllowCredentials[i]); 1006 } 1007 allowCredentialList.cCredentials = allowCredentials.Length(); 1008 allowCredentialList.ppCredentials = allowCredentialsPtrs.Elements(); 1009 pAllowCredentialList = &allowCredentialList; 1010 } 1011 1012 nsTArray<nsString> inputHints; 1013 (void)aArgs->GetHints(inputHints); 1014 1015 nsTArray<LPCWSTR> hints; 1016 PrunePublicKeyCredentialHints(inputHints, hints); 1017 1018 uint32_t timeout_u32; 1019 (void)aArgs->GetTimeoutMS(&timeout_u32); 1020 DWORD timeout = timeout_u32; 1021 1022 bool privateBrowsing; 1023 (void)aArgs->GetPrivateBrowsing(&privateBrowsing); 1024 BOOL winPrivateBrowsing = FALSE; 1025 if (privateBrowsing) { 1026 winPrivateBrowsing = TRUE; 1027 } 1028 1029 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions = 1030 { 1031 WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_8, 1032 timeout, 1033 {0, NULL}, 1034 {0, NULL}, 1035 winAttachment, 1036 winUserVerificationReq, 1037 0, // dwFlags 1038 winAppIdentifier, 1039 pbAppIdUsed, 1040 &aCancellationId, // CancellationId 1041 pAllowCredentialList, 1042 credLargeBlobOperation, // CredLargeBlobOperation 1043 credLargeBlobSize, // Size of CredLargeBlob 1044 credLargeBlob, // CredLargeBlob 1045 pPrfInputs, // HmacSecretSaltValues 1046 winPrivateBrowsing, // BrowserInPrivateMode 1047 NULL, // LinkedDevice 1048 FALSE, // AutoFill 1049 0, // Size of JsonExt 1050 NULL, // JsonExt 1051 (DWORD)hints.Length(), // Size of CredentialHints 1052 hints.Elements(), // CredentialHints 1053 }; 1054 1055 PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr; 1056 1057 // Bug 1518876: Get Window Handle from Content process for Windows 1058 // WebAuthN APIs 1059 HWND hWnd = GetForegroundWindow(); 1060 1061 HRESULT hr = gWinWebauthnGetAssertion( 1062 hWnd, rpId.get(), &WebAuthNClientData, &WebAuthNAssertionOptions, 1063 &pWebAuthNAssertion); 1064 1065 if (hr == S_OK) { 1066 RefPtr<WebAuthnSignResult> result = new WebAuthnSignResult( 1067 clientDataJSON, credLargeBlobOperation, pWebAuthNAssertion); 1068 gWinWebauthnFreeAssertion(pWebAuthNAssertion); 1069 if (winAppIdentifier != nullptr) { 1070 // The gWinWebauthnGetAssertion call modified bAppIdUsed through 1071 // a pointer provided in WebAuthNAssertionOptions. 1072 (void)result->SetUsedAppId(bAppIdUsed == TRUE); 1073 } 1074 aPromise->Resolve(result); 1075 } else { 1076 PCWSTR errorName = gWinWebauthnGetErrorName(hr); 1077 nsresult aError = NS_ERROR_DOM_ABORT_ERR; 1078 1079 if (_wcsicmp(errorName, L"InvalidStateError") == 0) { 1080 aError = NS_ERROR_DOM_INVALID_STATE_ERR; 1081 } else if (_wcsicmp(errorName, L"ConstraintError") == 0 || 1082 _wcsicmp(errorName, L"UnknownError") == 0) { 1083 aError = NS_ERROR_DOM_UNKNOWN_ERR; 1084 } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) { 1085 aError = NS_ERROR_DOM_INVALID_STATE_ERR; 1086 } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) { 1087 aError = NS_ERROR_DOM_NOT_ALLOWED_ERR; 1088 } 1089 1090 aPromise->Reject(aError); 1091 } 1092 })); 1093 1094 NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK); 1095 } 1096 1097 NS_IMETHODIMP 1098 WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId, 1099 const nsAString& aOrigin, 1100 uint64_t* aRv) { 1101 auto guard = mTransactionState.Lock(); 1102 if (guard->isNothing() || 1103 guard->ref().browsingContextId != aBrowsingContextId || 1104 guard->ref().pendingSignArgs.isNothing()) { 1105 *aRv = 0; 1106 return NS_OK; 1107 } 1108 1109 nsString origin; 1110 (void)guard->ref().pendingSignArgs.ref()->GetOrigin(origin); 1111 if (origin != aOrigin) { 1112 *aRv = 0; 1113 return NS_OK; 1114 } 1115 1116 *aRv = guard->ref().transactionId; 1117 return NS_OK; 1118 } 1119 1120 NS_IMETHODIMP 1121 WinWebAuthnService::GetAutoFillEntries( 1122 uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) { 1123 aRv.Clear(); 1124 nsString rpId; 1125 1126 { 1127 auto guard = mTransactionState.Lock(); 1128 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1129 guard->ref().pendingSignArgs.isNothing()) { 1130 return NS_ERROR_NOT_AVAILABLE; 1131 } 1132 (void)guard->ref().pendingSignArgs.ref()->GetRpId(rpId); 1133 } 1134 1135 StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock); 1136 if (!gWinWebAuthnModule) { 1137 return NS_ERROR_NOT_AVAILABLE; 1138 } 1139 1140 if (gWinWebauthnGetApiVersionNumber() < WEBAUTHN_API_VERSION_4) { 1141 // GetPlatformCredentialList was added in version 4. Earlier versions 1142 // can still present a generic "Use a Passkey" autofill entry, so 1143 // this isn't an error. 1144 return NS_OK; 1145 } 1146 1147 WEBAUTHN_GET_CREDENTIALS_OPTIONS getCredentialsOptions{ 1148 WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1, 1149 rpId.get(), // pwszRpId 1150 FALSE, // bBrowserInPrivateMode 1151 }; 1152 PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialList = nullptr; 1153 HRESULT hr = gWinWebauthnGetPlatformCredentialList(&getCredentialsOptions, 1154 &pCredentialList); 1155 // WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_ 1156 // annotation and a comment "Returns NTE_NOT_FOUND when credentials are 1157 // not found." 1158 if (pCredentialList == nullptr) { 1159 if (hr != NTE_NOT_FOUND) { 1160 return NS_ERROR_FAILURE; 1161 } 1162 return NS_OK; 1163 } 1164 MOZ_ASSERT(hr == S_OK); 1165 for (size_t i = 0; i < pCredentialList->cCredentialDetails; i++) { 1166 RefPtr<nsIWebAuthnAutoFillEntry> entry( 1167 new WebAuthnAutoFillEntry(pCredentialList->ppCredentialDetails[i])); 1168 aRv.AppendElement(entry); 1169 } 1170 gWinWebauthnFreePlatformCredentialList(pCredentialList); 1171 return NS_OK; 1172 } 1173 1174 NS_IMETHODIMP 1175 WinWebAuthnService::SelectAutoFillEntry( 1176 uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) { 1177 auto guard = mTransactionState.Lock(); 1178 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1179 guard->ref().pendingSignArgs.isNothing()) { 1180 return NS_ERROR_NOT_AVAILABLE; 1181 } 1182 1183 nsTArray<nsTArray<uint8_t>> allowList; 1184 (void)guard->ref().pendingSignArgs.ref()->GetAllowList(allowList); 1185 if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) { 1186 return NS_ERROR_FAILURE; 1187 } 1188 1189 Maybe<nsTArray<uint8_t>> id; 1190 id.emplace(); 1191 id.ref().Assign(aCredentialId); 1192 DoGetAssertion(std::move(id), guard); 1193 1194 return NS_OK; 1195 } 1196 1197 NS_IMETHODIMP 1198 WinWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) { 1199 auto guard = mTransactionState.Lock(); 1200 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1201 guard->ref().pendingSignArgs.isNothing()) { 1202 return NS_ERROR_NOT_AVAILABLE; 1203 } 1204 DoGetAssertion(Nothing(), guard); 1205 return NS_OK; 1206 } 1207 1208 NS_IMETHODIMP 1209 WinWebAuthnService::PinCallback(uint64_t aTransactionId, 1210 const nsACString& aPin) { 1211 return NS_ERROR_NOT_IMPLEMENTED; 1212 } 1213 1214 NS_IMETHODIMP 1215 WinWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId, 1216 bool aHasConsent) { 1217 return NS_ERROR_NOT_IMPLEMENTED; 1218 } 1219 1220 NS_IMETHODIMP 1221 WinWebAuthnService::SelectionCallback(uint64_t aTransactionId, 1222 uint64_t aIndex) { 1223 return NS_ERROR_NOT_IMPLEMENTED; 1224 } 1225 1226 NS_IMETHODIMP 1227 WinWebAuthnService::AddVirtualAuthenticator( 1228 const nsACString& aProtocol, const nsACString& aTransport, 1229 bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting, 1230 bool aIsUserVerified, nsACString& aRetval) { 1231 return NS_ERROR_NOT_IMPLEMENTED; 1232 } 1233 1234 NS_IMETHODIMP 1235 WinWebAuthnService::RemoveVirtualAuthenticator( 1236 const nsACString& aAuthenticatorId) { 1237 return NS_ERROR_NOT_IMPLEMENTED; 1238 } 1239 1240 NS_IMETHODIMP 1241 WinWebAuthnService::AddCredential(const nsACString& aAuthenticatorId, 1242 const nsACString& aCredentialId, 1243 bool aIsResidentCredential, 1244 const nsACString& aRpId, 1245 const nsACString& aPrivateKey, 1246 const nsACString& aUserHandle, 1247 uint32_t aSignCount) { 1248 return NS_ERROR_NOT_IMPLEMENTED; 1249 } 1250 1251 NS_IMETHODIMP 1252 WinWebAuthnService::GetCredentials( 1253 const nsACString& aAuthenticatorId, 1254 nsTArray<RefPtr<nsICredentialParameters>>& _aRetval) { 1255 return NS_ERROR_NOT_IMPLEMENTED; 1256 } 1257 1258 NS_IMETHODIMP 1259 WinWebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId, 1260 const nsACString& aCredentialId) { 1261 return NS_ERROR_NOT_IMPLEMENTED; 1262 } 1263 1264 NS_IMETHODIMP 1265 WinWebAuthnService::RemoveAllCredentials(const nsACString& aAuthenticatorId) { 1266 return NS_ERROR_NOT_IMPLEMENTED; 1267 } 1268 1269 NS_IMETHODIMP 1270 WinWebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId, 1271 bool aIsUserVerified) { 1272 return NS_ERROR_NOT_IMPLEMENTED; 1273 } 1274 1275 NS_IMETHODIMP 1276 WinWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; } 1277 1278 NS_IMETHODIMP 1279 WinWebAuthnService::RunCommand(const nsACString& aCmd) { 1280 return NS_ERROR_NOT_IMPLEMENTED; 1281 } 1282 1283 } // namespace mozilla::dom