[WebAuthN] Change the nonce in the CTAP kInit command to weak random values
[WebKit-https.git] / Source / WebKit / UIProcess / WebAuthentication / AuthenticatorManager.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "AuthenticatorManager.h"
28
29 #if ENABLE(WEB_AUTHN)
30
31 #include <WebCore/AuthenticatorTransport.h>
32 #include <WebCore/PublicKeyCredentialCreationOptions.h>
33 #include <wtf/MonotonicTime.h>
34
35 namespace WebKit {
36 using namespace WebCore;
37
38 namespace AuthenticatorManagerInternal {
39
40 #if PLATFORM(MAC)
41 const size_t maxTransportNumber = 2;
42 #else
43 const size_t maxTransportNumber = 1;
44 #endif
45
46 // Suggested by WebAuthN spec as of 7 August 2018.
47 const unsigned maxTimeOutValue = 120000;
48
49 // FIXME(188624, 188625): Support NFC and BLE authenticators.
50 static AuthenticatorManager::TransportSet collectTransports(const Optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria>& authenticatorSelection)
51 {
52     AuthenticatorManager::TransportSet result;
53     if (!authenticatorSelection || !authenticatorSelection->authenticatorAttachment) {
54         auto addResult = result.add(AuthenticatorTransport::Internal);
55         ASSERT_UNUSED(addResult, addResult.isNewEntry);
56 #if PLATFORM(MAC)
57         addResult = result.add(AuthenticatorTransport::Usb);
58         ASSERT_UNUSED(addResult, addResult.isNewEntry);
59 #endif
60         return result;
61     }
62
63     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
64         auto addResult = result.add(AuthenticatorTransport::Internal);
65         ASSERT_UNUSED(addResult, addResult.isNewEntry);
66         return result;
67     }
68     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform) {
69 #if PLATFORM(MAC)
70         auto addResult = result.add(AuthenticatorTransport::Usb);
71         ASSERT_UNUSED(addResult, addResult.isNewEntry);
72 #endif
73         return result;
74     }
75
76     ASSERT_NOT_REACHED();
77     return result;
78 }
79
80 // FIXME(188624, 188625): Support NFC and BLE authenticators.
81 // The goal is to find a union of different transports from allowCredentials.
82 // If it is not specified or any of its credentials doesn't specify its own. We should discover all.
83 // This is a variant of Step. 18.*.4 from https://www.w3.org/TR/webauthn/#discover-from-external-source
84 // as of 7 August 2018.
85 static AuthenticatorManager::TransportSet collectTransports(const Vector<PublicKeyCredentialDescriptor>& allowCredentials)
86 {
87     AuthenticatorManager::TransportSet result;
88     if (allowCredentials.isEmpty()) {
89         auto addResult = result.add(AuthenticatorTransport::Internal);
90         ASSERT_UNUSED(addResult, addResult.isNewEntry);
91 #if PLATFORM(MAC)
92         addResult = result.add(AuthenticatorTransport::Usb);
93         ASSERT_UNUSED(addResult, addResult.isNewEntry);
94 #endif
95         return result;
96     }
97
98     for (auto& allowCredential : allowCredentials) {
99         if (allowCredential.transports.isEmpty()) {
100             result.add(AuthenticatorTransport::Internal);
101 #if PLATFORM(MAC)
102             result.add(AuthenticatorTransport::Usb);
103             return result;
104 #endif
105         }
106         if (!result.contains(AuthenticatorTransport::Internal) && allowCredential.transports.contains(AuthenticatorTransport::Internal))
107             result.add(AuthenticatorTransport::Internal);
108 #if PLATFORM(MAC)
109         if (!result.contains(AuthenticatorTransport::Usb) && allowCredential.transports.contains(AuthenticatorTransport::Usb))
110             result.add(AuthenticatorTransport::Usb);
111 #endif
112         if (result.size() >= maxTransportNumber)
113             return result;
114     }
115
116     ASSERT(result.size() < maxTransportNumber);
117     return result;
118 }
119
120 } // namespace AuthenticatorManagerInternal
121
122 AuthenticatorManager::AuthenticatorManager()
123     : m_requestTimeOutTimer(RunLoop::main(), this, &AuthenticatorManager::timeOutTimerFired)
124 {
125 }
126
127 void AuthenticatorManager::makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, Callback&& callback)
128 {
129     using namespace AuthenticatorManagerInternal;
130
131     if (m_pendingCompletionHandler) {
132         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
133         return;
134     }
135
136     // 1. Save request for async operations.
137     m_pendingRequestData = { hash, true, options, { } };
138     m_pendingCompletionHandler = WTFMove(callback);
139     initTimeOutTimer(options.timeout);
140
141     // 2. Get available transports and start discovering authenticators on them.
142     startDiscovery(collectTransports(options.authenticatorSelection));
143 }
144
145 void AuthenticatorManager::getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, Callback&& callback)
146 {
147     using namespace AuthenticatorManagerInternal;
148
149     if (m_pendingCompletionHandler) {
150         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
151         return;
152     }
153
154     // 1. Save request for async operations.
155     m_pendingRequestData = { hash, false, { }, options };
156     m_pendingCompletionHandler = WTFMove(callback);
157     initTimeOutTimer(options.timeout);
158
159     // 2. Get available transports and start discovering authenticators on them.
160     ASSERT(m_services.isEmpty());
161     startDiscovery(collectTransports(options.allowCredentials));
162 }
163
164 void AuthenticatorManager::clearStateAsync()
165 {
166     RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
167         if (!weakThis)
168             return;
169         weakThis->m_pendingRequestData = { };
170         ASSERT(!weakThis->m_pendingCompletionHandler);
171         weakThis->m_services.clear();
172         weakThis->m_authenticators.clear();
173     });
174 }
175
176 void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator)
177 {
178     ASSERT(RunLoop::isMain());
179     authenticator->setObserver(*this);
180     authenticator->handleRequest(m_pendingRequestData);
181     auto addResult = m_authenticators.add(WTFMove(authenticator));
182     ASSERT_UNUSED(addResult, addResult.isNewEntry);
183 }
184
185 void AuthenticatorManager::respondReceived(Respond&& respond)
186 {
187     ASSERT(RunLoop::isMain());
188     if (!m_requestTimeOutTimer.isActive())
189         return;
190
191     ASSERT(m_pendingCompletionHandler);
192     if (WTF::holds_alternative<PublicKeyCredentialData>(respond)) {
193         m_pendingCompletionHandler(WTFMove(respond));
194         clearStateAsync();
195         m_requestTimeOutTimer.stop();
196         return;
197     }
198     respondReceivedInternal(WTFMove(respond));
199 }
200
201 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
202 {
203     return AuthenticatorTransportService::create(transport, observer);
204 }
205
206 void AuthenticatorManager::respondReceivedInternal(Respond&&)
207 {
208 }
209
210 void AuthenticatorManager::startDiscovery(const TransportSet& transports)
211 {
212     using namespace AuthenticatorManagerInternal;
213
214     ASSERT(m_services.isEmpty() && transports.size() <= maxTransportNumber);
215     for (auto& transport : transports) {
216         auto service = createService(transport, *this);
217         service->startDiscovery();
218         m_services.append(WTFMove(service));
219     }
220 }
221
222 void AuthenticatorManager::initTimeOutTimer(const Optional<unsigned>& timeOutInMs)
223 {
224     using namespace AuthenticatorManagerInternal;
225
226     unsigned timeOutInMsValue = std::min(maxTimeOutValue, timeOutInMs.valueOr(maxTimeOutValue));
227     m_requestTimeOutTimer.startOneShot(Seconds::fromMilliseconds(timeOutInMsValue));
228 }
229
230 void AuthenticatorManager::timeOutTimerFired()
231 {
232     ASSERT(m_requestTimeOutTimer.isActive());
233     m_pendingCompletionHandler((ExceptionData { NotAllowedError, "Operation timed out."_s }));
234     clearStateAsync();
235 }
236
237 } // namespace WebKit
238
239 #endif // ENABLE(WEB_AUTHN)