void
U2FTokenManager::AbortTransaction(const uint64_t& aTransactionId,
                                  const nsresult& aError)
{
  Unused << mTransactionParent->SendAbort(aTransactionId, aError);
  ClearTransaction();
}
void WinWebAuthnManager::MaybeClearTransaction(
    PWebAuthnTransactionParent* aParent) {
  // Only clear if we've been requested to do so by our current transaction
  // parent.
  if (mTransactionParent == aParent) {
    ClearTransaction();
  }
}
void
U2FTokenManager::MaybeConfirmSign(const uint64_t& aTransactionId,
                                  const WebAuthnGetAssertionResult& aResult)
{
  MOZ_ASSERT(mLastTransactionId == aTransactionId);
  mSignPromise.Complete();

  Unused << mTransactionParent->SendConfirmSign(aTransactionId, aResult);
  ClearTransaction();
}
void
U2FTokenManager::MaybeConfirmRegister(const uint64_t& aTransactionId,
                                      const WebAuthnMakeCredentialResult& aResult)
{
  MOZ_ASSERT(mLastTransactionId == aTransactionId);
  mRegisterPromise.Complete();

  Unused << mTransactionParent->SendConfirmRegister(aTransactionId, aResult);
  ClearTransaction();
}
void
U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent,
                        const uint64_t& aTransactionId)
{
  if (mTransactionParent != aParent || mLastTransactionId != aTransactionId) {
    return;
  }

  mTokenManagerImpl->Cancel();
  ClearTransaction();
}
Exemple #6
0
void
U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
                      const uint64_t& aTransactionId,
                      const WebAuthnGetAssertionInfo& aTransactionInfo)
{
  MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;
  mTokenManagerImpl = GetTokenManagerImpl();

  if (!mTokenManagerImpl) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return;
  }

  if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
      (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
    return;
  }

  // Show a prompt that lets the user cancel the ongoing transaction.
  NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
  SendPromptNotification(kSignPromptNotifcation,
                         aTransactionId,
                         origin.get());

  uint64_t tid = mLastTransactionId = aTransactionId;
  mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();

  mTokenManagerImpl
    ->Sign(aTransactionInfo)
    ->Then(GetCurrentThreadSerialEventTarget(), __func__,
      [tid, startTime](WebAuthnGetAssertionResult&& aResult) {
        U2FTokenManager* mgr = U2FTokenManager::Get();
        mgr->MaybeConfirmSign(tid, aResult);
        Telemetry::ScalarAdd(
          Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
          NS_LITERAL_STRING("U2FSignFinish"), 1);
        Telemetry::AccumulateTimeDelta(
          Telemetry::WEBAUTHN_GET_ASSERTION_MS,
          startTime);
      },
      [tid](nsresult rv) {
        MOZ_ASSERT(NS_FAILED(rv));
        U2FTokenManager* mgr = U2FTokenManager::Get();
        mgr->MaybeAbortSign(tid, rv);
        Telemetry::ScalarAdd(
          Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
          NS_LITERAL_STRING("U2FSignAbort"), 1);
      })
    ->Track(mSignPromise);
}
void
U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
                          const uint64_t& aTransactionId,
                          const WebAuthnMakeCredentialInfo& aTransactionInfo)
{
  MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;
  mTokenManagerImpl = GetTokenManagerImpl();

  if (!mTokenManagerImpl) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return;
  }

  // Check if all the supplied parameters are syntactically well-formed and
  // of the correct length. If not, return an error code equivalent to
  // UnknownError and terminate the operation.

  if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
      (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
    return;
  }

  uint64_t tid = mLastTransactionId = aTransactionId;
  mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
  mTokenManagerImpl->Register(aTransactionInfo.ExcludeList(),
                              aTransactionInfo.AuthenticatorSelection(),
                              aTransactionInfo.RpIdHash(),
                              aTransactionInfo.ClientDataHash(),
                              aTransactionInfo.TimeoutMS())
                   ->Then(GetCurrentThreadSerialEventTarget(), __func__,
                         [tid, startTime](U2FRegisterResult&& aResult) {
                           U2FTokenManager* mgr = U2FTokenManager::Get();
                           mgr->MaybeConfirmRegister(tid, aResult);
                           Telemetry::ScalarAdd(
                             Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
                             NS_LITERAL_STRING("U2FRegisterFinish"), 1);
                           Telemetry::AccumulateTimeDelta(
                             Telemetry::WEBAUTHN_CREATE_CREDENTIAL_MS,
                             startTime);
                         },
                         [tid](nsresult rv) {
                           MOZ_ASSERT(NS_FAILED(rv));
                           U2FTokenManager* mgr = U2FTokenManager::Get();
                           mgr->MaybeAbortRegister(tid, rv);
                           Telemetry::ScalarAdd(
                             Telemetry::ScalarID::SECURITY_WEBAUTHN_USED,
                             NS_LITERAL_STRING("U2FRegisterAbort"), 1);
                         })
                   ->Track(mRegisterPromise);
}
void WinWebAuthnManager::Cancel(PWebAuthnTransactionParent* aParent,
                                const uint64_t& aTransactionId) {
  if (mTransactionParent != aParent) {
    return;
  }

  ClearTransaction();

  auto iter = mCancellationIds.find(aTransactionId);
  if (iter != mCancellationIds.end()) {
    gWinWebauthnCancelCurrentOperation(iter->second);
  }
}
void
U2FTokenManager::MaybeConfirmRegister(const uint64_t& aTransactionId,
                                      U2FRegisterResult& aResult)
{
  MOZ_ASSERT(mLastTransactionId == aTransactionId);
  mRegisterPromise.Complete();

  nsTArray<uint8_t> registration;
  aResult.ConsumeRegistration(registration);

  Unused << mTransactionParent->SendConfirmRegister(aTransactionId, registration);
  ClearTransaction();
}
void
U2FTokenManager::MaybeConfirmSign(const uint64_t& aTransactionId,
                                  U2FSignResult& aResult)
{
  MOZ_ASSERT(mLastTransactionId == aTransactionId);
  mSignPromise.Complete();

  nsTArray<uint8_t> keyHandle;
  aResult.ConsumeKeyHandle(keyHandle);
  nsTArray<uint8_t> signature;
  aResult.ConsumeSignature(signature);

  Unused << mTransactionParent->SendConfirmSign(aTransactionId, keyHandle, signature);
  ClearTransaction();
}
void
U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
                          const uint64_t& aTransactionId,
                          const WebAuthnMakeCredentialInfo& aTransactionInfo)
{
  MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;
  mTokenManagerImpl = GetTokenManagerImpl();

  if (!mTokenManagerImpl) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return;
  }

  mLastTransactionId = aTransactionId;

  // Determine whether direct attestation was requested.
  bool directAttestationRequested = false;
  if (aTransactionInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo::TWebAuthnMakeCredentialExtraInfo) {
    const auto& extra = aTransactionInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();
    directAttestationRequested = extra.RequestDirectAttestation();
  }

  // Start a register request immediately if direct attestation
  // wasn't requested or the test pref is set.
  if (!directAttestationRequested ||
      U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) {
    // Force "none" attestation when "direct" attestation wasn't requested.
    DoRegister(aTransactionInfo, !directAttestationRequested);
    return;
  }

  // If the RP request direct attestation, ask the user for permission and
  // store the transaction info until the user proceeds or cancels.
  NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
  SendPromptNotification(kRegisterDirectPromptNotifcation,
                         aTransactionId,
                         origin.get());

  MOZ_ASSERT(mPendingRegisterInfo.isNothing());
  mPendingRegisterInfo = Some(aTransactionInfo);
}
Exemple #12
0
void
U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
                          const uint64_t& aTransactionId,
                          const WebAuthnMakeCredentialInfo& aTransactionInfo)
{
  MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;
  mTokenManagerImpl = GetTokenManagerImpl();

  if (!mTokenManagerImpl) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return;
  }

  // Check if all the supplied parameters are syntactically well-formed and
  // of the correct length. If not, return an error code equivalent to
  // UnknownError and terminate the operation.

  if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
      (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
    AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
    return;
  }

  mLastTransactionId = aTransactionId;

  // If the RP request direct attestation, ask the user for permission and
  // store the transaction info until the user proceeds or cancels.
  // Might be overriden by a pref for testing purposes.
  if (aTransactionInfo.RequestDirectAttestation() &&
      !U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) {
    NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin());
    SendPromptNotification(kRegisterDirectPromptNotifcation,
                           aTransactionId,
                           origin.get());

    MOZ_ASSERT(mPendingRegisterInfo.isNothing());
    mPendingRegisterInfo = Some(aTransactionInfo);
  } else {
    DoRegister(aTransactionInfo);
  }
}
void WinWebAuthnManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
                              const uint64_t& aTransactionId,
                              const WebAuthnGetAssertionInfo& aInfo) {
  MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNSign"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;

  // User Verification Requirement
  DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;

  // RPID
  PCWSTR rpID = nullptr;

  // Attachment
  DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;

  // AppId
  BOOL bU2fAppIdUsed = FALSE;
  BOOL* pbU2fAppIdUsed = nullptr;
  PCWSTR winAppIdentifier = nullptr;

  // Client Data
  WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
      WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, aInfo.ClientDataJSON().Length(),
      (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256};

  if (aInfo.Extra().type() ==
      WebAuthnMaybeGetAssertionExtraInfo::TWebAuthnGetAssertionExtraInfo) {
    const auto& extra = aInfo.Extra().get_WebAuthnGetAssertionExtraInfo();

    for (const WebAuthnExtension& ext : extra.Extensions()) {
      if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
        winAppIdentifier =
            ext.get_WebAuthnExtensionAppId().appIdentifier().get();
        pbU2fAppIdUsed = &bU2fAppIdUsed;
        break;
      }
    }

    // RPID
    rpID = aInfo.RpId().get();

    // User Verification Requirement
    UserVerificationRequirement userVerificationReq =
        static_cast<UserVerificationRequirement>(
            extra.userVerificationRequirement());

    switch (userVerificationReq) {
      case UserVerificationRequirement::Required:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
        break;
      case UserVerificationRequirement::Preferred:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
        break;
      case UserVerificationRequirement::Discouraged:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
        break;
      default:
        winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
        break;
    }
  } else {
    rpID = aInfo.Origin().get();
    winAppIdentifier = aInfo.RpId().get();
    pbU2fAppIdUsed = &bU2fAppIdUsed;
    winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
    winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
  }

  // allow Credentials
  nsTArray<WEBAUTHN_CREDENTIAL_EX> allowCredentials;
  WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr;
  nsTArray<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs;
  WEBAUTHN_CREDENTIAL_LIST allowCredentialList = {0};
  WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr;

  for (auto& cred : aInfo.AllowList()) {
    uint8_t transports = cred.transports();
    DWORD winTransports = 0;
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_USB) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
    }
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_NFC) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
    }
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_BLE) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
    }
    if (transports & CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
    }

    WEBAUTHN_CREDENTIAL_EX credential = {
        WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
        static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()),
        WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
    allowCredentials.AppendElement(credential);
  }

  if (allowCredentials.Length()) {
    pAllowCredentials = allowCredentials.Elements();
    for (DWORD i = 0; i < allowCredentials.Length(); i++) {
      allowCredentialsPtrs.AppendElement(&pAllowCredentials[i]);
    }
    allowCredentialList.cCredentials = allowCredentials.Length();
    allowCredentialList.ppCredentials = allowCredentialsPtrs.Elements();
    pAllowCredentialList = &allowCredentialList;
  }

  WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS WebAuthNAssertionOptions = {
      WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION,
      aInfo.TimeoutMS(),
      {0, NULL},
      {0, NULL},
      WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
      winUserVerificationReq,
      0,  // dwFlags
      winAppIdentifier,
      pbU2fAppIdUsed,
      nullptr,  // pCancellationId
      pAllowCredentialList,
  };

  GUID cancellationId = {0};
  if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) {
    WebAuthNAssertionOptions.pCancellationId = &cancellationId;
    mCancellationIds.emplace(aTransactionId, &cancellationId);
  }

  PWEBAUTHN_ASSERTION pWebAuthNAssertion = nullptr;

  // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN
  // APIs
  HWND hWnd = GetForegroundWindow();

  HRESULT hr =
      gWinWebauthnGetAssertion(hWnd, rpID, &WebAuthNClientData,
                               &WebAuthNAssertionOptions, &pWebAuthNAssertion);

  mCancellationIds.erase(aTransactionId);

  if (hr == S_OK) {
    nsTArray<uint8_t> signature;
    if (aInfo.Extra().type() ==
        WebAuthnMaybeGetAssertionExtraInfo::TWebAuthnGetAssertionExtraInfo) {
      signature.AppendElements(pWebAuthNAssertion->pbSignature,
                               pWebAuthNAssertion->cbSignature);
    } else {
      // AuthenticatorData Length check.
      // First 32 bytes: RPID Hash
      // Next 1 byte: Flags
      // Next 4 bytes: Counter
      if (pWebAuthNAssertion->cbAuthenticatorData < 32 + 1 + 4) {
        MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
      }

      signature.AppendElement(0x01);  // User Presence bit
      signature.AppendElements(pWebAuthNAssertion->pbAuthenticatorData +
                                   32 +  // RPID Hash length
                                   1,    // Flags
                               4);       // Counter length
      signature.AppendElements(pWebAuthNAssertion->pbSignature,
                               pWebAuthNAssertion->cbSignature);
    }

    nsTArray<uint8_t> keyHandle;
    keyHandle.AppendElements(pWebAuthNAssertion->Credential.pbId,
                             pWebAuthNAssertion->Credential.cbId);

    nsTArray<uint8_t> authenticatorData;
    authenticatorData.AppendElements(pWebAuthNAssertion->pbAuthenticatorData,
                                     pWebAuthNAssertion->cbAuthenticatorData);

    nsTArray<WebAuthnExtensionResult> extensions;

    if (pbU2fAppIdUsed && *pbU2fAppIdUsed) {
      extensions.AppendElement(WebAuthnExtensionResultAppId(true));
    }

    WebAuthnGetAssertionResult result(aInfo.ClientDataJSON(), keyHandle,
                                      signature, authenticatorData, extensions,
                                      signature);

    Unused << mTransactionParent->SendConfirmSign(aTransactionId, result);
    ClearTransaction();

    gWinWebauthnFreeAssertion(pWebAuthNAssertion);

  } else {
    PCWSTR errorName = gWinWebauthnGetErrorName(hr);
    nsresult aError = NS_ERROR_DOM_ABORT_ERR;

    if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
      aError = NS_ERROR_DOM_INVALID_STATE_ERR;
    } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
               _wcsicmp(errorName, L"UnknownError") == 0) {
      aError = NS_ERROR_DOM_UNKNOWN_ERR;
    } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
      aError = NS_ERROR_DOM_INVALID_STATE_ERR;
    } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
      aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
    }

    MaybeAbortSign(aTransactionId, aError);
  }
}
void WinWebAuthnManager::Register(
    PWebAuthnTransactionParent* aTransactionParent,
    const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aInfo) {
  MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNRegister"));

  ClearTransaction();
  mTransactionParent = aTransactionParent;

  BYTE U2FUserId = 0x01;

  // RP Information
  WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = {
      WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, aInfo.RpId().get(),
      nullptr, nullptr};

  // User Information
  WEBAUTHN_USER_ENTITY_INFORMATION userInfo = {
      WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
      0,
      nullptr,
      nullptr,
      nullptr,
      nullptr};

  // Client Data
  WEBAUTHN_CLIENT_DATA WebAuthNClientData = {
      WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, aInfo.ClientDataJSON().Length(),
      (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256};

  // Algorithms
  nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams;

  // User Verification Requirement
  DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;

  // Attachment
  DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;

  // Resident Key
  BOOL winRequireResidentKey = FALSE;

  // AttestationConveyance
  DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;

  if (aInfo.Extra().type() ==
      WebAuthnMaybeMakeCredentialExtraInfo::TWebAuthnMakeCredentialExtraInfo) {
    const auto& extra = aInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();

    rpInfo.pwszName = extra.Rp().Name().get();
    rpInfo.pwszIcon = extra.Rp().Icon().get();

    userInfo.cbId = static_cast<DWORD>(extra.User().Id().Length());
    userInfo.pbId = const_cast<unsigned char*>(extra.User().Id().Elements());
    userInfo.pwszName = extra.User().Name().get();
    userInfo.pwszIcon = extra.User().Icon().get();
    userInfo.pwszDisplayName = extra.User().DisplayName().get();

    for (const auto& coseAlg : extra.coseAlgs()) {
      WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
          WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
          WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg.alg()};
      coseParams.AppendElement(coseAlgorithm);
    }

    const auto& sel = extra.AuthenticatorSelection();

    UserVerificationRequirement userVerificationReq =
        static_cast<UserVerificationRequirement>(
            sel.userVerificationRequirement());
    switch (userVerificationReq) {
      case UserVerificationRequirement::Required:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
        break;
      case UserVerificationRequirement::Preferred:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
        break;
      case UserVerificationRequirement::Discouraged:
        winUserVerificationReq =
            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
        break;
      default:
        winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
        break;
    }

    if (sel.authenticatorAttachment().type() ==
        WebAuthnMaybeAuthenticatorAttachment::Tuint8_t) {
      const AuthenticatorAttachment authenticatorAttachment =
          static_cast<AuthenticatorAttachment>(
              sel.authenticatorAttachment().get_uint8_t());
      switch (authenticatorAttachment) {
        case AuthenticatorAttachment::Platform:
          winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
          break;
        case AuthenticatorAttachment::Cross_platform:
          winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
          break;
        default:
          break;
      }
    }

    winRequireResidentKey = sel.requireResidentKey();

    // AttestationConveyance
    AttestationConveyancePreference attestation =
        static_cast<AttestationConveyancePreference>(
            extra.attestationConveyancePreference());
    DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
    switch (attestation) {
      case AttestationConveyancePreference::Direct:
        winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
        break;
      case AttestationConveyancePreference::Indirect:
        winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT;
        break;
      case AttestationConveyancePreference::None:
        winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
        break;
      default:
        winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY;
        break;
    }
  } else {
    userInfo.cbId = sizeof(BYTE);
    userInfo.pbId = &U2FUserId;

    WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = {
        WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
        WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
        WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256};
    coseParams.AppendElement(coseAlgorithm);

    winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
  }

  WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = {
      static_cast<DWORD>(coseParams.Length()), coseParams.Elements()};

  // Exclude Credentials
  nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials;
  WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr;
  nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs;
  WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0};
  WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr;

  for (auto& cred : aInfo.ExcludeList()) {
    uint8_t transports = cred.transports();
    DWORD winTransports = 0;
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_USB) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
    }
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_NFC) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC;
    }
    if (transports & U2F_AUTHENTICATOR_TRANSPORT_BLE) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE;
    }
    if (transports & CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL) {
      winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
    }

    WEBAUTHN_CREDENTIAL_EX credential = {
        WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
        static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()),
        WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports};
    excludeCredentials.AppendElement(credential);
  }

  if (!excludeCredentials.IsEmpty()) {
    pExcludeCredentials = excludeCredentials.Elements();
    for (DWORD i = 0; i < excludeCredentials.Length(); i++) {
      excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]);
    }
    excludeCredentialList.cCredentials = excludeCredentials.Length();
    excludeCredentialList.ppCredentials = excludeCredentialsPtrs.Elements();
    pExcludeCredentialList = &excludeCredentialList;
  }

  // MakeCredentialOptions
  WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS WebAuthNCredentialOptions = {
      WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION,
      aInfo.TimeoutMS(),
      {0, NULL},
      {0, NULL},
      winAttachment,
      winRequireResidentKey,
      winUserVerificationReq,
      winAttestation,
      0,     // Flags
      NULL,  // CancellationId
      pExcludeCredentialList};

  GUID cancellationId = {0};
  if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) {
    WebAuthNCredentialOptions.pCancellationId = &cancellationId;
    mCancellationIds.emplace(aTransactionId, &cancellationId);
  }

  WEBAUTHN_CREDENTIAL_ATTESTATION* pWebAuthNCredentialAttestation = nullptr;

  // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN
  // APIs
  HWND hWnd = GetForegroundWindow();

  HRESULT hr = gWinWebauthnMakeCredential(
      hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters,
      &WebAuthNClientData, &WebAuthNCredentialOptions,
      &pWebAuthNCredentialAttestation);

  mCancellationIds.erase(aTransactionId);

  if (hr == S_OK) {
    nsTArray<uint8_t> attObject;
    attObject.AppendElements(
        pWebAuthNCredentialAttestation->pbAttestationObject,
        pWebAuthNCredentialAttestation->cbAttestationObject);

    nsTArray<uint8_t> credentialId;
    credentialId.AppendElements(pWebAuthNCredentialAttestation->pbCredentialId,
                                pWebAuthNCredentialAttestation->cbCredentialId);

    nsTArray<uint8_t> authenticatorData;

    if (aInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo::
                                    TWebAuthnMakeCredentialExtraInfo) {
      authenticatorData.AppendElements(
          pWebAuthNCredentialAttestation->pbAuthenticatorData,
          pWebAuthNCredentialAttestation->cbAuthenticatorData);
    } else {
      PWEBAUTHN_COMMON_ATTESTATION attestation =
          reinterpret_cast<PWEBAUTHN_COMMON_ATTESTATION>(
              pWebAuthNCredentialAttestation->pvAttestationDecode);

      DWORD coseKeyOffset = 32 +  // RPIDHash
                            1 +   // Flags
                            4 +   // Counter
                            16 +  // AAGuid
                            2 +   // Credential ID Length field
                            pWebAuthNCredentialAttestation->cbCredentialId;

      // Hardcoding as couldn't finder decoder and it is an ECC key.
      DWORD xOffset = coseKeyOffset + 10;
      DWORD yOffset = coseKeyOffset + 45;

      // Authenticator Data length check.
      if (pWebAuthNCredentialAttestation->cbAuthenticatorData < yOffset + 32) {
        MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR);
      }

      authenticatorData.AppendElement(0x05);  // Reserved Byte
      authenticatorData.AppendElement(0x04);  // ECC Uncompressed Key
      authenticatorData.AppendElements(
          pWebAuthNCredentialAttestation->pbAuthenticatorData + xOffset,
          32);  // X Coordinate
      authenticatorData.AppendElements(
          pWebAuthNCredentialAttestation->pbAuthenticatorData + yOffset,
          32);  // Y Coordinate
      authenticatorData.AppendElement(
          pWebAuthNCredentialAttestation->cbCredentialId);
      authenticatorData.AppendElements(
          pWebAuthNCredentialAttestation->pbCredentialId,
          pWebAuthNCredentialAttestation->cbCredentialId);
      authenticatorData.AppendElements(attestation->pX5c->pbData,
                                       attestation->pX5c->cbData);
      authenticatorData.AppendElements(attestation->pbSignature,
                                       attestation->cbSignature);
    }

    WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObject,
                                        credentialId, authenticatorData);

    Unused << mTransactionParent->SendConfirmRegister(aTransactionId, result);
    ClearTransaction();
    gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation);

  } else {
    PCWSTR errorName = gWinWebauthnGetErrorName(hr);
    nsresult aError = NS_ERROR_DOM_ABORT_ERR;

    if (_wcsicmp(errorName, L"InvalidStateError") == 0) {
      aError = NS_ERROR_DOM_INVALID_STATE_ERR;
    } else if (_wcsicmp(errorName, L"ConstraintError") == 0 ||
               _wcsicmp(errorName, L"UnknownError") == 0) {
      aError = NS_ERROR_DOM_UNKNOWN_ERR;
    } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) {
      aError = NS_ERROR_DOM_INVALID_STATE_ERR;
    } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) {
      aError = NS_ERROR_DOM_NOT_ALLOWED_ERR;
    }

    MaybeAbortRegister(aTransactionId, aError);
  }
}