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

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

Issue 2721223002: Add support for U2fHidDevice interaction (Closed)
Patch Set: Add comment and change logic for executing pending transactions 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
« no previous file with comments | « device/u2f/u2f_hid_device.h ('k') | device/u2f/u2f_hid_device_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 switch (state_) {
33 case State::CONNECTED:
34 case State::IDLE:
35 connection_->Close();
36 break;
37 default:
38 break;
39 }
40 }
41
42 void U2fHidDevice::DeviceTransact(scoped_refptr<U2fApduCommand> command,
43 const DeviceCallback& callback) {
44 Transition(command, callback);
45 }
46
47 void U2fHidDevice::Transition(scoped_refptr<U2fApduCommand> command,
48 const DeviceCallback& callback) {
49 switch (state_) {
50 case State::INIT:
51 state_ = State::BUSY;
52 Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(),
53 command, callback));
54 break;
55 case State::CONNECTED:
56 state_ = State::BUSY;
57 AllocateChannel(command, callback);
58 break;
59 case State::IDLE: {
60 state_ = State::BUSY;
61 scoped_refptr<U2fMessage> msg = U2fMessage::Create(
62 channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand());
63 WriteMessage(msg, true,
64 base::Bind(&U2fHidDevice::MessageReceived,
65 weak_factory_.GetWeakPtr(), callback));
66 break;
67 }
68 case State::BUSY:
69 pending_transactions_.push_back({std::move(command), callback});
70 break;
71 case State::DEVICE_ERROR:
72 default:
73 callback.Run(false, nullptr);
74 break;
75 }
76 }
77
78 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
79 HidService* hid_service = DeviceClient::Get()->GetHidService();
80
81 hid_service->Connect(device_info_->device_id(), callback);
82 }
83
84 void U2fHidDevice::OnConnect(scoped_refptr<U2fApduCommand> command,
85 const DeviceCallback& callback,
86 scoped_refptr<HidConnection> connection) {
87 if (connection) {
88 connection_ = connection;
89 state_ = State::CONNECTED;
90 } else {
91 state_ = State::DEVICE_ERROR;
92 }
93 Transition(command, callback);
94 }
95
96 void U2fHidDevice::AllocateChannel(scoped_refptr<U2fApduCommand> command,
97 const DeviceCallback& callback) {
98 // Send random nonce to device to verify received message
99 std::vector<uint8_t> nonce(8);
100 crypto::RandBytes(nonce.data(), nonce.size());
101 scoped_refptr<U2fMessage> message =
102 U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce);
103
104 WriteMessage(
105 message, true,
106 base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(),
107 nonce, command, callback));
108 }
109
110 void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
111 scoped_refptr<U2fApduCommand> command,
112 const DeviceCallback& callback,
113 bool success,
114 scoped_refptr<U2fMessage> message) {
115 // Channel allocation response is defined as:
116 // 0: 8 byte nonce
117 // 8: 4 byte channel id
118 // 12: Protocol version id
119 // 13: Major device version
120 // 14: Minor device version
121 // 15: Build device version
122 // 16: Capabilities
123 std::vector<uint8_t> payload = message->GetMessagePayload();
124 if (payload.size() != 17) {
125 callback.Run(false, nullptr);
126 return;
127 }
128
129 std::vector<uint8_t> received_nonce(std::begin(payload),
130 std::begin(payload) + 8);
131 if (nonce != received_nonce) {
132 callback.Run(false, nullptr);
133 return;
134 }
135
136 size_t index = 8;
137 channel_id_ = payload[index++] << 24;
138 channel_id_ |= payload[index++] << 16;
139 channel_id_ |= payload[index++] << 8;
140 channel_id_ |= payload[index++];
141 capabilities_ = payload[16];
142
143 state_ = State::IDLE;
144 Transition(command, callback);
145 }
146
147 void U2fHidDevice::WriteMessage(scoped_refptr<U2fMessage> message,
148 bool response_expected,
149 U2fHidMessageCallback callback) {
150 if (!connection_ || !message || message->NumPackets() == 0) {
151 std::move(callback).Run(false, nullptr);
152 return;
153 }
154
155 scoped_refptr<net::IOBufferWithSize> buffer = message->PopNextPacket();
156
157 connection_->Write(
158 buffer, buffer->size(),
159 base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
160 message, true, base::Passed(std::move(callback))));
161 }
162
163 void U2fHidDevice::PacketWritten(scoped_refptr<U2fMessage> message,
164 bool response_expected,
165 U2fHidMessageCallback callback,
166 bool success) {
167 if (success && message->NumPackets() > 0) {
168 WriteMessage(message, response_expected, std::move(callback));
169 } else if (success && response_expected) {
170 ReadMessage(std::move(callback));
171 } else {
172 std::move(callback).Run(success, nullptr);
173 }
174 }
175
176 void U2fHidDevice::ReadMessage(U2fHidMessageCallback callback) {
177 if (!connection_) {
178 std::move(callback).Run(false, nullptr);
179 return;
180 }
181
182 connection_->Read(base::Bind(&U2fHidDevice::OnRead,
183 weak_factory_.GetWeakPtr(),
184 base::Passed(std::move(callback))));
185 }
186
187 void U2fHidDevice::OnRead(U2fHidMessageCallback callback,
188 bool success,
189 scoped_refptr<net::IOBuffer> buf,
190 size_t size) {
191 if (!success) {
192 std::move(callback).Run(success, nullptr);
193 return;
194 }
195
196 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
197 memcpy(buffer->data(), buf->data(), size);
198 scoped_refptr<U2fMessage> read_message =
199 U2fMessage::CreateFromSerializedData(buffer);
200
201 if (!read_message) {
202 std::move(callback).Run(false, nullptr);
203 return;
204 }
205
206 // Received a message from a different channel, so try again
207 if (channel_id_ != read_message->channel_id()) {
208 connection_->Read(base::Bind(&U2fHidDevice::OnRead,
209 weak_factory_.GetWeakPtr(),
210 base::Passed(std::move(callback))));
211 return;
212 }
213
214 if (read_message->MessageComplete()) {
215 std::move(callback).Run(success, read_message);
216 return;
217 }
218
219 // Continue reading additional packets
220 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
221 weak_factory_.GetWeakPtr(), read_message,
222 base::Passed(std::move(callback))));
223 }
224
225 void U2fHidDevice::OnReadContinuation(scoped_refptr<U2fMessage> message,
226 U2fHidMessageCallback callback,
227 bool success,
228 scoped_refptr<net::IOBuffer> buf,
229 size_t size) {
230 if (!success) {
231 std::move(callback).Run(success, nullptr);
232 return;
233 }
234
235 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
236 memcpy(buffer->data(), buf->data(), size);
237 message->AddContinuationPacket(buffer);
238 if (message->MessageComplete()) {
239 std::move(callback).Run(success, message);
240 return;
241 }
242 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
243 weak_factory_.GetWeakPtr(), message,
244 base::Passed(std::move(callback))));
245 }
246
247 void U2fHidDevice::MessageReceived(const DeviceCallback& callback,
248 bool success,
249 scoped_refptr<U2fMessage> message) {
250 scoped_refptr<U2fApduResponse> response = nullptr;
251 if (success && message)
252 response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload());
253 state_ = success ? State::IDLE : State::DEVICE_ERROR;
254
255 base::WeakPtr<U2fHidDevice> self = weak_factory_.GetWeakPtr();
256 callback.Run(success, response);
257
258 // Executing |callback| may have freed |this|. Check |self| first.
259 if (self && !pending_transactions_.empty()) {
Reilly Grant (use Gerrit) 2017/03/07 23:08:38 I just noticed that if !success then state_ == DEV
Casey Piper 2017/03/08 23:07:19 I modified the logic to always return through Tran
260 // If any transactions were queued, process the first one
261 scoped_refptr<U2fApduCommand> pending_cmd =
262 std::move(pending_transactions_.front().first);
263 DeviceCallback pending_cb = pending_transactions_.front().second;
264 pending_transactions_.pop_front();
265 Transition(pending_cmd, pending_cb);
266 }
267 }
268
269 void U2fHidDevice::TryWink(const WinkCallback& callback) {
270 // Only try to wink if device claims support
271 if (!(capabilities_ & kWinkCapability) || state_ != State::IDLE) {
272 callback.Run();
273 return;
274 }
275
276 scoped_refptr<U2fMessage> wink_message = device::U2fMessage::Create(
277 channel_id_, U2fMessage::Type::CMD_WINK, std::vector<uint8_t>());
278 WriteMessage(
279 wink_message, true,
280 base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback));
281 }
282
283 void U2fHidDevice::OnWink(const WinkCallback& callback,
284 bool success,
285 scoped_refptr<U2fMessage> response) {
286 callback.Run();
287 }
288
289 std::string U2fHidDevice::GetId() {
290 std::ostringstream id("hid:");
291 id << device_info_->device_id();
292 return id.str();
293 }
294
295 // static
296 bool U2fHidDevice::IsTestEnabled() {
297 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
298 return command_line->HasSwitch(switches::kEnableU2fHidTest);
299 }
300
301 } // namespace device
OLDNEW
« no previous file with comments | « device/u2f/u2f_hid_device.h ('k') | device/u2f/u2f_hid_device_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698