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

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

Issue 2824803003: Add timeout task to U2F HID state machine (Closed)
Patch Set: Created 3 years, 8 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
« device/u2f/u2f_hid_device.h ('K') | « device/u2f/u2f_hid_device.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "u2f_hid_device.h" 5 #include "u2f_hid_device.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/threading/thread_task_runner_handle.h"
10 #include "crypto/random.h" 11 #include "crypto/random.h"
11 #include "device/base/device_client.h" 12 #include "device/base/device_client.h"
12 #include "device/hid/hid_connection.h" 13 #include "device/hid/hid_connection.h"
13 #include "u2f_apdu_command.h" 14 #include "u2f_apdu_command.h"
14 #include "u2f_message.h" 15 #include "u2f_message.h"
15 16
16 namespace device { 17 namespace device {
17 18
18 namespace switches { 19 namespace switches {
19 static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests"; 20 static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
20 } // namespace switches 21 } // namespace switches
21 22
22 U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info) 23 U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info)
23 : U2fDevice(), 24 : U2fDevice(),
24 state_(State::INIT), 25 state_(State::INIT),
25 device_info_(device_info), 26 device_info_(device_info),
26 weak_factory_(this) { 27 weak_factory_(this) {
27 channel_id_ = kBroadcastChannel; 28 channel_id_ = kBroadcastChannel;
28 } 29 }
29 30
30 U2fHidDevice::~U2fHidDevice() { 31 U2fHidDevice::~U2fHidDevice() {
31 // Cleanup connection 32 // Cleanup connection
32 if (connection_) 33 if (connection_ && !connection_->closed())
33 connection_->Close(); 34 connection_->Close();
34 } 35 }
35 36
36 void U2fHidDevice::DeviceTransact(std::unique_ptr<U2fApduCommand> command, 37 void U2fHidDevice::DeviceTransact(std::unique_ptr<U2fApduCommand> command,
37 const DeviceCallback& callback) { 38 const DeviceCallback& callback) {
38 Transition(std::move(command), callback); 39 Transition(std::move(command), callback);
39 } 40 }
40 41
41 void U2fHidDevice::Transition(std::unique_ptr<U2fApduCommand> command, 42 void U2fHidDevice::Transition(std::unique_ptr<U2fApduCommand> command,
42 const DeviceCallback& callback) { 43 const DeviceCallback& callback) {
43 switch (state_) { 44 switch (state_) {
44 case State::INIT: 45 case State::INIT: {
45 state_ = State::BUSY; 46 state_ = State::BUSY;
47 timeout_callback_.Reset(base::Bind(&U2fHidDevice::OnTimeout,
48 weak_factory_.GetWeakPtr(), callback));
49 auto self = weak_factory_.GetWeakPtr();
46 Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(), 50 Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(),
47 base::Passed(&command), callback)); 51 base::Passed(&command), callback));
52 if (self && !timeout_callback_.IsCancelled()) {
53 // Setup timeout task for 3 seconds
Reilly Grant (use Gerrit) 2017/04/17 22:25:17 Set up the timeout task before calling Connect to
Casey Piper 2017/04/17 23:02:38 Done. Did you mean DCHECK(timeout_callback.IsCance
54 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
55 FROM_HERE, timeout_callback_.callback(),
56 base::TimeDelta::FromMilliseconds(3000));
57 }
48 break; 58 break;
49 case State::CONNECTED: 59 }
60 case State::CONNECTED: {
50 state_ = State::BUSY; 61 state_ = State::BUSY;
62 timeout_callback_.Reset(base::Bind(&U2fHidDevice::OnTimeout,
63 weak_factory_.GetWeakPtr(), callback));
64 auto self = weak_factory_.GetWeakPtr();
51 AllocateChannel(std::move(command), callback); 65 AllocateChannel(std::move(command), callback);
66 if (self && !timeout_callback_.IsCancelled()) {
67 // Setup timeout task for 3 seconds
68 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
69 FROM_HERE, timeout_callback_.callback(),
70 base::TimeDelta::FromMilliseconds(3000));
71 }
52 break; 72 break;
73 }
53 case State::IDLE: { 74 case State::IDLE: {
54 state_ = State::BUSY; 75 state_ = State::BUSY;
55 std::unique_ptr<U2fMessage> msg = U2fMessage::Create( 76 std::unique_ptr<U2fMessage> msg = U2fMessage::Create(
56 channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand()); 77 channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand());
78
79 timeout_callback_.Reset(base::Bind(&U2fHidDevice::OnTimeout,
80 weak_factory_.GetWeakPtr(), callback));
81 auto self = weak_factory_.GetWeakPtr();
82 // Write message to the device
57 WriteMessage(std::move(msg), true, 83 WriteMessage(std::move(msg), true,
58 base::Bind(&U2fHidDevice::MessageReceived, 84 base::Bind(&U2fHidDevice::MessageReceived,
59 weak_factory_.GetWeakPtr(), callback)); 85 weak_factory_.GetWeakPtr(), callback));
86
87 if (self && !timeout_callback_.IsCancelled()) {
88 // Setup timeout task for 3 seconds
89 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
90 FROM_HERE, timeout_callback_.callback(),
91 base::TimeDelta::FromMilliseconds(3000));
92 }
60 break; 93 break;
61 } 94 }
62 case State::BUSY: 95 case State::BUSY:
63 pending_transactions_.push_back({std::move(command), callback}); 96 pending_transactions_.push_back({std::move(command), callback});
64 break; 97 break;
65 case State::DEVICE_ERROR: 98 case State::DEVICE_ERROR:
66 default: 99 default:
67 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr(); 100 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr();
68 callback.Run(false, nullptr); 101 callback.Run(false, nullptr);
69 // Executing callbacks may free |this|. Check |self| first. 102 // Executing callbacks may free |this|. Check |self| first.
70 while (self && !pending_transactions_.empty()) { 103 while (self && !pending_transactions_.empty()) {
71 // Respond to any pending requests 104 // Respond to any pending requests
72 DeviceCallback pending_cb = pending_transactions_.front().second; 105 DeviceCallback pending_cb = pending_transactions_.front().second;
73 pending_transactions_.pop_front(); 106 pending_transactions_.pop_front();
74 pending_cb.Run(false, nullptr); 107 pending_cb.Run(false, nullptr);
75 } 108 }
76 break; 109 break;
77 } 110 }
78 } 111 }
79 112
80 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) { 113 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
81 HidService* hid_service = DeviceClient::Get()->GetHidService(); 114 HidService* hid_service = DeviceClient::Get()->GetHidService();
82 115
83 hid_service->Connect(device_info_->device_id(), callback); 116 hid_service->Connect(device_info_->device_id(), callback);
84 } 117 }
85 118
86 void U2fHidDevice::OnConnect(std::unique_ptr<U2fApduCommand> command, 119 void U2fHidDevice::OnConnect(std::unique_ptr<U2fApduCommand> command,
87 const DeviceCallback& callback, 120 const DeviceCallback& callback,
88 scoped_refptr<HidConnection> connection) { 121 scoped_refptr<HidConnection> connection) {
122 if (timeout_callback_.IsCancelled()) {
123 return;
124 }
Reilly Grant (use Gerrit) 2017/04/17 22:25:17 nit: no braces around single-line if. The state m
Casey Piper 2017/04/17 23:02:38 Done.
125 timeout_callback_.Cancel();
126
89 if (connection) { 127 if (connection) {
90 connection_ = connection; 128 connection_ = connection;
91 state_ = State::CONNECTED; 129 state_ = State::CONNECTED;
92 } else { 130 } else {
93 state_ = State::DEVICE_ERROR; 131 state_ = State::DEVICE_ERROR;
94 } 132 }
95 Transition(std::move(command), callback); 133 Transition(std::move(command), callback);
96 } 134 }
97 135
98 void U2fHidDevice::AllocateChannel(std::unique_ptr<U2fApduCommand> command, 136 void U2fHidDevice::AllocateChannel(std::unique_ptr<U2fApduCommand> command,
99 const DeviceCallback& callback) { 137 const DeviceCallback& callback) {
100 // Send random nonce to device to verify received message 138 // Send random nonce to device to verify received message
101 std::vector<uint8_t> nonce(8); 139 std::vector<uint8_t> nonce(8);
102 crypto::RandBytes(nonce.data(), nonce.size()); 140 crypto::RandBytes(nonce.data(), nonce.size());
103 std::unique_ptr<U2fMessage> message = 141 std::unique_ptr<U2fMessage> message =
104 U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce); 142 U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce);
105 143
106 WriteMessage( 144 WriteMessage(
107 std::move(message), true, 145 std::move(message), true,
108 base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(), 146 base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(),
109 nonce, base::Passed(&command), callback)); 147 nonce, base::Passed(&command), callback));
110 } 148 }
111 149
112 void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce, 150 void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
113 std::unique_ptr<U2fApduCommand> command, 151 std::unique_ptr<U2fApduCommand> command,
114 const DeviceCallback& callback, 152 const DeviceCallback& callback,
115 bool success, 153 bool success,
116 std::unique_ptr<U2fMessage> message) { 154 std::unique_ptr<U2fMessage> message) {
155 if (timeout_callback_.IsCancelled()) {
156 return;
157 }
Reilly Grant (use Gerrit) 2017/04/17 22:25:17 nit: no braces around single-line if.
Casey Piper 2017/04/17 23:02:38 Done.
158 timeout_callback_.Cancel();
159
117 if (!success || !message) { 160 if (!success || !message) {
118 state_ = State::DEVICE_ERROR; 161 state_ = State::DEVICE_ERROR;
119 Transition(nullptr, callback); 162 Transition(nullptr, callback);
120 return; 163 return;
121 } 164 }
122 // Channel allocation response is defined as: 165 // Channel allocation response is defined as:
123 // 0: 8 byte nonce 166 // 0: 8 byte nonce
124 // 8: 4 byte channel id 167 // 8: 4 byte channel id
125 // 12: Protocol version id 168 // 12: Protocol version id
126 // 13: Major device version 169 // 13: Major device version
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 std::unique_ptr<U2fMessage> read_message = 249 std::unique_ptr<U2fMessage> read_message =
207 U2fMessage::CreateFromSerializedData(read_buffer); 250 U2fMessage::CreateFromSerializedData(read_buffer);
208 251
209 if (!read_message) { 252 if (!read_message) {
210 std::move(callback).Run(false, nullptr); 253 std::move(callback).Run(false, nullptr);
211 return; 254 return;
212 } 255 }
213 256
214 // Received a message from a different channel, so try again 257 // Received a message from a different channel, so try again
215 if (channel_id_ != read_message->channel_id()) { 258 if (channel_id_ != read_message->channel_id()) {
216 connection_->Read(base::Bind(&U2fHidDevice::OnRead, 259 base::ThreadTaskRunnerHandle::Get()->PostTask(
217 weak_factory_.GetWeakPtr(), 260 FROM_HERE,
218 base::Passed(&callback))); 261 base::Bind(&U2fHidDevice::ReadMessage, weak_factory_.GetWeakPtr(),
262 base::Passed(&callback)));
219 return; 263 return;
220 } 264 }
221 265
222 if (read_message->MessageComplete()) { 266 if (read_message->MessageComplete()) {
223 std::move(callback).Run(success, std::move(read_message)); 267 std::move(callback).Run(success, std::move(read_message));
224 return; 268 return;
225 } 269 }
226 270
227 // Continue reading additional packets 271 // Continue reading additional packets
228 connection_->Read( 272 connection_->Read(
(...skipping 15 matching lines...) Expand all
244 message->AddContinuationPacket(read_buffer); 288 message->AddContinuationPacket(read_buffer);
245 if (message->MessageComplete()) { 289 if (message->MessageComplete()) {
246 std::move(callback).Run(success, std::move(message)); 290 std::move(callback).Run(success, std::move(message));
247 return; 291 return;
248 } 292 }
249 connection_->Read( 293 connection_->Read(
250 base::Bind(&U2fHidDevice::OnReadContinuation, weak_factory_.GetWeakPtr(), 294 base::Bind(&U2fHidDevice::OnReadContinuation, weak_factory_.GetWeakPtr(),
251 base::Passed(&message), base::Passed(&callback))); 295 base::Passed(&message), base::Passed(&callback)));
252 } 296 }
253 297
254 void U2fHidDevice::MessageReceived(const DeviceCallback& callback, 298 void U2fHidDevice::MessageReceived(DeviceCallback callback,
255 bool success, 299 bool success,
256 std::unique_ptr<U2fMessage> message) { 300 std::unique_ptr<U2fMessage> message) {
301 if (timeout_callback_.IsCancelled()) {
302 return;
303 }
Reilly Grant (use Gerrit) 2017/04/17 22:25:17 nit: no braces around single-line if.
Casey Piper 2017/04/17 23:02:38 Done.
304 timeout_callback_.Cancel();
305
257 if (!success) { 306 if (!success) {
258 state_ = State::DEVICE_ERROR; 307 state_ = State::DEVICE_ERROR;
259 Transition(nullptr, callback); 308 Transition(nullptr, callback);
260 return; 309 return;
261 } 310 }
311
262 std::unique_ptr<U2fApduResponse> response = nullptr; 312 std::unique_ptr<U2fApduResponse> response = nullptr;
263 if (message) 313 if (message)
264 response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload()); 314 response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload());
265 state_ = State::IDLE; 315 state_ = State::IDLE;
266 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr(); 316 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr();
267 callback.Run(success, std::move(response)); 317 callback.Run(success, std::move(response));
268 318
269 // Executing |callback| may have freed |this|. Check |self| first. 319 // Executing |callback| may have freed |this|. Check |self| first.
270 if (self && !pending_transactions_.empty()) { 320 if (self && !pending_transactions_.empty()) {
271 // If any transactions were queued, process the first one 321 // If any transactions were queued, process the first one
(...skipping 18 matching lines...) Expand all
290 std::move(wink_message), true, 340 std::move(wink_message), true,
291 base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback)); 341 base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback));
292 } 342 }
293 343
294 void U2fHidDevice::OnWink(const WinkCallback& callback, 344 void U2fHidDevice::OnWink(const WinkCallback& callback,
295 bool success, 345 bool success,
296 std::unique_ptr<U2fMessage> response) { 346 std::unique_ptr<U2fMessage> response) {
297 callback.Run(); 347 callback.Run();
298 } 348 }
299 349
350 void U2fHidDevice::OnTimeout(DeviceCallback callback) {
351 timeout_callback_.Cancel();
Reilly Grant (use Gerrit) 2017/04/17 22:25:17 Since timeout_callback_ can only fire once it shou
Casey Piper 2017/04/17 23:02:38 Done.
352 state_ = State::DEVICE_ERROR;
353 Transition(nullptr, callback);
354 }
355
300 std::string U2fHidDevice::GetId() { 356 std::string U2fHidDevice::GetId() {
301 std::ostringstream id("hid:"); 357 std::ostringstream id("hid:", std::ios::ate);
302 id << device_info_->device_id(); 358 id << device_info_->device_id();
303 return id.str(); 359 return id.str();
304 } 360 }
305 361
306 // static 362 // static
307 bool U2fHidDevice::IsTestEnabled() { 363 bool U2fHidDevice::IsTestEnabled() {
308 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); 364 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
309 return command_line->HasSwitch(switches::kEnableU2fHidTest); 365 return command_line->HasSwitch(switches::kEnableU2fHidTest);
310 } 366 }
311 367
312 } // namespace device 368 } // namespace device
OLDNEW
« device/u2f/u2f_hid_device.h ('K') | « device/u2f/u2f_hid_device.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698