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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/dom/U2F.h"
8 #include "mozilla/dom/WebCryptoCommon.h"
9 #include "mozilla/ipc/PBackgroundChild.h"
10 #include "mozilla/ipc/BackgroundChild.h"
11 #include "mozilla/dom/WebAuthnTransactionChild.h"
12 #include "mozilla/dom/WebAuthnUtil.h"
13 #include "nsContentUtils.h"
14 #include "nsICryptoHash.h"
15 #include "nsIEffectiveTLDService.h"
16 #include "nsNetCID.h"
17 #include "nsNetUtil.h"
18 #include "nsURLParsers.h"
19 #include "U2FUtil.h"
20 #include "hasht.h"
21
22 using namespace mozilla::ipc;
23
24 // Forward decl because of nsHTMLDocument.h's complex dependency on
25 // /layout/style
26 class nsHTMLDocument {
27 public:
28 bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
29 const nsACString& aOrigHost);
30 };
31
32 namespace mozilla {
33 namespace dom {
34
35 static mozilla::LazyLogModule gU2FLog("u2fmanager");
36
37 NS_NAMED_LITERAL_STRING(kFinishEnrollment, "navigator.id.finishEnrollment");
38 NS_NAMED_LITERAL_STRING(kGetAssertion, "navigator.id.getAssertion");
39
40 // Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
41 NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
42 "https://www.gstatic.com/securitykey/origins.json");
43 NS_NAMED_LITERAL_STRING(
44 kGoogleAccountsAppId2,
45 "https://www.gstatic.com/securitykey/a/google.com/origins.json");
46
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
48 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
49 NS_INTERFACE_MAP_ENTRY(nsISupports)
50 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
51 NS_INTERFACE_MAP_END
52
53 NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
54 NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
55
56 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
57
58 /***********************************************************************
59 * Utility Functions
60 **********************************************************************/
61
62 static ErrorCode ConvertNSResultToErrorCode(const nsresult& aError) {
63 if (aError == NS_ERROR_DOM_TIMEOUT_ERR) {
64 return ErrorCode::TIMEOUT;
65 }
66 /* Emitted by U2F{Soft,HID}TokenManager when we really mean ineligible */
67 if (aError == NS_ERROR_DOM_INVALID_STATE_ERR) {
68 return ErrorCode::DEVICE_INELIGIBLE;
69 }
70 return ErrorCode::OTHER_ERROR;
71 }
72
AdjustedTimeoutMillis(const Optional<Nullable<int32_t>> & opt_aSeconds)73 static uint32_t AdjustedTimeoutMillis(
74 const Optional<Nullable<int32_t>>& opt_aSeconds) {
75 uint32_t adjustedTimeoutMillis = 30000u;
76 if (opt_aSeconds.WasPassed() && !opt_aSeconds.Value().IsNull()) {
77 adjustedTimeoutMillis = opt_aSeconds.Value().Value() * 1000u;
78 adjustedTimeoutMillis = std::max(15000u, adjustedTimeoutMillis);
79 adjustedTimeoutMillis = std::min(120000u, adjustedTimeoutMillis);
80 }
81 return adjustedTimeoutMillis;
82 }
83
AssembleClientData(const nsAString & aOrigin,const nsAString & aTyp,const nsAString & aChallenge,nsString & aClientData)84 static nsresult AssembleClientData(const nsAString& aOrigin,
85 const nsAString& aTyp,
86 const nsAString& aChallenge,
87 /* out */ nsString& aClientData) {
88 MOZ_ASSERT(NS_IsMainThread());
89 U2FClientData clientDataObject;
90 clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
91 clientDataObject.mChallenge.Construct(aChallenge);
92 clientDataObject.mOrigin.Construct(aOrigin);
93
94 if (NS_WARN_IF(!clientDataObject.ToJSON(aClientData))) {
95 return NS_ERROR_FAILURE;
96 }
97
98 return NS_OK;
99 }
100
RegisteredKeysToScopedCredentialList(const nsAString & aAppId,const nsTArray<RegisteredKey> & aKeys,nsTArray<WebAuthnScopedCredential> & aList)101 static void RegisteredKeysToScopedCredentialList(
102 const nsAString& aAppId, const nsTArray<RegisteredKey>& aKeys,
103 nsTArray<WebAuthnScopedCredential>& aList) {
104 for (const RegisteredKey& key : aKeys) {
105 // Check for required attributes
106 if (!key.mVersion.WasPassed() || !key.mKeyHandle.WasPassed() ||
107 key.mVersion.Value() != kRequiredU2FVersion) {
108 continue;
109 }
110
111 // If this key's mAppId doesn't match the invocation, we can't handle it.
112 if (key.mAppId.WasPassed() && !key.mAppId.Value().Equals(aAppId)) {
113 continue;
114 }
115
116 CryptoBuffer keyHandle;
117 nsresult rv = keyHandle.FromJwkBase64(key.mKeyHandle.Value());
118 if (NS_WARN_IF(NS_FAILED(rv))) {
119 continue;
120 }
121
122 WebAuthnScopedCredential c;
123 c.id() = keyHandle;
124 aList.AppendElement(c);
125 }
126 }
127
BuildTransactionHashes(const nsCString & aRpId,const nsCString & aClientDataJSON,CryptoBuffer & aRpIdHash,CryptoBuffer & aClientDataHash)128 static nsresult BuildTransactionHashes(
129 const nsCString& aRpId, const nsCString& aClientDataJSON,
130 /* out */ CryptoBuffer& aRpIdHash,
131 /* out */ CryptoBuffer& aClientDataHash) {
132 nsresult srv;
133 nsCOMPtr<nsICryptoHash> hashService =
134 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
135 if (NS_FAILED(srv)) {
136 return srv;
137 }
138
139 if (!aRpIdHash.SetLength(SHA256_LENGTH, fallible)) {
140 return NS_ERROR_OUT_OF_MEMORY;
141 }
142 srv = HashCString(hashService, aRpId, aRpIdHash);
143 if (NS_WARN_IF(NS_FAILED(srv))) {
144 return NS_ERROR_FAILURE;
145 }
146
147 if (!aClientDataHash.SetLength(SHA256_LENGTH, fallible)) {
148 return NS_ERROR_OUT_OF_MEMORY;
149 }
150 srv = HashCString(hashService, aClientDataJSON, aClientDataHash);
151 if (NS_WARN_IF(NS_FAILED(srv))) {
152 return NS_ERROR_FAILURE;
153 }
154
155 if (MOZ_LOG_TEST(gU2FLog, LogLevel::Debug)) {
156 nsString base64;
157 Unused << NS_WARN_IF(NS_FAILED(aRpIdHash.ToJwkBase64(base64)));
158
159 MOZ_LOG(gU2FLog, LogLevel::Debug,
160 ("dom::U2FManager::RpID: %s", aRpId.get()));
161
162 MOZ_LOG(gU2FLog, LogLevel::Debug,
163 ("dom::U2FManager::Rp ID Hash (base64): %s",
164 NS_ConvertUTF16toUTF8(base64).get()));
165
166 Unused << NS_WARN_IF(NS_FAILED(aClientDataHash.ToJwkBase64(base64)));
167
168 MOZ_LOG(gU2FLog, LogLevel::Debug,
169 ("dom::U2FManager::Client Data JSON: %s", aClientDataJSON.get()));
170
171 MOZ_LOG(gU2FLog, LogLevel::Debug,
172 ("dom::U2FManager::Client Data Hash (base64): %s",
173 NS_ConvertUTF16toUTF8(base64).get()));
174 }
175
176 return NS_OK;
177 }
178
179 /***********************************************************************
180 * U2F JavaScript API Implementation
181 **********************************************************************/
182
~U2F()183 U2F::~U2F() {
184 MOZ_ASSERT(NS_IsMainThread());
185
186 if (mTransaction.isSome()) {
187 ClearTransaction();
188 }
189
190 if (mChild) {
191 RefPtr<WebAuthnTransactionChild> c;
192 mChild.swap(c);
193 c->Disconnect();
194 }
195 }
196
Init(ErrorResult & aRv)197 void U2F::Init(ErrorResult& aRv) {
198 MOZ_ASSERT(mParent);
199
200 nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
201 MOZ_ASSERT(doc);
202 if (!doc) {
203 aRv.Throw(NS_ERROR_FAILURE);
204 return;
205 }
206
207 nsIPrincipal* principal = doc->NodePrincipal();
208 aRv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
209 if (NS_WARN_IF(aRv.Failed())) {
210 return;
211 }
212
213 if (NS_WARN_IF(mOrigin.IsEmpty())) {
214 aRv.Throw(NS_ERROR_FAILURE);
215 return;
216 }
217 }
218
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)219 /* virtual */ JSObject* U2F::WrapObject(JSContext* aCx,
220 JS::Handle<JSObject*> aGivenProto) {
221 return U2FBinding::Wrap(aCx, this, aGivenProto);
222 }
223
224 template <typename T, typename C>
ExecuteCallback(T & aResp,nsMainThreadPtrHandle<C> & aCb)225 void U2F::ExecuteCallback(T& aResp, nsMainThreadPtrHandle<C>& aCb) {
226 MOZ_ASSERT(NS_IsMainThread());
227 MOZ_ASSERT(aCb);
228
229 // Assert that mTransaction was cleared before before we were called to allow
230 // reentrancy from microtask checkpoints.
231 MOZ_ASSERT(mTransaction.isNothing());
232
233 ErrorResult error;
234 aCb->Call(aResp, error);
235 NS_WARNING_ASSERTION(!error.Failed(), "dom::U2F::Promise callback failed");
236 error.SuppressException(); // Useful exceptions already emitted
237 }
238
Register(const nsAString & aAppId,const Sequence<RegisterRequest> & aRegisterRequests,const Sequence<RegisteredKey> & aRegisteredKeys,U2FRegisterCallback & aCallback,const Optional<Nullable<int32_t>> & opt_aTimeoutSeconds,ErrorResult & aRv)239 void U2F::Register(const nsAString& aAppId,
240 const Sequence<RegisterRequest>& aRegisterRequests,
241 const Sequence<RegisteredKey>& aRegisteredKeys,
242 U2FRegisterCallback& aCallback,
243 const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
244 ErrorResult& aRv) {
245 MOZ_ASSERT(NS_IsMainThread());
246
247 if (mTransaction.isSome()) {
248 CancelTransaction(NS_ERROR_ABORT);
249 }
250
251 nsMainThreadPtrHandle<U2FRegisterCallback> callback(
252 new nsMainThreadPtrHolder<U2FRegisterCallback>("U2F::Register::callback",
253 &aCallback));
254
255 // Ensure we have a callback.
256 if (NS_WARN_IF(!callback)) {
257 return;
258 }
259
260 // Evaluate the AppID
261 nsString adjustedAppId(aAppId);
262 if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Register, adjustedAppId)) {
263 RegisterResponse response;
264 response.mErrorCode.Construct(
265 static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
266 ExecuteCallback(response, callback);
267 return;
268 }
269
270 // Produce the AppParam from the current AppID
271 nsCString cAppId = NS_ConvertUTF16toUTF8(adjustedAppId);
272
273 nsAutoString clientDataJSON;
274
275 // Pick the first valid RegisterRequest; we can only work with one.
276 for (const RegisterRequest& req : aRegisterRequests) {
277 if (!req.mChallenge.WasPassed() || !req.mVersion.WasPassed() ||
278 req.mVersion.Value() != kRequiredU2FVersion) {
279 continue;
280 }
281
282 nsresult rv = AssembleClientData(mOrigin, kFinishEnrollment,
283 req.mChallenge.Value(), clientDataJSON);
284 if (NS_WARN_IF(NS_FAILED(rv))) {
285 continue;
286 }
287 }
288
289 // Did we not get a valid RegisterRequest? Abort.
290 if (clientDataJSON.IsEmpty()) {
291 RegisterResponse response;
292 response.mErrorCode.Construct(
293 static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
294 ExecuteCallback(response, callback);
295 return;
296 }
297
298 // Build the exclusion list, if any
299 nsTArray<WebAuthnScopedCredential> excludeList;
300 RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
301 excludeList);
302
303 auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
304
305 CryptoBuffer rpIdHash, clientDataHash;
306 if (NS_FAILED(BuildTransactionHashes(cAppId, clientData, rpIdHash,
307 clientDataHash))) {
308 RegisterResponse response;
309 response.mErrorCode.Construct(
310 static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
311 ExecuteCallback(response, callback);
312 return;
313 }
314
315 if (!MaybeCreateBackgroundActor()) {
316 RegisterResponse response;
317 response.mErrorCode.Construct(
318 static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
319 ExecuteCallback(response, callback);
320 return;
321 }
322
323 ListenForVisibilityEvents();
324
325 // Always blank for U2F
326 nsTArray<WebAuthnExtension> extensions;
327
328 // Default values for U2F.
329 WebAuthnAuthenticatorSelection authSelection(
330 false /* requireResidentKey */, false /* requireUserVerification */,
331 false /* requirePlatformAttachment */);
332
333 uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
334
335 WebAuthnMakeCredentialInfo info(
336 mOrigin, rpIdHash, clientDataHash, adjustedTimeoutMillis, excludeList,
337 extensions, authSelection, false /* RequestDirectAttestation */);
338
339 MOZ_ASSERT(mTransaction.isNothing());
340 mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
341 mChild->SendRequestRegister(mTransaction.ref().mId, info);
342 }
343
FinishMakeCredential(const uint64_t & aTransactionId,const WebAuthnMakeCredentialResult & aResult)344 void U2F::FinishMakeCredential(const uint64_t& aTransactionId,
345 const WebAuthnMakeCredentialResult& aResult) {
346 MOZ_ASSERT(NS_IsMainThread());
347
348 // Check for a valid transaction.
349 if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
350 return;
351 }
352
353 if (NS_WARN_IF(!mTransaction.ref().HasRegisterCallback())) {
354 RejectTransaction(NS_ERROR_ABORT);
355 return;
356 }
357
358 CryptoBuffer clientDataBuf;
359 if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
360 RejectTransaction(NS_ERROR_ABORT);
361 return;
362 }
363
364 CryptoBuffer regBuf;
365 if (NS_WARN_IF(!regBuf.Assign(aResult.RegBuffer()))) {
366 RejectTransaction(NS_ERROR_ABORT);
367 return;
368 }
369
370 nsString clientDataBase64;
371 nsString registrationDataBase64;
372 nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
373 nsresult rvRegistrationData = regBuf.ToJwkBase64(registrationDataBase64);
374
375 if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
376 NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
377 RejectTransaction(NS_ERROR_ABORT);
378 return;
379 }
380
381 // Assemble a response object to return
382 RegisterResponse response;
383 response.mVersion.Construct(kRequiredU2FVersion);
384 response.mClientData.Construct(clientDataBase64);
385 response.mRegistrationData.Construct(registrationDataBase64);
386 response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
387
388 // Keep the callback pointer alive.
389 nsMainThreadPtrHandle<U2FRegisterCallback> callback(
390 mTransaction.ref().GetRegisterCallback());
391
392 ClearTransaction();
393 ExecuteCallback(response, callback);
394 }
395
Sign(const nsAString & aAppId,const nsAString & aChallenge,const Sequence<RegisteredKey> & aRegisteredKeys,U2FSignCallback & aCallback,const Optional<Nullable<int32_t>> & opt_aTimeoutSeconds,ErrorResult & aRv)396 void U2F::Sign(const nsAString& aAppId, const nsAString& aChallenge,
397 const Sequence<RegisteredKey>& aRegisteredKeys,
398 U2FSignCallback& aCallback,
399 const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
400 ErrorResult& aRv) {
401 MOZ_ASSERT(NS_IsMainThread());
402
403 if (mTransaction.isSome()) {
404 CancelTransaction(NS_ERROR_ABORT);
405 }
406
407 nsMainThreadPtrHandle<U2FSignCallback> callback(
408 new nsMainThreadPtrHolder<U2FSignCallback>("U2F::Sign::callback",
409 &aCallback));
410
411 // Ensure we have a callback.
412 if (NS_WARN_IF(!callback)) {
413 return;
414 }
415
416 // Evaluate the AppID
417 nsString adjustedAppId(aAppId);
418 if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Sign, adjustedAppId)) {
419 SignResponse response;
420 response.mErrorCode.Construct(
421 static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
422 ExecuteCallback(response, callback);
423 return;
424 }
425
426 // Produce the AppParam from the current AppID
427 nsCString cAppId = NS_ConvertUTF16toUTF8(adjustedAppId);
428
429 nsAutoString clientDataJSON;
430 nsresult rv =
431 AssembleClientData(mOrigin, kGetAssertion, aChallenge, clientDataJSON);
432 if (NS_WARN_IF(NS_FAILED(rv))) {
433 SignResponse response;
434 response.mErrorCode.Construct(
435 static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
436 ExecuteCallback(response, callback);
437 return;
438 }
439
440 // Build the key list, if any
441 nsTArray<WebAuthnScopedCredential> permittedList;
442 RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
443 permittedList);
444
445 auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
446
447 CryptoBuffer rpIdHash, clientDataHash;
448 if (NS_FAILED(BuildTransactionHashes(cAppId, clientData, rpIdHash,
449 clientDataHash))) {
450 SignResponse response;
451 response.mErrorCode.Construct(
452 static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
453 ExecuteCallback(response, callback);
454 return;
455 }
456
457 if (!MaybeCreateBackgroundActor()) {
458 SignResponse response;
459 response.mErrorCode.Construct(
460 static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
461 ExecuteCallback(response, callback);
462 return;
463 }
464
465 ListenForVisibilityEvents();
466
467 // Always blank for U2F
468 nsTArray<WebAuthnExtension> extensions;
469
470 uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
471
472 WebAuthnGetAssertionInfo info(mOrigin, rpIdHash, clientDataHash,
473 adjustedTimeoutMillis, permittedList,
474 false, /* requireUserVerification */
475 extensions);
476
477 MOZ_ASSERT(mTransaction.isNothing());
478 mTransaction = Some(U2FTransaction(clientData, Move(AsVariant(callback))));
479 mChild->SendRequestSign(mTransaction.ref().mId, info);
480 }
481
FinishGetAssertion(const uint64_t & aTransactionId,const WebAuthnGetAssertionResult & aResult)482 void U2F::FinishGetAssertion(const uint64_t& aTransactionId,
483 const WebAuthnGetAssertionResult& aResult) {
484 MOZ_ASSERT(NS_IsMainThread());
485
486 // Check for a valid transaction.
487 if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
488 return;
489 }
490
491 if (NS_WARN_IF(!mTransaction.ref().HasSignCallback())) {
492 RejectTransaction(NS_ERROR_ABORT);
493 return;
494 }
495
496 CryptoBuffer clientDataBuf;
497 if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
498 RejectTransaction(NS_ERROR_ABORT);
499 return;
500 }
501
502 CryptoBuffer credBuf;
503 if (NS_WARN_IF(!credBuf.Assign(aResult.CredentialID()))) {
504 RejectTransaction(NS_ERROR_ABORT);
505 return;
506 }
507
508 CryptoBuffer sigBuf;
509 if (NS_WARN_IF(!sigBuf.Assign(aResult.SigBuffer()))) {
510 RejectTransaction(NS_ERROR_ABORT);
511 return;
512 }
513
514 // Assemble a response object to return
515 nsString clientDataBase64;
516 nsString signatureDataBase64;
517 nsString keyHandleBase64;
518 nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
519 nsresult rvSignatureData = sigBuf.ToJwkBase64(signatureDataBase64);
520 nsresult rvKeyHandle = credBuf.ToJwkBase64(keyHandleBase64);
521 if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
522 NS_WARN_IF(NS_FAILED(rvSignatureData) ||
523 NS_WARN_IF(NS_FAILED(rvKeyHandle)))) {
524 RejectTransaction(NS_ERROR_ABORT);
525 return;
526 }
527
528 SignResponse response;
529 response.mKeyHandle.Construct(keyHandleBase64);
530 response.mClientData.Construct(clientDataBase64);
531 response.mSignatureData.Construct(signatureDataBase64);
532 response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
533
534 // Keep the callback pointer alive.
535 nsMainThreadPtrHandle<U2FSignCallback> callback(
536 mTransaction.ref().GetSignCallback());
537
538 ClearTransaction();
539 ExecuteCallback(response, callback);
540 }
541
ClearTransaction()542 void U2F::ClearTransaction() {
543 if (!NS_WARN_IF(mTransaction.isNothing())) {
544 StopListeningForVisibilityEvents();
545 }
546
547 mTransaction.reset();
548 }
549
RejectTransaction(const nsresult & aError)550 void U2F::RejectTransaction(const nsresult& aError) {
551 if (NS_WARN_IF(mTransaction.isNothing())) {
552 return;
553 }
554
555 StopListeningForVisibilityEvents();
556
557 // Clear out mTransaction before calling ExecuteCallback() below to allow
558 // reentrancy from microtask checkpoints.
559 Maybe<U2FTransaction> maybeTransaction(Move(mTransaction));
560 MOZ_ASSERT(mTransaction.isNothing() && maybeTransaction.isSome());
561
562 U2FTransaction& transaction = maybeTransaction.ref();
563 ErrorCode code = ConvertNSResultToErrorCode(aError);
564
565 if (transaction.HasRegisterCallback()) {
566 RegisterResponse response;
567 response.mErrorCode.Construct(static_cast<uint32_t>(code));
568 ExecuteCallback(response, transaction.GetRegisterCallback());
569 }
570
571 if (transaction.HasSignCallback()) {
572 SignResponse response;
573 response.mErrorCode.Construct(static_cast<uint32_t>(code));
574 ExecuteCallback(response, transaction.GetSignCallback());
575 }
576 }
577
CancelTransaction(const nsresult & aError)578 void U2F::CancelTransaction(const nsresult& aError) {
579 if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
580 mChild->SendRequestCancel(mTransaction.ref().mId);
581 }
582
583 RejectTransaction(aError);
584 }
585
RequestAborted(const uint64_t & aTransactionId,const nsresult & aError)586 void U2F::RequestAborted(const uint64_t& aTransactionId,
587 const nsresult& aError) {
588 MOZ_ASSERT(NS_IsMainThread());
589
590 if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
591 RejectTransaction(aError);
592 }
593 }
594
595 } // namespace dom
596 } // namespace mozilla
597