ac9ba9762037b8e3d860d8a69395f3b88d7b46f0
[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/RunLoop.h>
34
35 namespace WebKit {
36 using namespace WebCore;
37
38 namespace AuthenticatorManagerInternal {
39
40 const size_t maxTransportNumber = 1;
41 // Suggested by WebAuthN spec as of 7 August 2018.
42 const unsigned maxTimeOutValue = 120000;
43
44 // FIXME(188623, 188624, 188625): Support USB, NFC and BLE authenticators.
45 static AuthenticatorManager::TransportSet collectTransports(const std::optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria>& authenticatorSelection)
46 {
47     AuthenticatorManager::TransportSet result;
48     if (!authenticatorSelection) {
49         auto addResult = result.add(AuthenticatorTransport::Internal);
50         ASSERT_UNUSED(addResult, addResult.isNewEntry);
51         return result;
52     }
53
54     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
55         auto addResult = result.add(AuthenticatorTransport::Internal);
56         ASSERT_UNUSED(addResult, addResult.isNewEntry);
57         return result;
58     }
59     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform)
60         return result;
61
62     ASSERT_NOT_REACHED();
63     return result;
64 }
65
66 // FIXME(188623, 188624, 188625): Support USB, NFC and BLE authenticators.
67 // The goal is to find a union of different transports from allowCredentials.
68 // If it is not specified or any of its credentials doesn't specify its own. We should discover all.
69 // This is a variant of Step. 18.*.4 from https://www.w3.org/TR/webauthn/#discover-from-external-source
70 // as of 7 August 2018.
71 static AuthenticatorManager::TransportSet collectTransports(const Vector<PublicKeyCredentialDescriptor>& allowCredentials)
72 {
73     AuthenticatorManager::TransportSet result;
74     if (allowCredentials.isEmpty()) {
75         auto addResult = result.add(AuthenticatorTransport::Internal);
76         ASSERT_UNUSED(addResult, addResult.isNewEntry);
77         return result;
78     }
79
80     for (auto& allowCredential : allowCredentials) {
81         if (allowCredential.transports.isEmpty()) {
82             result.add(AuthenticatorTransport::Internal);
83             return result;
84         }
85         if (!result.contains(AuthenticatorTransport::Internal) && allowCredential.transports.contains(AuthenticatorTransport::Internal))
86             result.add(AuthenticatorTransport::Internal);
87         if (result.size() >= maxTransportNumber)
88             return result;
89     }
90
91     ASSERT(result.size() < maxTransportNumber);
92     return result;
93 }
94
95 } // namespace AuthenticatorManagerInternal
96
97 void AuthenticatorManager::makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, Callback&& callback)
98 {
99     using namespace AuthenticatorManagerInternal;
100
101     if (m_pendingCompletionHandler) {
102         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
103         return;
104     }
105
106     // 1. Save request for async operations.
107     m_pendingRequestData = { hash, true, options, { } };
108     m_pendingCompletionHandler = WTFMove(callback);
109     initTimeOutTimer(options.timeout);
110
111     // 2. Get available transports and start discovering authenticators on them.
112     startDiscovery(collectTransports(options.authenticatorSelection));
113 }
114
115 void AuthenticatorManager::getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, Callback&& callback)
116 {
117     using namespace AuthenticatorManagerInternal;
118
119     if (m_pendingCompletionHandler) {
120         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
121         return;
122     }
123
124     // 1. Save request for async operations.
125     m_pendingRequestData = { hash, false, { }, options };
126     m_pendingCompletionHandler = WTFMove(callback);
127     initTimeOutTimer(options.timeout);
128
129     // 2. Get available transports and start discovering authenticators on them.
130     ASSERT(m_services.isEmpty());
131     startDiscovery(collectTransports(options.allowCredentials));
132 }
133
134 void AuthenticatorManager::clearState()
135 {
136     m_pendingRequestData = { };
137     ASSERT(!m_pendingCompletionHandler);
138     m_services.clear();
139     m_authenticators.clear();
140 }
141
142 void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator)
143 {
144     ASSERT(RunLoop::isMain());
145     authenticator->setObserver(*this);
146     authenticator->handleRequest(m_pendingRequestData);
147     auto addResult = m_authenticators.add(WTFMove(authenticator));
148     ASSERT_UNUSED(addResult, addResult.isNewEntry);
149 }
150
151 void AuthenticatorManager::respondReceived(Respond&& respond)
152 {
153     ASSERT(RunLoop::isMain());
154     ASSERT(m_requestTimeOutTimer);
155     if (!m_requestTimeOutTimer->isActive())
156         return;
157
158     ASSERT(m_pendingCompletionHandler);
159     if (WTF::holds_alternative<PublicKeyCredentialData>(respond)) {
160         m_pendingCompletionHandler(WTFMove(respond));
161         clearState();
162         m_requestTimeOutTimer->stop();
163         return;
164     }
165     respondReceivedInternal(WTFMove(respond));
166 }
167
168 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
169 {
170     return AuthenticatorTransportService::create(transport, observer);
171 }
172
173 void AuthenticatorManager::respondReceivedInternal(Respond&&)
174 {
175 }
176
177 void AuthenticatorManager::startDiscovery(const TransportSet& transports)
178 {
179     using namespace AuthenticatorManagerInternal;
180
181     ASSERT(m_services.isEmpty() && transports.size() <= maxTransportNumber);
182     for (auto& transport : transports) {
183         auto service = createService(transport, *this);
184         service->startDiscovery();
185         m_services.append(WTFMove(service));
186     }
187 }
188
189 void AuthenticatorManager::initTimeOutTimer(const std::optional<unsigned>& timeOutInMs)
190 {
191     using namespace AuthenticatorManagerInternal;
192
193     unsigned timeOutInMsValue = std::min(maxTimeOutValue, timeOutInMs.value_or(maxTimeOutValue));
194
195     m_requestTimeOutTimer = std::make_unique<Timer>([context = this]() mutable {
196         context->m_pendingCompletionHandler((ExceptionData { NotAllowedError, "Operation timed out."_s }));
197         context->clearState();
198     });
199     m_requestTimeOutTimer->startOneShot(Seconds::fromMilliseconds(timeOutInMsValue));
200 }
201
202 } // namespace WebKit
203
204 #endif // ENABLE(WEB_AUTHN)