OLD | NEW |
---|---|
(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 | |
OLD | NEW |