Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Side by Side Diff: device/u2f/u2f_hid_device.cc

Issue 2721223002: Add support for U2fHidDevice interaction (Closed)
Patch Set: Ensure queued callbacks are called in error scenarios Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "u2f_hid_device.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "crypto/random.h"
11 #include "device/base/device_client.h"
12 #include "device/hid/hid_connection.h"
13 #include "u2f_apdu_command.h"
14 #include "u2f_message.h"
15
16 namespace device {
17
18 namespace switches {
19 static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
20 } // namespace switches
21
22 U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info)
23 : U2fDevice(),
24 state_(State::INIT),
25 device_info_(device_info),
26 weak_factory_(this) {
27 channel_id_ = kBroadcastChannel;
28 }
29
30 U2fHidDevice::~U2fHidDevice() {
31 // Cleanup connection
32 if (connection_)
33 connection_->Close();
34 }
35
36 void U2fHidDevice::DeviceTransact(scoped_refptr<U2fApduCommand> command,
37 const DeviceCallback& callback) {
38 Transition(command, callback);
39 }
40
41 void U2fHidDevice::Transition(scoped_refptr<U2fApduCommand> command,
42 const DeviceCallback& callback) {
43 switch (state_) {
44 case State::INIT:
45 state_ = State::BUSY;
46 Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(),
47 command, callback));
48 break;
49 case State::CONNECTED:
50 state_ = State::BUSY;
51 AllocateChannel(command, callback);
52 break;
53 case State::IDLE: {
54 state_ = State::BUSY;
55 scoped_refptr<U2fMessage> msg = U2fMessage::Create(
56 channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand());
57 WriteMessage(msg, true,
58 base::Bind(&U2fHidDevice::MessageReceived,
59 weak_factory_.GetWeakPtr(), callback));
60 break;
61 }
62 case State::BUSY:
63 pending_transactions_.push_back({std::move(command), callback});
64 break;
65 case State::DEVICE_ERROR:
66 default:
67 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr();
68 callback.Run(false, nullptr);
69 // Executing |callback| may have freed |this|. Check |self| first.
Reilly Grant (use Gerrit) 2017/03/09 00:37:03 "Executing callbacks may free |this|. Check |self|
Casey Piper 2017/03/10 00:32:09 Done.
70 while (self && !pending_transactions_.empty()) {
71 // Respond to any pending requests
72 DeviceCallback pending_cb = pending_transactions_.front().second;
73 pending_transactions_.pop_front();
74 pending_cb.Run(false, nullptr);
75 }
76 break;
77 }
78 }
79
80 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
81 HidService* hid_service = DeviceClient::Get()->GetHidService();
82
83 hid_service->Connect(device_info_->device_id(), callback);
84 }
85
86 void U2fHidDevice::OnConnect(scoped_refptr<U2fApduCommand> command,
87 const DeviceCallback& callback,
88 scoped_refptr<HidConnection> connection) {
89 if (connection) {
90 connection_ = connection;
91 state_ = State::CONNECTED;
92 } else {
93 state_ = State::DEVICE_ERROR;
94 }
95 Transition(command, callback);
96 }
97
98 void U2fHidDevice::AllocateChannel(scoped_refptr<U2fApduCommand> command,
99 const DeviceCallback& callback) {
100 // Send random nonce to device to verify received message
101 std::vector<uint8_t> nonce(8);
102 crypto::RandBytes(nonce.data(), nonce.size());
103 scoped_refptr<U2fMessage> message =
104 U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce);
105
106 WriteMessage(
107 message, true,
108 base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(),
109 nonce, command, callback));
110 }
111
112 void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
113 scoped_refptr<U2fApduCommand> command,
114 const DeviceCallback& callback,
115 bool success,
116 scoped_refptr<U2fMessage> message) {
117 if (!success || !message || message->GetMessagePayload().size() != 17) {
118 state_ = State::DEVICE_ERROR;
119 Transition(nullptr, callback);
120 return;
121 }
122 // Channel allocation response is defined as:
123 // 0: 8 byte nonce
124 // 8: 4 byte channel id
125 // 12: Protocol version id
126 // 13: Major device version
127 // 14: Minor device version
128 // 15: Build device version
129 // 16: Capabilities
130 std::vector<uint8_t> payload = message->GetMessagePayload();
Reilly Grant (use Gerrit) 2017/03/09 00:37:03 GetMessagePayload() is expensive so we shouldn't b
Casey Piper 2017/03/10 00:32:09 Acknowledged.
131 std::vector<uint8_t> received_nonce(std::begin(payload),
132 std::begin(payload) + 8);
133 if (nonce != received_nonce) {
134 state_ = State::DEVICE_ERROR;
135 Transition(nullptr, callback);
136 return;
137 }
138
139 size_t index = 8;
140 channel_id_ = payload[index++] << 24;
141 channel_id_ |= payload[index++] << 16;
142 channel_id_ |= payload[index++] << 8;
143 channel_id_ |= payload[index++];
144 capabilities_ = payload[16];
145
146 state_ = State::IDLE;
147 Transition(command, callback);
148 }
149
150 void U2fHidDevice::WriteMessage(scoped_refptr<U2fMessage> message,
151 bool response_expected,
152 U2fHidMessageCallback callback) {
153 if (!connection_ || !message || message->NumPackets() == 0) {
154 std::move(callback).Run(false, nullptr);
155 return;
156 }
157
158 scoped_refptr<net::IOBufferWithSize> buffer = message->PopNextPacket();
159
160 connection_->Write(
161 buffer, buffer->size(),
162 base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
163 message, true, base::Passed(std::move(callback))));
164 }
165
166 void U2fHidDevice::PacketWritten(scoped_refptr<U2fMessage> message,
167 bool response_expected,
168 U2fHidMessageCallback callback,
169 bool success) {
170 if (success && message->NumPackets() > 0) {
171 WriteMessage(message, response_expected, std::move(callback));
172 } else if (success && response_expected) {
173 ReadMessage(std::move(callback));
174 } else {
175 std::move(callback).Run(success, nullptr);
176 }
177 }
178
179 void U2fHidDevice::ReadMessage(U2fHidMessageCallback callback) {
180 if (!connection_) {
181 std::move(callback).Run(false, nullptr);
182 return;
183 }
184
185 connection_->Read(base::Bind(&U2fHidDevice::OnRead,
186 weak_factory_.GetWeakPtr(),
187 base::Passed(std::move(callback))));
188 }
189
190 void U2fHidDevice::OnRead(U2fHidMessageCallback callback,
191 bool success,
192 scoped_refptr<net::IOBuffer> buf,
193 size_t size) {
194 if (!success) {
195 std::move(callback).Run(success, nullptr);
196 return;
197 }
198
199 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
200 memcpy(buffer->data(), buf->data(), size);
201 scoped_refptr<U2fMessage> read_message =
202 U2fMessage::CreateFromSerializedData(buffer);
203
204 if (!read_message) {
205 std::move(callback).Run(false, nullptr);
206 return;
207 }
208
209 // Received a message from a different channel, so try again
210 if (channel_id_ != read_message->channel_id()) {
211 connection_->Read(base::Bind(&U2fHidDevice::OnRead,
212 weak_factory_.GetWeakPtr(),
213 base::Passed(std::move(callback))));
214 return;
215 }
216
217 if (read_message->MessageComplete()) {
218 std::move(callback).Run(success, read_message);
219 return;
220 }
221
222 // Continue reading additional packets
223 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
224 weak_factory_.GetWeakPtr(), read_message,
225 base::Passed(std::move(callback))));
226 }
227
228 void U2fHidDevice::OnReadContinuation(scoped_refptr<U2fMessage> message,
229 U2fHidMessageCallback callback,
230 bool success,
231 scoped_refptr<net::IOBuffer> buf,
232 size_t size) {
233 if (!success) {
234 std::move(callback).Run(success, nullptr);
235 return;
236 }
237
238 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
239 memcpy(buffer->data(), buf->data(), size);
240 message->AddContinuationPacket(buffer);
241 if (message->MessageComplete()) {
242 std::move(callback).Run(success, message);
243 return;
244 }
245 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
246 weak_factory_.GetWeakPtr(), message,
247 base::Passed(std::move(callback))));
248 }
249
250 void U2fHidDevice::MessageReceived(const DeviceCallback& callback,
251 bool success,
252 scoped_refptr<U2fMessage> message) {
253 if (!success) {
254 state_ = State::DEVICE_ERROR;
255 Transition(nullptr, callback);
256 return;
257 }
258 scoped_refptr<U2fApduResponse> response = nullptr;
259 if (message)
260 response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload());
261 state_ = State::IDLE;
262 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr();
263 callback.Run(success, response);
264
265 // Executing |callback| may have freed |this|. Check |self| first.
266 if (self && !pending_transactions_.empty()) {
267 // If any transactions were queued, process the first one
268 scoped_refptr<U2fApduCommand> pending_cmd =
269 std::move(pending_transactions_.front().first);
270 DeviceCallback pending_cb = pending_transactions_.front().second;
271 pending_transactions_.pop_front();
272 Transition(pending_cmd, pending_cb);
273 }
274 }
275
276 void U2fHidDevice::TryWink(const WinkCallback& callback) {
277 // Only try to wink if device claims support
278 if (!(capabilities_ & kWinkCapability) || state_ != State::IDLE) {
279 callback.Run();
280 return;
281 }
282
283 scoped_refptr<U2fMessage> wink_message = device::U2fMessage::Create(
284 channel_id_, U2fMessage::Type::CMD_WINK, std::vector<uint8_t>());
285 WriteMessage(
286 wink_message, true,
287 base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback));
288 }
289
290 void U2fHidDevice::OnWink(const WinkCallback& callback,
291 bool success,
292 scoped_refptr<U2fMessage> response) {
293 callback.Run();
294 }
295
296 std::string U2fHidDevice::GetId() {
297 std::ostringstream id("hid:");
298 id << device_info_->device_id();
299 return id.str();
300 }
301
302 // static
303 bool U2fHidDevice::IsTestEnabled() {
304 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
305 return command_line->HasSwitch(switches::kEnableU2fHidTest);
306 }
307
308 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698