Chromium Code Reviews| 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 |