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

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

Issue 2721223002: Add support for U2fHidDevice interaction (Closed)
Patch Set: Modify unittest BUILD file 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 "base/bind.h"
6 #include "base/command_line.h"
7 #include "base/rand_util.h"
8 #include "device/base/device_client.h"
9 #include "device/hid/hid_connection.h"
10
11 #include "u2f_apdu_command.h"
12 #include "u2f_hid_device.h"
13 #include "u2f_message.h"
14
15 namespace device {
16
17 namespace switches {
18 static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
19 } // namespace switches
20
21 U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info)
22 : U2fDevice(),
23 state_(State::INIT),
24 device_info_(device_info),
25 weak_factory_(this) {
26 channel_id_ = kBroadcastChannel;
27 }
28
29 U2fHidDevice::~U2fHidDevice() {
30 // Cleanup connection
31 switch (state_) {
32 case State::CONNECTED:
33 case State::IDLE:
34 connection_->Close();
35 break;
36 default:
37 break;
38 }
39 }
40
41 void U2fHidDevice::DeviceTransact(scoped_refptr<U2fApduCommand> command,
42 const DeviceCallback& callback) {
43 Transition(command, callback);
44 }
45
46 void U2fHidDevice::Transition(scoped_refptr<U2fApduCommand> command,
47 const U2fDevice::DeviceCallback& callback) {
48 switch (state_) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 Since connection and channel allocation are asynch
Casey Piper 2017/03/04 02:06:28 Done, any requests that arrive while the device is
49 case State::INIT:
50 Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(),
51 command, callback));
52 break;
53 case State::CONNECTED:
54 AllocateChannel(command, callback);
55 break;
56 case State::IDLE: {
57 scoped_refptr<U2fMessage> msg = U2fMessage::Create(
58 channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand());
59 WriteMessage(msg, true,
60 base::Bind(&U2fHidDevice::MessageReceived,
61 weak_factory_.GetWeakPtr(), callback));
62 break;
63 }
64 case State::DEVICE_ERROR:
65 default:
66 callback.Run(false, nullptr);
67 break;
68 }
69 }
70
71 void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
72 HidService* hid_service = DeviceClient::Get()->GetHidService();
73
74 hid_service->Connect(device_info_->device_id(), callback);
75 }
76
77 void U2fHidDevice::OnConnect(scoped_refptr<U2fApduCommand> command,
78 const DeviceCallback& callback,
79 scoped_refptr<HidConnection> connection) {
80 if (connection != nullptr) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 if (connection)
Casey Piper 2017/03/04 02:06:28 Done.
81 connection_ = connection;
82 state_ = State::CONNECTED;
83 } else {
84 state_ = State::DEVICE_ERROR;
85 }
86 Transition(command, callback);
87 }
88
89 void U2fHidDevice::AllocateChannel(scoped_refptr<U2fApduCommand> command,
90 const DeviceCallback& callback) {
91 uint8_t rand[8];
92 // Send random nonce to device to verify received message
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 Please add a comment documenting that this nonce d
Casey Piper 2017/03/04 02:06:28 Done.
93 base::RandBytes(rand, 8);
94 std::vector<uint8_t> nonce(rand, std::end(rand));
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 You can do this without copying: std::vector<uint
Casey Piper 2017/03/04 02:06:28 Acknowledged.
95 scoped_refptr<U2fMessage> message =
96 U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce);
97
98 WriteMessage(
99 message, true,
100 base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(),
101 nonce, command, callback));
102 }
103
104 void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
105 scoped_refptr<U2fApduCommand> command,
106 const DeviceCallback& callback,
107 bool success,
108 scoped_refptr<U2fMessage> message) {
109 // Channel allocation response is defined as:
110 // 0: 8 byte nonce
111 // 8: 4 byte channel id
112 // 12: Protocol version id
113 // 13: Major device version
114 // 14: Minor device version
115 // 15: Build device version
116 // 16: Capabilities
117 std::vector<uint8_t> payload = message->GetMessagePayload();
118 if (payload.size() != 17) {
119 callback.Run(false, nullptr);
120 return;
121 }
122
123 std::vector<uint8_t> received_nonce(std::begin(payload),
124 std::begin(payload) + 8);
125 if (nonce != received_nonce) {
126 callback.Run(false, nullptr);
127 return;
128 }
129
130 size_t index = 8;
131 channel_id_ = payload[index++] << 24;
132 channel_id_ |= payload[index++] << 16;
133 channel_id_ |= payload[index++] << 8;
134 channel_id_ |= payload[index++];
135 capabilities_ = payload[16];
136
137 state_ = State::IDLE;
138 Transition(command, callback);
139 }
140
141 void U2fHidDevice::WriteMessage(scoped_refptr<U2fMessage> message,
142 bool response_expected,
143 const U2fHidMessageCallback& callback) {
144 if (connection_ == nullptr || message == nullptr ||
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 if (!connection_ || !message ||
Casey Piper 2017/03/04 02:06:28 Done.
145 message->NumPackets() == 0) {
146 callback.Run(false, nullptr);
147 return;
148 }
149
150 scoped_refptr<net::IOBufferWithSize> buffer = message->PopNextPacket();
151
152 connection_->Write(
153 buffer, buffer->size(),
154 base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
155 message, true, callback));
156 }
157
158 void U2fHidDevice::PacketWritten(scoped_refptr<U2fMessage> message,
159 bool response_expected,
160 const U2fHidMessageCallback& callback,
161 bool success) {
162 if (success && message->NumPackets() > 0) {
163 scoped_refptr<net::IOBufferWithSize> buffer = message->PopNextPacket();
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 Can you replace these statements with: WriteMessa
Casey Piper 2017/03/04 02:06:28 Done.
164 connection_->Write(
165 buffer, buffer->size(),
166 base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
167 message, response_expected, callback));
168 } else if (success && response_expected) {
169 ReadMessage(callback);
170 } else {
171 callback.Run(success, nullptr);
172 }
173 }
174
175 void U2fHidDevice::ReadMessage(const U2fHidMessageCallback& callback) {
176 if (connection_ == nullptr) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 if (!connection_)
Casey Piper 2017/03/04 02:06:28 Done.
177 callback.Run(false, nullptr);
178 return;
179 }
180
181 connection_->Read(
182 base::Bind(&U2fHidDevice::OnRead, weak_factory_.GetWeakPtr(), callback));
183 }
184
185 void U2fHidDevice::OnRead(const U2fHidMessageCallback& callback,
186 bool success,
187 scoped_refptr<net::IOBuffer> buf,
188 size_t size) {
189 if (!success) {
190 callback.Run(success, nullptr);
191 return;
192 }
193
194 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
195 memcpy(buffer->data(), buf->data(), size);
196 scoped_refptr<U2fMessage> read_message =
197 U2fMessage::CreateFromSerializedData(buffer);
198
199 if (read_message == nullptr) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 if (!read_message)
Casey Piper 2017/03/04 02:06:28 Done.
200 callback.Run(false, nullptr);
201 return;
202 }
203
204 // Received a message from a different channel, so try again
205 if (channel_id_ != read_message->channel_id()) {
206 connection_->Read(base::Bind(&U2fHidDevice::OnRead,
207 weak_factory_.GetWeakPtr(), callback));
208 return;
209 }
210
211 if (read_message->MessageComplete()) {
212 callback.Run(success, read_message);
213 return;
214 }
215
216 // Continue reading additional packets
217 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
218 weak_factory_.GetWeakPtr(), read_message,
219 callback));
220 }
221
222 void U2fHidDevice::OnReadContinuation(scoped_refptr<U2fMessage> message,
223 const U2fHidMessageCallback& callback,
224 bool success,
225 scoped_refptr<net::IOBuffer> buf,
226 size_t size) {
227 if (!success) {
228 callback.Run(success, nullptr);
229 return;
230 }
231
232 scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
233 memcpy(buffer->data(), buf->data(), size);
234 message->AddContinuationPacket(buffer);
235 if (message->MessageComplete()) {
236 callback.Run(success, message);
237 return;
238 }
239 connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
240 weak_factory_.GetWeakPtr(), message, callback));
241 }
242
243 void U2fHidDevice::MessageReceived(const DeviceCallback& callback,
244 bool success,
245 scoped_refptr<U2fMessage> message) {
246 scoped_refptr<U2fApduResponse> response = nullptr;
247 if (success && message != nullptr)
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 if (success && message)
Casey Piper 2017/03/04 02:06:28 Done.
248 response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload());
249 if (!success)
250 state_ = State::DEVICE_ERROR;
251 callback.Run(success, response);
252 }
253
254 void U2fHidDevice::TryWink(const WinkCallback& callback) {
255 // Only try to wink if device claims support
256 if (!(capabilities_ | kWinkCapability) || state_ != State::IDLE) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 capabilities_ & kWinkCapability?
Casey Piper 2017/03/04 02:06:28 Ah, yeah. Done.
257 callback.Run();
258 return;
259 }
260
261 scoped_refptr<U2fMessage> wink_message = device::U2fMessage::Create(
262 channel_id_, U2fMessage::Type::CMD_WINK, std::vector<uint8_t>());
263 WriteMessage(
264 wink_message, true,
265 base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback));
266 }
267
268 void U2fHidDevice::OnWink(const WinkCallback& callback,
269 bool success,
270 scoped_refptr<U2fMessage> response) {
271 callback.Run();
272 }
273
274 std::string U2fHidDevice::Id() {
275 std::ostringstream id("hid:");
276 id << device_info_->device_id();
277 return id.str();
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 return base::StringPrintf("hid:%d", device_info_->
Casey Piper 2017/03/04 02:06:28 StringPrintf only seems to work when device_id is
Reilly Grant (use Gerrit) 2017/03/06 20:49:23 Curses, I suppose I can only blame myself for that
278 }
279
280 // static
281 bool U2fHidDevice::IsTestEnabled() {
282 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
283 return command_line->HasSwitch(switches::kEnableU2fHidTest);
284 }
285
286 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698