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 #2
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);
  }
}
Exemple #3
0
void
U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo)
{
  mozilla::ipc::AssertIsOnBackgroundThread();
  MOZ_ASSERT(mLastTransactionId > 0);

  // Show a prompt that lets the user cancel the ongoing transaction.
  NS_ConvertUTF16toUTF8 origin(aInfo.Origin());
  SendPromptNotification(kRegisterPromptNotifcation,
                         mLastTransactionId,
                         origin.get());

  uint64_t tid = mLastTransactionId;
  mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
  bool requestDirectAttestation = aInfo.RequestDirectAttestation();

  mTokenManagerImpl
    ->Register(aInfo)
    ->Then(GetCurrentThreadSerialEventTarget(), __func__,
          [tid, startTime, requestDirectAttestation](WebAuthnMakeCredentialResult&& aResult) {
            U2FTokenManager* mgr = U2FTokenManager::Get();
            // The token manager implementations set DirectAttestationPermitted
            // to false by default. Override this here with information from
            // the JS prompt.
            aResult.DirectAttestationPermitted() = requestDirectAttestation;
            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
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::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);
  }
}