tor-browser

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

PublicKeyCredential.cpp (29946B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/PublicKeyCredential.h"
      8 
      9 #include "mozilla/Base64.h"
     10 #include "mozilla/HoldDropJSObjects.h"
     11 #include "mozilla/Preferences.h"
     12 #include "mozilla/StaticPrefs_security.h"
     13 #include "mozilla/dom/AuthenticatorResponse.h"
     14 #include "mozilla/dom/ChromeUtils.h"
     15 #include "mozilla/dom/CredentialsContainer.h"
     16 #include "mozilla/dom/Navigator.h"
     17 #include "mozilla/dom/Promise.h"
     18 #include "mozilla/dom/WebAuthenticationBinding.h"
     19 #include "mozilla/dom/WebAuthnHandler.h"
     20 #include "nsCycleCollectionParticipant.h"
     21 
     22 #ifdef MOZ_WIDGET_ANDROID
     23 #  include "mozilla/MozPromise.h"
     24 #  include "mozilla/java/GeckoResultNatives.h"
     25 #  include "mozilla/java/WebAuthnTokenManagerWrappers.h"
     26 #endif
     27 
     28 namespace mozilla::dom {
     29 
     30 NS_IMPL_CYCLE_COLLECTION_CLASS(PublicKeyCredential)
     31 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PublicKeyCredential, Credential)
     32  tmp->mRawIdCachedObj = nullptr;
     33 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     34 
     35 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
     36  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     37  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRawIdCachedObj)
     38 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     39 
     40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PublicKeyCredential,
     41                                                  Credential)
     42  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttestationResponse)
     43  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssertionResponse)
     44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     45 
     46 NS_IMPL_ADDREF_INHERITED(PublicKeyCredential, Credential)
     47 NS_IMPL_RELEASE_INHERITED(PublicKeyCredential, Credential)
     48 
     49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PublicKeyCredential)
     50 NS_INTERFACE_MAP_END_INHERITING(Credential)
     51 
     52 PublicKeyCredential::PublicKeyCredential(nsPIDOMWindowInner* aParent)
     53    : Credential(aParent), mRawIdCachedObj(nullptr) {
     54  mozilla::HoldJSObjects(this);
     55 }
     56 
     57 PublicKeyCredential::~PublicKeyCredential() { mozilla::DropJSObjects(this); }
     58 
     59 JSObject* PublicKeyCredential::WrapObject(JSContext* aCx,
     60                                          JS::Handle<JSObject*> aGivenProto) {
     61  return PublicKeyCredential_Binding::Wrap(aCx, this, aGivenProto);
     62 }
     63 
     64 void PublicKeyCredential::GetRawId(JSContext* aCx,
     65                                   JS::MutableHandle<JSObject*> aValue,
     66                                   ErrorResult& aRv) {
     67  if (!mRawIdCachedObj) {
     68    mRawIdCachedObj = ArrayBuffer::Create(aCx, mRawId, aRv);
     69    if (aRv.Failed()) {
     70      return;
     71    }
     72  }
     73  aValue.set(mRawIdCachedObj);
     74 }
     75 
     76 void PublicKeyCredential::GetAuthenticatorAttachment(
     77    DOMString& aAuthenticatorAttachment) {
     78  if (mAuthenticatorAttachment.isSome()) {
     79    aAuthenticatorAttachment.SetKnownLiveString(mAuthenticatorAttachment.ref());
     80  } else {
     81    aAuthenticatorAttachment.SetNull();
     82  }
     83 }
     84 
     85 already_AddRefed<AuthenticatorResponse> PublicKeyCredential::Response() const {
     86  if (mAttestationResponse) {
     87    return do_AddRef(mAttestationResponse);
     88  }
     89  if (mAssertionResponse) {
     90    return do_AddRef(mAssertionResponse);
     91  }
     92  return nullptr;
     93 }
     94 
     95 void PublicKeyCredential::SetRawId(const nsTArray<uint8_t>& aBuffer) {
     96  mRawId.Assign(aBuffer);
     97 }
     98 
     99 void PublicKeyCredential::SetAuthenticatorAttachment(
    100    const Maybe<nsString>& aAuthenticatorAttachment) {
    101  mAuthenticatorAttachment = aAuthenticatorAttachment;
    102 }
    103 
    104 void PublicKeyCredential::SetAttestationResponse(
    105    const RefPtr<AuthenticatorAttestationResponse>& aAttestationResponse) {
    106  mAttestationResponse = aAttestationResponse;
    107 }
    108 
    109 void PublicKeyCredential::SetAssertionResponse(
    110    const RefPtr<AuthenticatorAssertionResponse>& aAssertionResponse) {
    111  mAssertionResponse = aAssertionResponse;
    112 }
    113 
    114 /* static */
    115 already_AddRefed<Promise>
    116 PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(
    117    GlobalObject& aGlobal, ErrorResult& aError) {
    118  nsCOMPtr<nsPIDOMWindowInner> window =
    119      do_QueryInterface(aGlobal.GetAsSupports());
    120  if (!window) {
    121    aError.Throw(NS_ERROR_FAILURE);
    122    return nullptr;
    123  }
    124 
    125  RefPtr<WebAuthnHandler> handler =
    126      window->Navigator()->Credentials()->GetWebAuthnHandler();
    127  return handler->IsUVPAA(aGlobal, aError);
    128 }
    129 
    130 /* static */
    131 already_AddRefed<Promise> PublicKeyCredential::GetClientCapabilities(
    132    GlobalObject& aGlobal, ErrorResult& aError) {
    133  RefPtr<Promise> promise =
    134      Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
    135  if (aError.Failed()) {
    136    return nullptr;
    137  }
    138 
    139  // From https://w3c.github.io/webauthn/#sctn-getClientCapabilities:
    140  //    Keys in PublicKeyCredentialClientCapabilities MUST be sorted in
    141  //    ascending lexicographical order. The set of keys SHOULD contain the set
    142  //    of enumeration values of ClientCapability
    143  //    (https://w3c.github.io/webauthn/#enumdef-clientcapability) but the
    144  //    client MAY omit keys as it deems necessary. [...] The set of keys SHOULD
    145  //    also contain a key for each extension implemented by the client, where
    146  //    the key is formed by prefixing the string 'extension:' to the extension
    147  //    identifier. The associated value for each implemented extension SHOULD
    148  //    be true.
    149  //
    150  Record<nsString, bool> capabilities;
    151 
    152  auto entry = capabilities.Entries().AppendElement();
    153  entry->mKey = u"conditionalCreate"_ns;
    154  entry->mValue = false;
    155 
    156  entry = capabilities.Entries().AppendElement();
    157  entry->mKey = u"conditionalGet"_ns;
    158 #if defined(MOZ_WIDGET_ANDROID)
    159  entry->mValue = false;
    160 #else
    161  entry->mValue = StaticPrefs::security_webauthn_enable_conditional_mediation();
    162 #endif
    163 
    164  entry = capabilities.Entries().AppendElement();
    165  entry->mKey = u"extension:appid"_ns;
    166  entry->mValue = true;
    167 
    168  // Bug 1570429: support the appidExclude extension.
    169  // entry = capabilities.Entries().AppendElement();
    170  // entry->mKey = u"extension:appidExclude"_ns;
    171  // entry->mValue = true;
    172 
    173  // Bug 1844448: support the credBlob extension.
    174  // entry = capabilities.Entries().AppendElement();
    175  // entry->mKey = u"extension:credBlob"_ns;
    176  // entry->mValue = true;
    177 
    178  entry = capabilities.Entries().AppendElement();
    179  entry->mKey = u"extension:credProps"_ns;
    180  entry->mValue = true;
    181 
    182  entry = capabilities.Entries().AppendElement();
    183  entry->mKey = u"extension:credentialProtectionPolicy"_ns;
    184 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
    185  entry->mValue = false;
    186 #else
    187  entry->mValue = true;
    188 #endif
    189 
    190  entry = capabilities.Entries().AppendElement();
    191  entry->mKey = u"extension:enforceCredentialProtectionPolicy"_ns;
    192 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
    193  entry->mValue = false;
    194 #else
    195  entry->mValue = true;
    196 #endif
    197 
    198  // Bug 1844448: support the credBlob extension.
    199  // entry = capabilities.Entries().AppendElement();
    200  // entry->mKey = u"extension:getCredBlob"_ns;
    201  // entry->mValue = true;
    202 
    203  entry = capabilities.Entries().AppendElement();
    204  entry->mKey = u"extension:hmacCreateSecret"_ns;
    205  entry->mValue = true;
    206 
    207  entry = capabilities.Entries().AppendElement();
    208  entry->mKey = u"extension:largeBlob"_ns;
    209 #if defined(XP_MACOSX) || defined(XP_WIN)
    210  entry->mValue = true;
    211 #else
    212  entry->mValue = false;
    213 #endif
    214 
    215  entry = capabilities.Entries().AppendElement();
    216  entry->mKey = u"extension:minPinLength"_ns;
    217  entry->mValue = true;
    218 
    219  entry = capabilities.Entries().AppendElement();
    220  entry->mKey = u"extension:prf"_ns;
    221  entry->mValue = true;
    222 
    223  entry = capabilities.Entries().AppendElement();
    224  entry->mKey = u"hybridTransport"_ns;
    225 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(MOZ_WIDGET_ANDROID)
    226  entry->mValue = true;
    227 #else
    228  entry->mValue = false;
    229 #endif
    230 
    231  entry = capabilities.Entries().AppendElement();
    232  entry->mKey = u"passkeyPlatformAuthenticator"_ns;
    233 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(MOZ_WIDGET_ANDROID)
    234  entry->mValue = true;
    235 #else
    236  entry->mValue = false;
    237 #endif
    238 
    239  entry = capabilities.Entries().AppendElement();
    240  entry->mKey = u"relatedOrigins"_ns;
    241  entry->mValue = false;
    242 
    243  entry = capabilities.Entries().AppendElement();
    244  entry->mKey = u"signalAllAcceptedCredentials"_ns;
    245  entry->mValue = false;
    246 
    247  entry = capabilities.Entries().AppendElement();
    248  entry->mKey = u"signalCurrentUserDetails"_ns;
    249  entry->mValue = false;
    250 
    251  entry = capabilities.Entries().AppendElement();
    252  entry->mKey = u"signalUnknownCredential"_ns;
    253  entry->mValue = false;
    254 
    255  entry = capabilities.Entries().AppendElement();
    256  entry->mKey = u"userVerifyingPlatformAuthenticator"_ns;
    257 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(MOZ_WIDGET_ANDROID)
    258  entry->mValue = true;
    259 #else
    260  entry->mValue = false;
    261 #endif
    262 
    263  promise->MaybeResolve(capabilities);
    264  return promise.forget();
    265 }
    266 
    267 /* static */
    268 already_AddRefed<Promise> PublicKeyCredential::IsConditionalMediationAvailable(
    269    GlobalObject& aGlobal, ErrorResult& aError) {
    270  RefPtr<Promise> promise =
    271      Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
    272  if (aError.Failed()) {
    273    return nullptr;
    274  }
    275 #if defined(MOZ_WIDGET_ANDROID)
    276  promise->MaybeResolve(false);
    277 #else
    278  promise->MaybeResolve(
    279      StaticPrefs::security_webauthn_enable_conditional_mediation());
    280 #endif
    281  return promise.forget();
    282 }
    283 
    284 void PublicKeyCredential::GetClientExtensionResults(
    285    JSContext* cx, AuthenticationExtensionsClientOutputs& aResult) const {
    286  if (mClientExtensionOutputs.mAppid.WasPassed()) {
    287    aResult.mAppid.Construct(mClientExtensionOutputs.mAppid.Value());
    288  }
    289 
    290  if (mClientExtensionOutputs.mCredProps.WasPassed()) {
    291    aResult.mCredProps.Construct(mClientExtensionOutputs.mCredProps.Value());
    292  }
    293 
    294  if (mClientExtensionOutputs.mHmacCreateSecret.WasPassed()) {
    295    aResult.mHmacCreateSecret.Construct(
    296        mClientExtensionOutputs.mHmacCreateSecret.Value());
    297  }
    298 
    299  if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
    300    const AuthenticationExtensionsLargeBlobOutputs& src =
    301        mClientExtensionOutputs.mLargeBlob.Value();
    302    AuthenticationExtensionsLargeBlobOutputs& dest =
    303        aResult.mLargeBlob.Construct();
    304 
    305    if (src.mSupported.WasPassed()) {
    306      dest.mSupported.Construct(src.mSupported.Value());
    307    }
    308 
    309    if (src.mWritten.WasPassed()) {
    310      dest.mWritten.Construct(src.mWritten.Value());
    311    }
    312 
    313    if (mLargeBlobValue.isSome()) {
    314      dest.mBlob.Construct().Init(
    315          TypedArrayCreator<ArrayBuffer>(mLargeBlobValue.ref()).Create(cx));
    316    }
    317  }
    318 
    319  if (mClientExtensionOutputs.mPrf.WasPassed()) {
    320    AuthenticationExtensionsPRFOutputs& dest = aResult.mPrf.Construct();
    321 
    322    if (mClientExtensionOutputs.mPrf.Value().mEnabled.WasPassed()) {
    323      dest.mEnabled.Construct(
    324          mClientExtensionOutputs.mPrf.Value().mEnabled.Value());
    325    }
    326 
    327    if (mPrfResultsFirst.isSome()) {
    328      AuthenticationExtensionsPRFValues& destResults =
    329          dest.mResults.Construct();
    330 
    331      destResults.mFirst.SetAsArrayBuffer().Init(
    332          TypedArrayCreator<ArrayBuffer>(mPrfResultsFirst.ref()).Create(cx));
    333 
    334      if (mPrfResultsSecond.isSome()) {
    335        destResults.mSecond.Construct().SetAsArrayBuffer().Init(
    336            TypedArrayCreator<ArrayBuffer>(mPrfResultsSecond.ref()).Create(cx));
    337      }
    338    }
    339  }
    340 }
    341 
    342 void PublicKeyCredential::ToJSON(JSContext* aCx,
    343                                 JS::MutableHandle<JSObject*> aRetval,
    344                                 ErrorResult& aError) {
    345  JS::Rooted<JS::Value> value(aCx);
    346  if (mAttestationResponse) {
    347    RegistrationResponseJSON json;
    348    GetId(json.mId);
    349    GetId(json.mRawId);
    350    mAttestationResponse->ToJSON(json.mResponse, aError);
    351    if (aError.Failed()) {
    352      return;
    353    }
    354    if (mAuthenticatorAttachment.isSome()) {
    355      json.mAuthenticatorAttachment.Construct();
    356      json.mAuthenticatorAttachment.Value() = mAuthenticatorAttachment.ref();
    357    }
    358    if (mClientExtensionOutputs.mCredProps.WasPassed()) {
    359      json.mClientExtensionResults.mCredProps.Construct(
    360          mClientExtensionOutputs.mCredProps.Value());
    361    }
    362    if (mClientExtensionOutputs.mHmacCreateSecret.WasPassed()) {
    363      json.mClientExtensionResults.mHmacCreateSecret.Construct(
    364          mClientExtensionOutputs.mHmacCreateSecret.Value());
    365    }
    366    if (mClientExtensionOutputs.mPrf.WasPassed()) {
    367      json.mClientExtensionResults.mPrf.Construct();
    368      if (mClientExtensionOutputs.mPrf.Value().mEnabled.WasPassed()) {
    369        json.mClientExtensionResults.mPrf.Value().mEnabled.Construct(
    370            mClientExtensionOutputs.mPrf.Value().mEnabled.Value());
    371      }
    372      if (mPrfResultsFirst.isSome()) {
    373        AuthenticationExtensionsPRFValuesJSON& dest =
    374            json.mClientExtensionResults.mPrf.Value().mResults.Construct();
    375        nsCString prfFirst;
    376        nsresult rv = mozilla::Base64URLEncode(
    377            mPrfResultsFirst->Length(), mPrfResultsFirst->Elements(),
    378            Base64URLEncodePaddingPolicy::Omit, prfFirst);
    379        if (NS_FAILED(rv)) {
    380          aError.ThrowEncodingError(
    381              "could not encode first prf output as urlsafe base64");
    382          return;
    383        }
    384        dest.mFirst.Assign(NS_ConvertUTF8toUTF16(prfFirst));
    385        if (mPrfResultsSecond.isSome()) {
    386          nsCString prfSecond;
    387          nsresult rv = mozilla::Base64URLEncode(
    388              mPrfResultsSecond->Length(), mPrfResultsSecond->Elements(),
    389              Base64URLEncodePaddingPolicy::Omit, prfSecond);
    390          if (NS_FAILED(rv)) {
    391            aError.ThrowEncodingError(
    392                "could not encode second prf output as urlsafe base64");
    393            return;
    394          }
    395          dest.mSecond.Construct(NS_ConvertUTF8toUTF16(prfSecond));
    396        }
    397      }
    398    }
    399    if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
    400      const AuthenticationExtensionsLargeBlobOutputs& src =
    401          mClientExtensionOutputs.mLargeBlob.Value();
    402      AuthenticationExtensionsLargeBlobOutputsJSON& dest =
    403          json.mClientExtensionResults.mLargeBlob.Construct();
    404      if (src.mSupported.WasPassed()) {
    405        dest.mSupported.Construct(src.mSupported.Value());
    406      }
    407    }
    408    json.mType.Assign(u"public-key"_ns);
    409    if (!ToJSValue(aCx, json, &value)) {
    410      aError.StealExceptionFromJSContext(aCx);
    411      return;
    412    }
    413  } else if (mAssertionResponse) {
    414    AuthenticationResponseJSON json;
    415    GetId(json.mId);
    416    GetId(json.mRawId);
    417    mAssertionResponse->ToJSON(json.mResponse, aError);
    418    if (aError.Failed()) {
    419      return;
    420    }
    421    if (mAuthenticatorAttachment.isSome()) {
    422      json.mAuthenticatorAttachment.Construct();
    423      json.mAuthenticatorAttachment.Value() = mAuthenticatorAttachment.ref();
    424    }
    425    if (mClientExtensionOutputs.mAppid.WasPassed()) {
    426      json.mClientExtensionResults.mAppid.Construct(
    427          mClientExtensionOutputs.mAppid.Value());
    428    }
    429    if (mClientExtensionOutputs.mPrf.WasPassed()) {
    430      json.mClientExtensionResults.mPrf.Construct();
    431    }
    432    if (mClientExtensionOutputs.mPrf.WasPassed() && mPrfResultsFirst.isSome()) {
    433      AuthenticationExtensionsPRFValuesJSON& dest =
    434          json.mClientExtensionResults.mPrf.Value().mResults.Construct();
    435      nsCString prfFirst;
    436      nsresult rv = mozilla::Base64URLEncode(
    437          mPrfResultsFirst->Length(), mPrfResultsFirst->Elements(),
    438          Base64URLEncodePaddingPolicy::Omit, prfFirst);
    439      if (NS_FAILED(rv)) {
    440        aError.ThrowEncodingError(
    441            "could not encode first prf output as urlsafe base64");
    442        return;
    443      }
    444      dest.mFirst.Assign(NS_ConvertUTF8toUTF16(prfFirst));
    445      if (mPrfResultsSecond.isSome()) {
    446        nsCString prfSecond;
    447        nsresult rv = mozilla::Base64URLEncode(
    448            mPrfResultsSecond->Length(), mPrfResultsSecond->Elements(),
    449            Base64URLEncodePaddingPolicy::Omit, prfSecond);
    450        if (NS_FAILED(rv)) {
    451          aError.ThrowEncodingError(
    452              "could not encode second prf output as urlsafe base64");
    453          return;
    454        }
    455        dest.mSecond.Construct(NS_ConvertUTF8toUTF16(prfSecond));
    456      }
    457    }
    458    if (mClientExtensionOutputs.mLargeBlob.WasPassed()) {
    459      const AuthenticationExtensionsLargeBlobOutputs& src =
    460          mClientExtensionOutputs.mLargeBlob.Value();
    461      AuthenticationExtensionsLargeBlobOutputsJSON& dest =
    462          json.mClientExtensionResults.mLargeBlob.Construct();
    463      if (src.mWritten.WasPassed()) {
    464        dest.mWritten.Construct(src.mWritten.Value());
    465      }
    466      if (mLargeBlobValue.isSome()) {
    467        nsCString largeBlobB64;
    468        nsresult rv = mozilla::Base64URLEncode(
    469            mLargeBlobValue->Length(), mLargeBlobValue->Elements(),
    470            Base64URLEncodePaddingPolicy::Omit, largeBlobB64);
    471        if (NS_FAILED(rv)) {
    472          aError.ThrowEncodingError(
    473              "could not encode large blob data as urlsafe base64");
    474          return;
    475        }
    476        dest.mBlob.Construct(NS_ConvertUTF8toUTF16(largeBlobB64));
    477      }
    478    }
    479    json.mType.Assign(u"public-key"_ns);
    480    if (!ToJSValue(aCx, json, &value)) {
    481      aError.StealExceptionFromJSContext(aCx);
    482      return;
    483    }
    484  } else {
    485    MOZ_ASSERT_UNREACHABLE(
    486        "either mAttestationResponse or mAssertionResponse should be set");
    487  }
    488  JS::Rooted<JSObject*> result(aCx, &value.toObject());
    489  aRetval.set(result);
    490 }
    491 
    492 void PublicKeyCredential::SetClientExtensionResultAppId(bool aResult) {
    493  mClientExtensionOutputs.mAppid.Construct();
    494  mClientExtensionOutputs.mAppid.Value() = aResult;
    495 }
    496 
    497 void PublicKeyCredential::SetClientExtensionResultCredPropsRk(bool aResult) {
    498  mClientExtensionOutputs.mCredProps.Construct();
    499  mClientExtensionOutputs.mCredProps.Value().mRk.Construct();
    500  mClientExtensionOutputs.mCredProps.Value().mRk.Value() = aResult;
    501 }
    502 
    503 void PublicKeyCredential::SetClientExtensionResultHmacSecret(
    504    bool aHmacCreateSecret) {
    505  mClientExtensionOutputs.mHmacCreateSecret.Construct();
    506  mClientExtensionOutputs.mHmacCreateSecret.Value() = aHmacCreateSecret;
    507 }
    508 
    509 void PublicKeyCredential::InitClientExtensionResultLargeBlob() {
    510  mClientExtensionOutputs.mLargeBlob.Construct();
    511 }
    512 
    513 void PublicKeyCredential::SetClientExtensionResultLargeBlobSupported(
    514    bool aLargeBlobSupported) {
    515  mClientExtensionOutputs.mLargeBlob.Value().mSupported.Construct(
    516      aLargeBlobSupported);
    517 }
    518 
    519 void PublicKeyCredential::SetClientExtensionResultLargeBlobValue(
    520    const nsTArray<uint8_t>& aLargeBlobValue) {
    521  mLargeBlobValue.emplace(aLargeBlobValue.Length());
    522  mLargeBlobValue->Assign(aLargeBlobValue);
    523 }
    524 
    525 void PublicKeyCredential::SetClientExtensionResultLargeBlobWritten(
    526    bool aLargeBlobWritten) {
    527  mClientExtensionOutputs.mLargeBlob.Value().mWritten.Construct(
    528      aLargeBlobWritten);
    529 }
    530 
    531 void PublicKeyCredential::InitClientExtensionResultPrf() {
    532  mClientExtensionOutputs.mPrf.Construct();
    533 }
    534 
    535 void PublicKeyCredential::SetClientExtensionResultPrfEnabled(bool aPrfEnabled) {
    536  mClientExtensionOutputs.mPrf.Value().mEnabled.Construct(aPrfEnabled);
    537 }
    538 
    539 void PublicKeyCredential::SetClientExtensionResultPrfResultsFirst(
    540    const nsTArray<uint8_t>& aPrfResultsFirst) {
    541  mPrfResultsFirst.emplace(32);
    542  mPrfResultsFirst->Assign(aPrfResultsFirst);
    543 }
    544 
    545 void PublicKeyCredential::SetClientExtensionResultPrfResultsSecond(
    546    const nsTArray<uint8_t>& aPrfResultsSecond) {
    547  mPrfResultsSecond.emplace(32);
    548  mPrfResultsSecond->Assign(aPrfResultsSecond);
    549 }
    550 
    551 bool Base64DecodeToArrayBuffer(GlobalObject& aGlobal, const nsAString& aString,
    552                               ArrayBuffer& aArrayBuffer, ErrorResult& aRv) {
    553  JSContext* cx = aGlobal.Context();
    554  JS::Rooted<JSObject*> result(cx);
    555  Base64URLDecodeOptions options;
    556  options.mPadding = Base64URLDecodePadding::Ignore;
    557  mozilla::dom::ChromeUtils::Base64URLDecode(
    558      aGlobal, NS_ConvertUTF16toUTF8(aString), options, &result, aRv);
    559  if (aRv.Failed()) {
    560    return false;
    561  }
    562  return aArrayBuffer.Init(result);
    563 }
    564 
    565 bool DecodeAuthenticationExtensionsPRFValuesJSON(
    566    GlobalObject& aGlobal,
    567    const AuthenticationExtensionsPRFValuesJSON& aBase64Values,
    568    AuthenticationExtensionsPRFValues& aValues, ErrorResult& aRv) {
    569  if (!Base64DecodeToArrayBuffer(aGlobal, aBase64Values.mFirst,
    570                                 aValues.mFirst.SetAsArrayBuffer(), aRv)) {
    571    return false;
    572  }
    573  if (aBase64Values.mSecond.WasPassed()) {
    574    if (!Base64DecodeToArrayBuffer(
    575            aGlobal, aBase64Values.mSecond.Value(),
    576            aValues.mSecond.Construct().SetAsArrayBuffer(), aRv)) {
    577      return false;
    578    }
    579  }
    580  return true;
    581 }
    582 
    583 bool DecodeAuthenticationExtensionsPRFInputsJSON(
    584    GlobalObject& aGlobal,
    585    const AuthenticationExtensionsPRFInputsJSON& aInputsJSON,
    586    AuthenticationExtensionsPRFInputs& aInputs, ErrorResult& aRv) {
    587  if (aInputsJSON.mEval.WasPassed()) {
    588    if (!DecodeAuthenticationExtensionsPRFValuesJSON(
    589            aGlobal, aInputsJSON.mEval.Value(), aInputs.mEval.Construct(),
    590            aRv)) {
    591      return false;
    592    }
    593  }
    594  if (aInputsJSON.mEvalByCredential.WasPassed()) {
    595    const Record<nsString, AuthenticationExtensionsPRFValuesJSON>& recordsJSON =
    596        aInputsJSON.mEvalByCredential.Value();
    597    Record<nsString, AuthenticationExtensionsPRFValues>& records =
    598        aInputs.mEvalByCredential.Construct();
    599    if (!records.Entries().SetCapacity(recordsJSON.Entries().Length(),
    600                                       fallible)) {
    601      return false;
    602    }
    603    for (auto& entryJSON : recordsJSON.Entries()) {
    604      auto entry = records.Entries().AppendElement();
    605      entry->mKey = entryJSON.mKey;
    606      if (!DecodeAuthenticationExtensionsPRFValuesJSON(
    607              aGlobal, entryJSON.mValue, entry->mValue, aRv)) {
    608        return false;
    609      }
    610    }
    611  }
    612  return true;
    613 }
    614 
    615 bool DecodeAuthenticationExtensionsLargeBlobInputsJSON(
    616    GlobalObject& aGlobal,
    617    const AuthenticationExtensionsLargeBlobInputsJSON& aInputsJSON,
    618    AuthenticationExtensionsLargeBlobInputs& aInputs, ErrorResult& aRv) {
    619  if (aInputsJSON.mSupport.WasPassed()) {
    620    aInputs.mSupport.Construct(aInputsJSON.mSupport.Value());
    621  }
    622  if (aInputsJSON.mRead.WasPassed()) {
    623    aInputs.mRead.Construct(aInputsJSON.mRead.Value());
    624  }
    625  if (aInputsJSON.mWrite.WasPassed()) {
    626    if (!Base64DecodeToArrayBuffer(
    627            aGlobal, aInputsJSON.mWrite.Value(),
    628            aInputs.mWrite.Construct().SetAsArrayBuffer(), aRv)) {
    629      return false;
    630    }
    631  }
    632  return true;
    633 }
    634 
    635 void PublicKeyCredential::ParseCreationOptionsFromJSON(
    636    GlobalObject& aGlobal,
    637    const PublicKeyCredentialCreationOptionsJSON& aOptions,
    638    PublicKeyCredentialCreationOptions& aResult, ErrorResult& aRv) {
    639  if (aOptions.mRp.mId.WasPassed()) {
    640    aResult.mRp.mId.Construct(aOptions.mRp.mId.Value());
    641  }
    642  aResult.mRp.mName.Assign(aOptions.mRp.mName);
    643 
    644  aResult.mUser.mName.Assign(aOptions.mUser.mName);
    645  if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mUser.mId,
    646                                 aResult.mUser.mId.SetAsArrayBuffer(), aRv)) {
    647    aRv.ThrowEncodingError("could not decode user ID as urlsafe base64");
    648    return;
    649  }
    650  aResult.mUser.mDisplayName.Assign(aOptions.mUser.mDisplayName);
    651 
    652  if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mChallenge,
    653                                 aResult.mChallenge.SetAsArrayBuffer(), aRv)) {
    654    aRv.ThrowEncodingError("could not decode challenge as urlsafe base64");
    655    return;
    656  }
    657 
    658  aResult.mPubKeyCredParams = aOptions.mPubKeyCredParams;
    659 
    660  if (aOptions.mTimeout.WasPassed()) {
    661    aResult.mTimeout.Construct(aOptions.mTimeout.Value());
    662  }
    663 
    664  for (const auto& excludeCredentialJSON : aOptions.mExcludeCredentials) {
    665    PublicKeyCredentialDescriptor* excludeCredential =
    666        aResult.mExcludeCredentials.AppendElement(fallible);
    667    if (!excludeCredential) {
    668      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    669      return;
    670    }
    671    excludeCredential->mType = excludeCredentialJSON.mType;
    672    if (!Base64DecodeToArrayBuffer(aGlobal, excludeCredentialJSON.mId,
    673                                   excludeCredential->mId.SetAsArrayBuffer(),
    674                                   aRv)) {
    675      aRv.ThrowEncodingError(
    676          "could not decode excluded credential ID as urlsafe base64");
    677      return;
    678    }
    679    if (excludeCredentialJSON.mTransports.WasPassed()) {
    680      excludeCredential->mTransports.Construct(
    681          excludeCredentialJSON.mTransports.Value());
    682    }
    683  }
    684 
    685  if (aOptions.mAuthenticatorSelection.WasPassed()) {
    686    aResult.mAuthenticatorSelection = aOptions.mAuthenticatorSelection.Value();
    687  }
    688 
    689  aResult.mHints = aOptions.mHints;
    690 
    691  aResult.mAttestation = aOptions.mAttestation;
    692 
    693  if (aOptions.mExtensions.WasPassed()) {
    694    const AuthenticationExtensionsClientInputsJSON& extInputsJSON =
    695        aOptions.mExtensions.Value();
    696    AuthenticationExtensionsClientInputs& extInputs = aResult.mExtensions;
    697    if (extInputsJSON.mAppid.WasPassed()) {
    698      extInputs.mAppid.Construct(extInputsJSON.mAppid.Value());
    699    }
    700    if (extInputsJSON.mCredentialProtectionPolicy.WasPassed()) {
    701      extInputs.mCredentialProtectionPolicy.Construct(
    702          extInputsJSON.mCredentialProtectionPolicy.Value());
    703    }
    704    if (extInputsJSON.mEnforceCredentialProtectionPolicy.WasPassed()) {
    705      extInputs.mEnforceCredentialProtectionPolicy.Construct(
    706          extInputsJSON.mEnforceCredentialProtectionPolicy.Value());
    707    }
    708    if (extInputsJSON.mCredProps.WasPassed()) {
    709      extInputs.mCredProps.Construct(extInputsJSON.mCredProps.Value());
    710    }
    711    if (extInputsJSON.mHmacCreateSecret.WasPassed()) {
    712      extInputs.mHmacCreateSecret.Construct(
    713          extInputsJSON.mHmacCreateSecret.Value());
    714    }
    715    if (extInputsJSON.mMinPinLength.WasPassed()) {
    716      extInputs.mMinPinLength.Construct(extInputsJSON.mMinPinLength.Value());
    717    }
    718    if (extInputsJSON.mLargeBlob.WasPassed()) {
    719      const AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
    720          extInputsJSON.mLargeBlob.Value();
    721      AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
    722          aResult.mExtensions.mLargeBlob.Construct();
    723      if (!DecodeAuthenticationExtensionsLargeBlobInputsJSON(
    724              aGlobal, largeBlobInputsJSON, largeBlobInputs, aRv)) {
    725        aRv.ThrowEncodingError(
    726            "could not decode large blob inputs as urlsafe base64");
    727        return;
    728      }
    729    }
    730    if (extInputsJSON.mPrf.WasPassed()) {
    731      const AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
    732          extInputsJSON.mPrf.Value();
    733      AuthenticationExtensionsPRFInputs& prfInputs = extInputs.mPrf.Construct();
    734      if (!DecodeAuthenticationExtensionsPRFInputsJSON(aGlobal, prfInputsJSON,
    735                                                       prfInputs, aRv)) {
    736        aRv.ThrowEncodingError("could not decode prf inputs as urlsafe base64");
    737        return;
    738      }
    739    }
    740  }
    741 }
    742 
    743 void PublicKeyCredential::ParseRequestOptionsFromJSON(
    744    GlobalObject& aGlobal,
    745    const PublicKeyCredentialRequestOptionsJSON& aOptions,
    746    PublicKeyCredentialRequestOptions& aResult, ErrorResult& aRv) {
    747  if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mChallenge,
    748                                 aResult.mChallenge.SetAsArrayBuffer(), aRv)) {
    749    aRv.ThrowEncodingError("could not decode challenge as urlsafe base64");
    750    return;
    751  }
    752 
    753  if (aOptions.mTimeout.WasPassed()) {
    754    aResult.mTimeout.Construct(aOptions.mTimeout.Value());
    755  }
    756 
    757  if (aOptions.mRpId.WasPassed()) {
    758    aResult.mRpId.Construct(aOptions.mRpId.Value());
    759  }
    760 
    761  for (const auto& allowCredentialJSON : aOptions.mAllowCredentials) {
    762    PublicKeyCredentialDescriptor* allowCredential =
    763        aResult.mAllowCredentials.AppendElement(fallible);
    764    if (!allowCredential) {
    765      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    766      return;
    767    }
    768    allowCredential->mType = allowCredentialJSON.mType;
    769    if (!Base64DecodeToArrayBuffer(aGlobal, allowCredentialJSON.mId,
    770                                   allowCredential->mId.SetAsArrayBuffer(),
    771                                   aRv)) {
    772      aRv.ThrowEncodingError(
    773          "could not decode allowed credential ID as urlsafe base64");
    774      return;
    775    }
    776    if (allowCredentialJSON.mTransports.WasPassed()) {
    777      allowCredential->mTransports.Construct(
    778          allowCredentialJSON.mTransports.Value());
    779    }
    780  }
    781 
    782  aResult.mUserVerification = aOptions.mUserVerification;
    783 
    784  aResult.mHints = aOptions.mHints;
    785 
    786  if (aOptions.mExtensions.WasPassed()) {
    787    if (aOptions.mExtensions.Value().mAppid.WasPassed()) {
    788      aResult.mExtensions.mAppid.Construct(
    789          aOptions.mExtensions.Value().mAppid.Value());
    790    }
    791    if (aOptions.mExtensions.Value().mCredProps.WasPassed()) {
    792      aResult.mExtensions.mCredProps.Construct(
    793          aOptions.mExtensions.Value().mCredProps.Value());
    794    }
    795    if (aOptions.mExtensions.Value().mLargeBlob.WasPassed()) {
    796      const AuthenticationExtensionsLargeBlobInputsJSON& largeBlobInputsJSON =
    797          aOptions.mExtensions.Value().mLargeBlob.Value();
    798      AuthenticationExtensionsLargeBlobInputs& largeBlobInputs =
    799          aResult.mExtensions.mLargeBlob.Construct();
    800      if (!DecodeAuthenticationExtensionsLargeBlobInputsJSON(
    801              aGlobal, largeBlobInputsJSON, largeBlobInputs, aRv)) {
    802        aRv.ThrowEncodingError(
    803            "could not decode large blob inputs as urlsafe base64");
    804        return;
    805      }
    806    }
    807    if (aOptions.mExtensions.Value().mMinPinLength.WasPassed()) {
    808      aResult.mExtensions.mMinPinLength.Construct(
    809          aOptions.mExtensions.Value().mMinPinLength.Value());
    810    }
    811    if (aOptions.mExtensions.Value().mPrf.WasPassed()) {
    812      const AuthenticationExtensionsPRFInputsJSON& prfInputsJSON =
    813          aOptions.mExtensions.Value().mPrf.Value();
    814      AuthenticationExtensionsPRFInputs& prfInputs =
    815          aResult.mExtensions.mPrf.Construct();
    816      if (!DecodeAuthenticationExtensionsPRFInputsJSON(aGlobal, prfInputsJSON,
    817                                                       prfInputs, aRv)) {
    818        aRv.ThrowEncodingError("could not decode prf inputs as urlsafe base64");
    819        return;
    820      }
    821    }
    822  }
    823 }
    824 
    825 }  // namespace mozilla::dom