Add WKBundlePage SPI to temporarily force light or dark appearance on a page.
[WebKit-https.git] / Source / WebKit / UIProcess / WebAuthentication / fido / CtapHidDriver.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 "CtapHidDriver.h"
28
29 #if ENABLE(WEB_AUTHN) && PLATFORM(MAC)
30
31 #include <WebCore/FidoConstants.h>
32 #include <wtf/CryptographicallyRandomNumber.h>
33 #include <wtf/RunLoop.h>
34 #include <wtf/Vector.h>
35 #include <wtf/text/Base64.h>
36
37 namespace WebKit {
38 using namespace fido;
39
40 CtapHidDriver::Worker::Worker(UniqueRef<HidConnection>&& connection)
41     : m_connection(WTFMove(connection))
42 {
43     m_connection->initialize();
44 }
45
46 CtapHidDriver::Worker::~Worker()
47 {
48     m_connection->terminate();
49 }
50
51 void CtapHidDriver::Worker::transact(fido::FidoHidMessage&& requestMessage, MessageCallback&& callback)
52 {
53     ASSERT(m_state == State::Idle);
54     m_state = State::Write;
55     m_requestMessage = WTFMove(requestMessage);
56     m_responseMessage.reset();
57     m_callback = WTFMove(callback);
58
59     // HidConnection could hold data from other applications, and thereofore invalidate it before each transaction.
60     m_connection->invalidateCache();
61     m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable {
62         ASSERT(RunLoop::isMain());
63         if (!weakThis)
64             return;
65         weakThis->write(sent);
66     });
67 }
68
69 void CtapHidDriver::Worker::write(HidConnection::DataSent sent)
70 {
71     ASSERT(m_state == State::Write);
72     // FIXME(192061)
73     LOG_ERROR("Start writing data.");
74     if (sent != HidConnection::DataSent::Yes) {
75         returnMessage(WTF::nullopt);
76         return;
77     }
78
79     if (!m_requestMessage->numPackets()) {
80         m_state = State::Read;
81         m_connection->registerDataReceivedCallback([weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) mutable {
82             ASSERT(RunLoop::isMain());
83             if (!weakThis)
84                 return;
85             weakThis->read(data);
86         });
87         return;
88     }
89
90     m_connection->send(m_requestMessage->popNextPacket(), [weakThis = makeWeakPtr(*this)](HidConnection::DataSent sent) mutable {
91         ASSERT(RunLoop::isMain());
92         if (!weakThis)
93             return;
94         weakThis->write(sent);
95     });
96 }
97
98 void CtapHidDriver::Worker::read(const Vector<uint8_t>& data)
99 {
100     ASSERT(m_state == State::Read);
101     // FIXME(192061)
102     LOG_ERROR("Start reading data.");
103     if (!m_responseMessage) {
104         m_responseMessage = FidoHidMessage::createFromSerializedData(data);
105         // The first few reports could be for other applications, and therefore ignore those.
106         if (!m_responseMessage || m_responseMessage->channelId() != m_requestMessage->channelId()) {
107             LOG_ERROR("Couldn't parse a hid init packet: %s", m_responseMessage ? "wrong channel id." : "bad data.");
108             m_responseMessage.reset();
109             return;
110         }
111     } else {
112         if (!m_responseMessage->addContinuationPacket(data)) {
113             LOG_ERROR("Couldn't parse a hid continuation packet.");
114             returnMessage(WTF::nullopt);
115             return;
116         }
117     }
118
119     if (m_responseMessage->messageComplete()) {
120         // A KeepAlive cmd could be sent between a request and a response to indicate that
121         // the authenticator is waiting for user consent. Keep listening for the response.
122         if (m_responseMessage->cmd() == FidoHidDeviceCommand::kKeepAlive) {
123             m_responseMessage.reset();
124             return;
125         }
126         returnMessage(WTFMove(m_responseMessage));
127         return;
128     }
129 }
130
131 void CtapHidDriver::Worker::returnMessage(Optional<fido::FidoHidMessage>&& message)
132 {
133     // FIXME(192061)
134     LOG_ERROR("Start returning data.");
135     m_state = State::Idle;
136     m_connection->unregisterDataReceivedCallback();
137     m_callback(WTFMove(message));
138 }
139
140 CtapHidDriver::CtapHidDriver(UniqueRef<HidConnection>&& connection)
141     : m_worker(makeUniqueRef<Worker>(WTFMove(connection)))
142     , m_nonce(kHidInitNonceLength)
143 {
144 }
145
146 void CtapHidDriver::transact(Vector<uint8_t>&& data, ResponseCallback&& callback)
147 {
148     ASSERT(m_state == State::Idle);
149     m_state = State::AllocateChannel;
150     m_channelId = kHidBroadcastChannel;
151     m_requestData = WTFMove(data);
152     m_responseCallback = WTFMove(callback);
153
154     // Allocate a channel.
155     // FIXME(192061)
156     LOG_ERROR("Start allocating a channel.");
157     ASSERT(m_nonce.size() == kHidInitNonceLength);
158     cryptographicallyRandomValues(m_nonce.data(), m_nonce.size());
159     auto initCommand = FidoHidMessage::create(m_channelId, FidoHidDeviceCommand::kInit, m_nonce);
160     ASSERT(initCommand);
161     m_worker->transact(WTFMove(*initCommand), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable {
162         ASSERT(RunLoop::isMain());
163         if (!weakThis)
164             return;
165         weakThis->continueAfterChannelAllocated(WTFMove(response));
166     });
167 }
168
169 void CtapHidDriver::continueAfterChannelAllocated(Optional<FidoHidMessage>&& message)
170 {
171     ASSERT(m_state == State::AllocateChannel);
172     if (!message) {
173         returnResponse({ });
174         return;
175     }
176     ASSERT(message->channelId() == m_channelId);
177
178     auto payload = message->getMessagePayload();
179     ASSERT(payload.size() == kHidInitResponseSize);
180     // Restart the transaction in the next run loop when nonce mismatches.
181     if (memcmp(payload.data(), m_nonce.data(), m_nonce.size())) {
182         m_state = State::Idle;
183         RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), data = WTFMove(m_requestData), callback = WTFMove(m_responseCallback)]() mutable {
184             if (!weakThis)
185                 return;
186             weakThis->transact(WTFMove(data), WTFMove(callback));
187         });
188         return;
189     }
190
191     m_state = State::Ready;
192     auto index = kHidInitNonceLength;
193     m_channelId = static_cast<uint32_t>(payload[index++]) << 24;
194     m_channelId |= static_cast<uint32_t>(payload[index++]) << 16;
195     m_channelId |= static_cast<uint32_t>(payload[index++]) << 8;
196     m_channelId |= static_cast<uint32_t>(payload[index]);
197     // FIXME(191534): Check the reset of the payload.
198     // FIXME(192061)
199     LOG_ERROR("Start sending the request.");
200     auto cmd = FidoHidMessage::create(m_channelId, m_protocol == ProtocolVersion::kCtap ? FidoHidDeviceCommand::kCbor : FidoHidDeviceCommand::kMsg, m_requestData);
201     ASSERT(cmd);
202     m_worker->transact(WTFMove(*cmd), [weakThis = makeWeakPtr(*this)](Optional<FidoHidMessage>&& response) mutable {
203         ASSERT(RunLoop::isMain());
204         if (!weakThis)
205             return;
206         weakThis->continueAfterResponseReceived(WTFMove(response));
207     });
208 }
209
210 void CtapHidDriver::continueAfterResponseReceived(Optional<fido::FidoHidMessage>&& message)
211 {
212     ASSERT(m_state == State::Ready);
213     ASSERT(!message || message->channelId() == m_channelId);
214     // FIXME(192061)
215     LOG_ERROR("Start returning the response.");
216     returnResponse(message ? message->getMessagePayload() : Vector<uint8_t>());
217 }
218
219 void CtapHidDriver::returnResponse(Vector<uint8_t>&& response)
220 {
221     // Reset state before calling the response callback to avoid being deleted.
222     m_state = State::Idle;
223     m_responseCallback(WTFMove(response));
224 }
225
226 } // namespace WebKit
227
228 #endif // ENABLE(WEB_AUTHN) && PLATFORM(MAC)