Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  WebAuthnUtil.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/WebAuthnUtil.h"

#include "hasht.h"
#include "js/Exception.h"
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/dom/WebAuthenticationBinding.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/extensions/MatchPattern.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/net/DNS.h"
#include "mozpkix/pkixutil.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsHTMLDocument.h"
#include "nsICryptoHash.h"
#include "nsIEffectiveTLDService.h"
#include "nsIURIMutator.h"
#include "nsNetUtil.h"

namespace mozilla::dom {

bool IsValidAppId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
                  const nsCString& aAppId) {
  // An AppID is a substitute for the RP ID that allows the caller to assert
  // credentials that were created using the legacy U2F protocol. While an RP ID
  // is the caller origin's effective domain, or a registrable suffix thereof,
  // an AppID is a URL (with a scheme and a possibly non-empty path) that is
  // same-site with the caller's origin.
  //
  // The U2F protocol nominally uses Algorithm 3.1.2 of [1] to validate AppIDs.
  // However, the WebAuthn spec [2] notes that it is not necessary to "implement
  // steps four and onward of" Algorithm 3.1.2. Instead, in step three, "the
  // comparison on the host is relaxed to accept hosts on the same site." Step
  // two is best seen as providing a default value for the AppId when one is not
  // provided. That leaves step 1 and the same-site check, which is what we
  // implement here.
  //
  // [1]
  // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-if-a-caller-s-facetid-is-authorized-for-an-appid
  // [2] https://w3c.github.io/webauthn/#sctn-appid-extension

  auto* principal = BasePrincipal::Cast(aPrincipal);
  bool reqIsFromExtension = !!principal->AddonPolicy();
  if (reqIsFromExtension) {
    // AppIDs are not allowed from extensions.
    return false;
  }

  nsCOMPtr<nsIURI> callerUri;
  nsresult rv = principal->GetURI(getter_AddRefs(callerUri));
  if (NS_FAILED(rv)) {
    return false;
  }

  nsCOMPtr<nsIURI> appIdUri;
  rv = NS_NewURI(getter_AddRefs(appIdUri), aAppId);
  if (NS_FAILED(rv)) {
    return false;
  }

  // Step 1 of Algorithm 3.1.2. "If the AppID is not an HTTPS URL, and matches
  // the FacetID of the caller, no additional processing is necessary and the
  // operation may proceed." In the web context, the "FacetID" is defined as
  // "the Web Origin [RFC6454] of the web page triggering the FIDO operation,
  // written as a URI with an empty path. Default ports are omitted and any path
  // component is ignored."
  if (!appIdUri->SchemeIs("https")) {
    nsCString facetId;
    rv = principal->GetWebExposedOriginSerialization(facetId);
    return NS_SUCCEEDED(rv) && facetId == aAppId;
  }

  // Same site check
  nsCOMPtr<nsIEffectiveTLDService> tldService =
      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
  if (!tldService) {
    return false;
  }

  nsAutoCString baseDomainCaller;
  rv = tldService->GetBaseDomain(callerUri, 0, baseDomainCaller);
  if (NS_FAILED(rv)) {
    return false;
  }

  nsAutoCString baseDomainAppId;
  rv = tldService->GetBaseDomain(appIdUri, 0, baseDomainAppId);
  if (NS_FAILED(rv)) {
    return false;
  }

  if (baseDomainCaller == baseDomainAppId) {
    return true;
  }

  // Exceptions for Google Accounts from Bug 1436078. These were supposed to be
  // temporary, but users reported breakage when we tried to remove them (Bug
  // 1822703). We will need to keep them indefinitely.
  if (baseDomainCaller.EqualsLiteral("google.com") &&
      (aAppId.Equals("https://www.gstatic.com/securitykey/origins.json"_ns) ||
       aAppId.Equals(
           "https://www.gstatic.com/securitykey/a/google.com/origins.json"_ns))) {
    return true;
  }

  return false;
}

nsresult DefaultRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
                     /* out */ nsACString& aRpId) {
  // [https://w3c.github.io/webauthn/#rp-id]
  // "By default, the RP ID for a WebAuthn operation is set to the caller's
  // origin's effective domain."
  auto* basePrin = BasePrincipal::Cast(aPrincipal);
  nsCOMPtr<nsIURI> uri;
  if (NS_FAILED(basePrin->GetURI(getter_AddRefs(uri)))) {
    return NS_ERROR_FAILURE;
  }
  return uri->GetAsciiHost(aRpId);
}

bool IsWebAuthnAllowedInDocument(const nsCOMPtr<Document>& aDoc) {
  MOZ_ASSERT(aDoc);
  return aDoc->IsHTMLOrXHTML();
}

bool IsWebAuthnAllowedInContext(WindowGlobalParent* aContext) {
  nsIPrincipal* principal = aContext->DocumentPrincipal();
  MOZ_ASSERT(principal);

  if (principal->GetIsNullPrincipal()) {
    return false;
  }

  if (principal->GetIsIpAddress()) {
    return false;
  }
  // This next test is not strictly necessary since CredentialsContainer is
  // [SecureContext] in our webidl.
  if (!principal->GetIsOriginPotentiallyTrustworthy()) {
    return false;
  }

  if (principal->GetIsLoopbackHost()) {
    return true;
  }

  if (StaticPrefs::security_webauthn_allow_with_certificate_override()) {
    return true;
  }

  WindowGlobalParent* windowContext = aContext;
  while (windowContext) {
    if (nsCOMPtr<nsIChannel> chan = windowContext->GetDocumentChannel()) {
      nsCOMPtr<nsITransportSecurityInfo> securityInfo;
      nsresult rv = chan->GetSecurityInfo(getter_AddRefs(securityInfo));
      if (NS_SUCCEEDED(rv) && securityInfo &&
          !IsWebAuthnAllowedForTransportSecurityInfo(securityInfo)) {
        return false;
      }
    }
    windowContext = windowContext->GetParentWindowContext();
  }

  return true;
}

bool IsWebAuthnAllowedForTransportSecurityInfo(
    nsITransportSecurityInfo* aSecurityInfo) {
  nsITransportSecurityInfo::OverridableErrorCategory overridableErrorCategory;
  if (!aSecurityInfo || NS_FAILED(aSecurityInfo->GetOverridableErrorCategory(
                            &overridableErrorCategory))) {
    return false;
  }

  switch (overridableErrorCategory) {
    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET:
      return true;
    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME:
      return true;
    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST:
      return false;
    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN:
      return false;
    default:
      return false;
  }
}

// The "is a registrable domain suffix of or is equal to" condition is defined
// in https://html.spec.whatwg.org/multipage/browsers.html#dom-document-domain
// as a subroutine of the document.domain setter, and it is exposed in XUL as
// the Document::IsValidDomain function. Unfortunately Document::IsValidDomain
// takes URIs, and to support web extensions we need to compare directly with
// ASCII host names.
bool IsRegistrableDomainSuffixOfOrEqualTo(const nsACString& aQuery,
                                          const nsACString& aReference) {
  nsCOMPtr<nsIEffectiveTLDService> tldService =
      mozilla::components::EffectiveTLD::Service();
  if (!tldService) {
    return false;
  }

  // exclude values that are themselves in the public suffix list.
  nsAutoCString queryPublicSuffix;
  nsresult rv =
      tldService->GetKnownPublicSuffixFromHost(aQuery, queryPublicSuffix);
  if (NS_FAILED(rv) || aQuery == queryPublicSuffix) {
    return false;
  }

  if (aQuery.Equals(aReference)) {
    return true;
  }

  if (aQuery.Length() > aReference.Length() &&
      StringEndsWith(aQuery, aReference) &&
      aQuery.CharAt(aQuery.Length() - aReference.Length() - 1) == '.') {
    // The query string ends with '.' followed by the reference string. It is a
    // registrable domain suffix of the reference string if and only if its base
    // domain is entirely contained in `aReference`.
    nsAutoCString queryBaseDomain;
    rv = tldService->GetBaseDomainFromHost(aQuery, 0, queryBaseDomain);
    if (NS_FAILED(rv)) {
      return false;
    }

    return aReference.Length() >= queryBaseDomain.Length();
  }

  return false;
}

static bool OriginCanClaimRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
                               const nsACString& aRpId) {
  // This checks two of the conditions defined in
  // https://w3c.github.io/webauthn/#rp-id, namely that the RP ID value is
  //  (1) "a valid domain string", and
  //  (2) "a registrable domain suffix of or is equal to the caller's origin's
  //      effective domain"
  //
  // The specification also requires
  //  (3) Either "the origin's scheme is https" or "the origin's host is
  //      localhost and its scheme is http".
  // We relax this third condition slightly and expose WebAuthn in all secure
  // contexts (https://www.w3.org/TR/secure-contexts/).

  // Condition (1)
  nsAutoCString normalizedRpId;
  nsresult rv = NS_DomainToASCII(aRpId, normalizedRpId);
  if (NS_FAILED(rv)) {
    return false;
  }
  if (normalizedRpId != aRpId) {
    return false;
  }

  // Condition (2)
  auto* basePrin = BasePrincipal::Cast(aPrincipal);
  nsAutoCString current;
  if (NS_FAILED(basePrin->GetAsciiHost(current))) {
    return false;
  }
  if (!IsRegistrableDomainSuffixOfOrEqualTo(current, aRpId)) {
    return false;
  }

  // Condition (3)
  if (!aPrincipal->GetIsOriginPotentiallyTrustworthy()) {
    return false;
  }

  return true;
}

static bool ExtensionCanClaimRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
                                  const nsACString& aRpId) {
  // The conditions here are largely the same as in OriginCanClaimRpId. However,
  // rather than making direct comparisons with the caller's origin, we check
  // whether the extension has host permissions for a suitable origin via
  // WebExtensionPolicy::CanAccessURI (which checks the restricted URI list).
  //
  // The conditions that we enforce are:
  // (1) The RP ID must be a valid domain string.
  // (2) The RP ID must not be a single-label non-loopback hostname or a known
  //     public suffix.
  // (3) The extension must have host permissions for either
  //     - https://<aRpId>, or
  //     - http://<aRpId>, if aRpId is a loopback hostname (per
  //     mozilla::net::IsLoopbackHostname).

  // Condition (1)
  nsAutoCString normalizedRpId;
  nsresult rv = NS_DomainToASCII(aRpId, normalizedRpId);
  if (NS_FAILED(rv)) {
    return false;
  }
  if (normalizedRpId != aRpId) {
    return false;
  }

  // Condition (2)
  nsCOMPtr<nsIEffectiveTLDService> tldService =
      mozilla::components::EffectiveTLD::Service();
  if (!tldService) {
    return false;
  }

  nsAutoCString rpIdPublicSuffix;
  if (NS_FAILED(
          tldService->GetKnownPublicSuffixFromHost(aRpId, rpIdPublicSuffix))) {
    return false;
  }

  if (aRpId == rpIdPublicSuffix) {
    return false;
  }

  // Exclude single-label non-loopback hostnames.
  int32_t firstDot = aRpId.FindChar('.');
  if ((firstDot < 0 || firstDot == (int32_t)aRpId.Length() - 1) &&
      !mozilla::net::IsLoopbackHostname(aRpId)) {
    return false;
  }

  // Condition (3)
  auto* basePrin = BasePrincipal::Cast(aPrincipal);
  MOZ_ASSERT(basePrin->AddonPolicy());

  nsAutoCString httpsUriSpec("https://"_ns);
  httpsUriSpec.Append(aRpId);
  httpsUriSpec.AppendLiteral("/");
  nsCOMPtr<nsIURI> uri;
  rv = NS_NewURI(getter_AddRefs(uri), httpsUriSpec);
  if (NS_FAILED(rv)) {
    return false;
  }

  if (basePrin->AddonPolicy()->CanAccessURI(uri.get())) {
    return true;
  }

  if (mozilla::net::IsLoopbackHostname(aRpId)) {
    nsCOMPtr<nsIURI> httpUri;
    rv = NS_MutateURI(uri).SetScheme("http"_ns).Finalize(
        getter_AddRefs(httpUri));
    if (NS_SUCCEEDED(rv) &&
        basePrin->AddonPolicy()->CanAccessURI(httpUri.get())) {
      return true;
    }
  }

  return false;
}

bool IsValidRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
                 const nsACString& aRpId) {
  auto* basePrincipal = BasePrincipal::Cast(aPrincipal);
  bool reqIsFromExtension = !!basePrincipal->AddonPolicy();
  if (reqIsFromExtension) {
    return ExtensionCanClaimRpId(aPrincipal, aRpId);
  }
  return OriginCanClaimRpId(aPrincipal, aRpId);
}

nsresult GetWebAuthnClientDataOrigin(nsIPrincipal* aPrincipal,
                                     /* out */ nsACString& aOrigin) {
  auto* basePrincipal = BasePrincipal::Cast(aPrincipal);

  bool reqIsFromExtension = !!basePrincipal->AddonPolicy();
  if (reqIsFromExtension) {
    nsAutoCString extensionId;
    basePrincipal->AddonPolicy()->Id()->ToUTF8String(extensionId);

    nsTArray<uint8_t> hashedId;
    nsresult rv = HashCString(extensionId, hashedId);
    if (NS_FAILED(rv)) {
      return rv;
    }

    // The extension ID may not be a valid host string. So we use the sha256
    // hash of the extension ID encoded in base 16 with the digits [a-p].
    aOrigin.Assign("moz-extension://");
    for (uint8_t byte : hashedId) {
      aOrigin.Append(char('a' + ((byte >> 4) & 0x0F)));
      aOrigin.Append(char('a' + (byte & 0x0F)));
    }

    return NS_OK;
  }

  return basePrincipal->GetWebExposedOriginSerialization(aOrigin);
}

static nsresult HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
                            /* out */ nsTArray<uint8_t>& aOut) {
  MOZ_ASSERT(aHashService);

  nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = aHashService->Update(
      reinterpret_cast<const uint8_t*>(aIn.BeginReading()), aIn.Length());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsAutoCString fullHash;
  // Passing false below means we will get a binary result rather than a
  // base64-encoded string.
  rv = aHashService->Finish(false, fullHash);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  aOut.Clear();
  aOut.AppendElements(reinterpret_cast<uint8_t const*>(fullHash.BeginReading()),
                      fullHash.Length());

  return NS_OK;
}

nsresult HashCString(const nsACString& aIn, /* out */ nsTArray<uint8_t>& aOut) {
  nsresult srv;
  nsCOMPtr<nsICryptoHash> hashService =
      do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
  if (NS_FAILED(srv)) {
    return srv;
  }

  srv = HashCString(hashService, aIn, aOut);
  if (NS_WARN_IF(NS_FAILED(srv))) {
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

uint32_t WebAuthnTimeout(const Optional<uint32_t>& aTimeout) {
  uint32_t adjustedTimeout = 30000;
  if (aTimeout.WasPassed()) {
    adjustedTimeout = aTimeout.Value();
    adjustedTimeout = std::max(15000u, adjustedTimeout);
    adjustedTimeout = std::min(120000u, adjustedTimeout);
  }
  return adjustedTimeout;
}

static nsresult SerializeWebAuthnData(
    const OwningArrayBufferViewOrArrayBuffer& aData, nsString& aOut) {
  return ProcessTypedArrays(
      aData, [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
        nsAutoCString result;
        nsresult rv = mozilla::Base64URLEncode(
            aData.Length(), aData.Elements(),
            Base64URLEncodePaddingPolicy::Omit, result);
        if (NS_SUCCEEDED(rv)) {
          aOut.Assign(NS_ConvertUTF8toUTF16(result));
        }
        return rv;
      });
}

nsresult SerializeWebAuthnCreationOptions(
    JSContext* aCx, const nsString& aRpId,
    const PublicKeyCredentialCreationOptions& aOptions, nsString& aOut) {
  nsresult rv;
  PublicKeyCredentialCreationOptionsJSON json;

  json.mRp.mId.Construct(aRpId);

  json.mRp.mName.Assign(aOptions.mRp.mName);

  json.mUser.mName.Assign(aOptions.mUser.mName);

  rv = SerializeWebAuthnData(aOptions.mUser.mId, json.mUser.mId);
  NS_ENSURE_SUCCESS(rv, rv);

  json.mUser.mDisplayName.Assign(aOptions.mUser.mDisplayName);

  rv = SerializeWebAuthnData(aOptions.mChallenge, json.mChallenge);
  NS_ENSURE_SUCCESS(rv, rv);

  json.mPubKeyCredParams = aOptions.mPubKeyCredParams;

  json.mTimeout.Construct(WebAuthnTimeout(aOptions.mTimeout));

  for (const auto& excludeCredential : aOptions.mExcludeCredentials) {
    PublicKeyCredentialDescriptorJSON* excludeCredentialJSON =
        json.mExcludeCredentials.AppendElement(fallible);
    if (!excludeCredentialJSON) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    excludeCredentialJSON->mType = excludeCredential.mType;
    rv = SerializeWebAuthnData(excludeCredential.mId,
                               excludeCredentialJSON->mId);
    NS_ENSURE_SUCCESS(rv, rv);

    if (excludeCredential.mTransports.WasPassed()) {
      excludeCredentialJSON->mTransports.Construct(
          excludeCredential.mTransports.Value());
    }
  }

  json.mAuthenticatorSelection.Construct(aOptions.mAuthenticatorSelection);

  json.mHints = aOptions.mHints;

  json.mAttestation = aOptions.mAttestation;

  AuthenticationExtensionsClientInputsJSON& extensionsJSON =
      json.mExtensions.Construct();

  if (aOptions.mExtensions.mAppid.WasPassed()) {
    extensionsJSON.mAppid.Construct(aOptions.mExtensions.mAppid.Value());
  }

  if (aOptions.mExtensions.mCredentialProtectionPolicy.WasPassed()) {
    extensionsJSON.mCredentialProtectionPolicy.Construct(
        aOptions.mExtensions.mCredentialProtectionPolicy.Value());
  }

  if (aOptions.mExtensions.mEnforceCredentialProtectionPolicy.WasPassed()) {
    extensionsJSON.mEnforceCredentialProtectionPolicy.Construct(
        aOptions.mExtensions.mEnforceCredentialProtectionPolicy.Value());
  }

  if (aOptions.mExtensions.mCredProps.WasPassed()) {
    extensionsJSON.mCredProps.Construct(
        aOptions.mExtensions.mCredProps.Value());
  }

  if (aOptions.mExtensions.mHmacCreateSecret.WasPassed()) {
    extensionsJSON.mHmacCreateSecret.Construct(
        aOptions.mExtensions.mHmacCreateSecret.Value());
  }

  if (aOptions.mExtensions.mMinPinLength.WasPassed()) {
    extensionsJSON.mMinPinLength.Construct(
        aOptions.mExtensions.mMinPinLength.Value());
  }

  if (aOptions.mExtensions.mLargeBlob.WasPassed()) {
    const AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
        aOptions.mExtensions.mLargeBlob.Value();
    AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
        extensionsJSON.mLargeBlob.Construct();

    if (largeBlobInputs.mSupport.WasPassed()) {
      largeBlobInputsJSON.mSupport.Construct(largeBlobInputs.mSupport.Value());
    }

    if (largeBlobInputs.mRead.WasPassed()) {
      largeBlobInputsJSON.mRead.Construct(largeBlobInputs.mRead.Value());
    }

    if (largeBlobInputs.mWrite.WasPassed()) {
      nsString write;
      rv = SerializeWebAuthnData(largeBlobInputs.mWrite.Value(), write);
      NS_ENSURE_SUCCESS(rv, rv);
      largeBlobInputsJSON.mWrite.Construct(write);
    }
  }

  if (aOptions.mExtensions.mPrf.WasPassed()) {
    const AuthenticationExtensionsPRFInputs& prfInputs =
        aOptions.mExtensions.mPrf.Value();
    AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
        extensionsJSON.mPrf.Construct();

    if (prfInputs.mEval.WasPassed()) {
      AuthenticationExtensionsPRFValuesJSON& evalJSON =
          prfInputsJSON.mEval.Construct();
      rv = SerializeWebAuthnData(prfInputs.mEval.Value().mFirst,
                                 evalJSON.mFirst);
      NS_ENSURE_SUCCESS(rv, rv);

      if (prfInputs.mEval.Value().mSecond.WasPassed()) {
        nsString second;
        rv = SerializeWebAuthnData(prfInputs.mEval.Value().mSecond.Value(),
                                   second);
        NS_ENSURE_SUCCESS(rv, rv);
        evalJSON.mSecond.Construct(second);
      }
    }

    if (prfInputs.mEvalByCredential.WasPassed()) {
      auto& evalByCredentialJSON = prfInputsJSON.mEvalByCredential.Construct();
      for (const auto& entry : prfInputs.mEvalByCredential.Value().Entries()) {
        auto* jsonEntry =
            evalByCredentialJSON.Entries().AppendElement(fallible);
        if (!jsonEntry) {
          return NS_ERROR_OUT_OF_MEMORY;
        }

        jsonEntry->mKey = entry.mKey;
        AuthenticationExtensionsPRFValuesJSON& valuesJSON = jsonEntry->mValue;

        rv = SerializeWebAuthnData(entry.mValue.mFirst, valuesJSON.mFirst);
        NS_ENSURE_SUCCESS(rv, rv);

        if (entry.mValue.mSecond.WasPassed()) {
          nsString second;
          rv = SerializeWebAuthnData(entry.mValue.mSecond.Value(), second);
          NS_ENSURE_SUCCESS(rv, rv);
          valuesJSON.mSecond.Construct(second);
        }
      }
    }
  }

  JS::Rooted<JS::Value> value(aCx);
  if (!ToJSValue(aCx, json, &value)) {
    // Callers reject the promise with a fixed error code and have no way to
    // forward a pending exception, so we must not leave one set.
    JS_ClearPendingException(aCx);
    return NS_ERROR_FAILURE;
  }

  nsAutoString jsonString;
  if (!nsContentUtils::StringifyJSON(aCx, value, jsonString,
                                     UndefinedIsNullStringLiteral)) {
    JS_ClearPendingException(aCx);
    return NS_ERROR_FAILURE;
  }

  aOut = std::move(jsonString);
  return NS_OK;
}

nsresult SerializeWebAuthnRequestOptions(
    JSContext* aCx, const nsString& aRpId,
    const PublicKeyCredentialRequestOptions& aOptions, nsString& aOut) {
  nsresult rv;
  PublicKeyCredentialRequestOptionsJSON json;

  rv = SerializeWebAuthnData(aOptions.mChallenge, json.mChallenge);
  NS_ENSURE_SUCCESS(rv, rv);

  json.mTimeout.Construct(WebAuthnTimeout(aOptions.mTimeout));

  json.mRpId.Construct(aRpId);

  for (const auto& allowCredential : aOptions.mAllowCredentials) {
    PublicKeyCredentialDescriptorJSON* allowCredentialJSON =
        json.mAllowCredentials.AppendElement(fallible);
    if (!allowCredentialJSON) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    allowCredentialJSON->mType = allowCredential.mType;
    rv = SerializeWebAuthnData(allowCredential.mId, allowCredentialJSON->mId);
    NS_ENSURE_SUCCESS(rv, rv);

    if (allowCredential.mTransports.WasPassed()) {
      allowCredentialJSON->mTransports.Construct(
          allowCredential.mTransports.Value());
    }
  }

  json.mUserVerification = aOptions.mUserVerification;

  json.mHints = aOptions.mHints;

  AuthenticationExtensionsClientInputsJSON& extensionsJSON =
      json.mExtensions.Construct();

  if (aOptions.mExtensions.mAppid.WasPassed()) {
    extensionsJSON.mAppid.Construct(aOptions.mExtensions.mAppid.Value());
  }

  if (aOptions.mExtensions.mCredProps.WasPassed()) {
    extensionsJSON.mCredProps.Construct(
        aOptions.mExtensions.mCredProps.Value());
  }

  if (aOptions.mExtensions.mHmacCreateSecret.WasPassed()) {
    extensionsJSON.mHmacCreateSecret.Construct(
        aOptions.mExtensions.mHmacCreateSecret.Value());
  }

  if (aOptions.mExtensions.mMinPinLength.WasPassed()) {
    extensionsJSON.mMinPinLength.Construct(
        aOptions.mExtensions.mMinPinLength.Value());
  }

  if (aOptions.mExtensions.mLargeBlob.WasPassed()) {
    const AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
        aOptions.mExtensions.mLargeBlob.Value();
    AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
        extensionsJSON.mLargeBlob.Construct();

    if (largeBlobInputs.mSupport.WasPassed()) {
      largeBlobInputsJSON.mSupport.Construct(largeBlobInputs.mSupport.Value());
    }

    if (largeBlobInputs.mRead.WasPassed()) {
      largeBlobInputsJSON.mRead.Construct(largeBlobInputs.mRead.Value());
    }

    if (largeBlobInputs.mWrite.WasPassed()) {
      nsString write;
      rv = SerializeWebAuthnData(largeBlobInputs.mWrite.Value(), write);
      NS_ENSURE_SUCCESS(rv, rv);
      largeBlobInputsJSON.mWrite.Construct(write);
    }
  }

  if (aOptions.mExtensions.mPrf.WasPassed()) {
    const AuthenticationExtensionsPRFInputs& prfInputs =
        aOptions.mExtensions.mPrf.Value();
    AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
        extensionsJSON.mPrf.Construct();

    if (prfInputs.mEval.WasPassed()) {
      AuthenticationExtensionsPRFValuesJSON& evalJSON =
          prfInputsJSON.mEval.Construct();
      rv = SerializeWebAuthnData(prfInputs.mEval.Value().mFirst,
                                 evalJSON.mFirst);
      NS_ENSURE_SUCCESS(rv, rv);

      if (prfInputs.mEval.Value().mSecond.WasPassed()) {
        nsString second;
        rv = SerializeWebAuthnData(prfInputs.mEval.Value().mSecond.Value(),
                                   second);
        NS_ENSURE_SUCCESS(rv, rv);
        evalJSON.mSecond.Construct(second);
      }
    }

    if (prfInputs.mEvalByCredential.WasPassed()) {
      auto& evalByCredentialJSON = prfInputsJSON.mEvalByCredential.Construct();
      for (const auto& entry : prfInputs.mEvalByCredential.Value().Entries()) {
        auto* jsonEntry =
            evalByCredentialJSON.Entries().AppendElement(fallible);
        if (!jsonEntry) {
          return NS_ERROR_OUT_OF_MEMORY;
        }

        jsonEntry->mKey = entry.mKey;
        AuthenticationExtensionsPRFValuesJSON& valuesJSON = jsonEntry->mValue;

        rv = SerializeWebAuthnData(entry.mValue.mFirst, valuesJSON.mFirst);
        NS_ENSURE_SUCCESS(rv, rv);

        if (entry.mValue.mSecond.WasPassed()) {
          nsString second;
          rv = SerializeWebAuthnData(entry.mValue.mSecond.Value(), second);
          NS_ENSURE_SUCCESS(rv, rv);
          valuesJSON.mSecond.Construct(second);
        }
      }
    }
  }

  JS::Rooted<JS::Value> value(aCx);
  if (!ToJSValue(aCx, json, &value)) {
    // Callers reject the promise with a fixed error code and have no way to
    // forward a pending exception, so we must not leave one set.
    JS_ClearPendingException(aCx);
    return NS_ERROR_FAILURE;
  }

  nsAutoString jsonString;
  if (!nsContentUtils::StringifyJSON(aCx, value, jsonString,
                                     UndefinedIsNullStringLiteral)) {
    JS_ClearPendingException(aCx);
    return NS_ERROR_FAILURE;
  }

  aOut = std::move(jsonString);
  return NS_OK;
}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=85 H=96 G=90

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002