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