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/. */
7 #include "mozilla/dom/U2FTokenManager.h"
8 #include "mozilla/dom/U2FTokenTransport.h"
9 #include "mozilla/dom/PWebAuthnTransactionParent.h"
10 #include "mozilla/MozPromise.h"
11 #include "mozilla/ipc/BackgroundParent.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/Unused.h"
16 #include "nsIObserver.h"
17 #include "nsIObserverService.h"
18 #include "nsIThread.h"
19 #include "nsTextFormatter.h"
20 #include "mozilla/Telemetry.h"
21 #include "WebAuthnEnumStrings.h"
23 #include "mozilla/dom/AndroidWebAuthnTokenManager.h"
25 namespace mozilla::dom
{
27 /***********************************************************************
29 **********************************************************************/
32 static mozilla::LazyLogModule
gU2FTokenManagerLog("u2fkeymanager");
33 StaticAutoPtr
<U2FTokenManager
> gU2FTokenManager
;
34 static nsIThread
* gBackgroundThread
;
37 /***********************************************************************
38 * U2FManager Implementation
39 **********************************************************************/
41 U2FTokenManager::U2FTokenManager()
42 : mTransactionParent(nullptr), mLastTransactionId(0) {
43 MOZ_ASSERT(XRE_IsParentProcess());
44 // Create on the main thread to make sure ClearOnShutdown() works.
45 MOZ_ASSERT(NS_IsMainThread());
49 void U2FTokenManager::Initialize() {
50 if (!XRE_IsParentProcess()) {
53 MOZ_ASSERT(NS_IsMainThread());
54 MOZ_ASSERT(!gU2FTokenManager
);
55 gU2FTokenManager
= new U2FTokenManager();
56 ClearOnShutdown(&gU2FTokenManager
);
60 U2FTokenManager
* U2FTokenManager::Get() {
61 MOZ_ASSERT(XRE_IsParentProcess());
62 // We should only be accessing this on the background thread
63 MOZ_ASSERT(!NS_IsMainThread());
64 return gU2FTokenManager
;
67 void U2FTokenManager::AbortTransaction(const uint64_t& aTransactionId
,
68 const nsresult
& aError
) {
69 Unused
<< mTransactionParent
->SendAbort(aTransactionId
, aError
);
73 void U2FTokenManager::AbortOngoingTransaction() {
74 if (mLastTransactionId
> 0 && mTransactionParent
) {
75 // Send an abort to any other ongoing transaction
76 Unused
<< mTransactionParent
->SendAbort(mLastTransactionId
,
77 NS_ERROR_DOM_ABORT_ERR
);
82 void U2FTokenManager::MaybeClearTransaction(
83 PWebAuthnTransactionParent
* aParent
) {
84 // Only clear if we've been requested to do so by our current transaction
86 if (mTransactionParent
== aParent
) {
91 void U2FTokenManager::ClearTransaction() {
92 mTransactionParent
= nullptr;
94 // Drop managers at the end of all transactions
95 if (mTokenManagerImpl
) {
96 mTokenManagerImpl
->Drop();
97 mTokenManagerImpl
= nullptr;
100 // Forget promises, if necessary.
101 mRegisterPromise
.DisconnectIfExists();
102 mSignPromise
.DisconnectIfExists();
104 // Clear transaction id.
105 mLastTransactionId
= 0;
108 RefPtr
<U2FTokenTransport
> U2FTokenManager::GetTokenManagerImpl() {
109 mozilla::ipc::AssertIsOnBackgroundThread();
111 if (mTokenManagerImpl
) {
112 return mTokenManagerImpl
;
115 if (!gBackgroundThread
) {
116 gBackgroundThread
= NS_GetCurrentThread();
117 MOZ_ASSERT(gBackgroundThread
, "This should never be null!");
120 return AndroidWebAuthnTokenManager::GetInstance();
123 void U2FTokenManager::Register(
124 PWebAuthnTransactionParent
* aTransactionParent
,
125 const uint64_t& aTransactionId
,
126 const WebAuthnMakeCredentialInfo
& aTransactionInfo
) {
127 MOZ_LOG(gU2FTokenManagerLog
, LogLevel::Debug
, ("U2FAuthRegister"));
129 AbortOngoingTransaction();
130 mTransactionParent
= aTransactionParent
;
131 mTokenManagerImpl
= GetTokenManagerImpl();
133 if (!mTokenManagerImpl
) {
134 AbortTransaction(aTransactionId
, NS_ERROR_DOM_NOT_ALLOWED_ERR
);
138 mLastTransactionId
= aTransactionId
;
140 mozilla::ipc::AssertIsOnBackgroundThread();
141 MOZ_ASSERT(mLastTransactionId
> 0);
143 uint64_t tid
= mLastTransactionId
;
146 ->Register(aTransactionInfo
, /* aForceNoneAttestation */ true)
148 GetCurrentSerialEventTarget(), __func__
,
149 [tid
](WebAuthnMakeCredentialResult
&& aResult
) {
150 Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED
,
151 u
"U2FRegisterFinish"_ns
, 1);
152 U2FTokenManager
* mgr
= U2FTokenManager::Get();
153 mgr
->MaybeConfirmRegister(tid
, aResult
);
156 MOZ_ASSERT(NS_FAILED(rv
));
157 U2FTokenManager
* mgr
= U2FTokenManager::Get();
158 Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED
,
159 u
"U2FRegisterAbort"_ns
, 1);
160 mgr
->MaybeAbortRegister(tid
, rv
);
162 ->Track(mRegisterPromise
);
165 void U2FTokenManager::MaybeConfirmRegister(
166 const uint64_t& aTransactionId
,
167 const WebAuthnMakeCredentialResult
& aResult
) {
168 MOZ_ASSERT(mLastTransactionId
== aTransactionId
);
169 mRegisterPromise
.Complete();
171 Unused
<< mTransactionParent
->SendConfirmRegister(aTransactionId
, aResult
);
175 void U2FTokenManager::MaybeAbortRegister(const uint64_t& aTransactionId
,
176 const nsresult
& aError
) {
177 MOZ_ASSERT(mLastTransactionId
== aTransactionId
);
178 mRegisterPromise
.Complete();
179 AbortTransaction(aTransactionId
, aError
);
182 void U2FTokenManager::Sign(PWebAuthnTransactionParent
* aTransactionParent
,
183 const uint64_t& aTransactionId
,
184 const WebAuthnGetAssertionInfo
& aTransactionInfo
) {
185 MOZ_LOG(gU2FTokenManagerLog
, LogLevel::Debug
, ("U2FAuthSign"));
187 AbortOngoingTransaction();
188 mTransactionParent
= aTransactionParent
;
189 mTokenManagerImpl
= GetTokenManagerImpl();
191 if (!mTokenManagerImpl
) {
192 AbortTransaction(aTransactionId
, NS_ERROR_DOM_NOT_ALLOWED_ERR
);
196 mLastTransactionId
= aTransactionId
;
198 mozilla::ipc::AssertIsOnBackgroundThread();
199 MOZ_ASSERT(mLastTransactionId
> 0);
200 uint64_t tid
= mLastTransactionId
;
202 mTokenManagerImpl
->Sign(aTransactionInfo
)
204 GetCurrentSerialEventTarget(), __func__
,
205 [tid
](nsTArray
<WebAuthnGetAssertionResultWrapper
>&& aResult
) {
206 U2FTokenManager
* mgr
= U2FTokenManager::Get();
207 Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED
,
208 u
"U2FSignFinish"_ns
, 1);
209 if (aResult
.Length() == 1) {
210 WebAuthnGetAssertionResult result
= aResult
[0].assertion
;
211 mgr
->MaybeConfirmSign(tid
, result
);
215 MOZ_ASSERT(NS_FAILED(rv
));
216 U2FTokenManager
* mgr
= U2FTokenManager::Get();
217 Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_WEBAUTHN_USED
,
218 u
"U2FSignAbort"_ns
, 1);
219 mgr
->MaybeAbortSign(tid
, rv
);
221 ->Track(mSignPromise
);
224 void U2FTokenManager::MaybeConfirmSign(
225 const uint64_t& aTransactionId
, const WebAuthnGetAssertionResult
& aResult
) {
226 MOZ_ASSERT(mLastTransactionId
== aTransactionId
);
227 mSignPromise
.Complete();
229 Unused
<< mTransactionParent
->SendConfirmSign(aTransactionId
, aResult
);
233 void U2FTokenManager::MaybeAbortSign(const uint64_t& aTransactionId
,
234 const nsresult
& aError
) {
235 MOZ_ASSERT(mLastTransactionId
== aTransactionId
);
236 mSignPromise
.Complete();
237 AbortTransaction(aTransactionId
, aError
);
240 void U2FTokenManager::Cancel(PWebAuthnTransactionParent
* aParent
,
241 const Tainted
<uint64_t>& aTransactionId
) {
242 // The last transaction ID also suffers from the issue described in Bug
243 // 1696159. A content process could cancel another content processes
244 // transaction by guessing the last transaction ID.
245 if (mTransactionParent
!= aParent
||
246 !MOZ_IS_VALID(aTransactionId
, mLastTransactionId
== aTransactionId
)) {
250 mTokenManagerImpl
->Cancel();
254 } // namespace mozilla::dom