tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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