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

Unified Diff: device/u2f/u2f_hid_device.cc

Issue 2721223002: Add support for U2fHidDevice interaction (Closed)
Patch Set: Modify unittest BUILD file Created 3 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: device/u2f/u2f_hid_device.cc
diff --git a/device/u2f/u2f_hid_device.cc b/device/u2f/u2f_hid_device.cc
new file mode 100644
index 0000000000000000000000000000000000000000..67a74559a922194744d82c9562d337fb71753311
--- /dev/null
+++ b/device/u2f/u2f_hid_device.cc
@@ -0,0 +1,286 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/rand_util.h"
+#include "device/base/device_client.h"
+#include "device/hid/hid_connection.h"
+
+#include "u2f_apdu_command.h"
+#include "u2f_hid_device.h"
+#include "u2f_message.h"
+
+namespace device {
+
+namespace switches {
+static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
+} // namespace switches
+
+U2fHidDevice::U2fHidDevice(scoped_refptr<HidDeviceInfo> device_info)
+ : U2fDevice(),
+ state_(State::INIT),
+ device_info_(device_info),
+ weak_factory_(this) {
+ channel_id_ = kBroadcastChannel;
+}
+
+U2fHidDevice::~U2fHidDevice() {
+ // Cleanup connection
+ switch (state_) {
+ case State::CONNECTED:
+ case State::IDLE:
+ connection_->Close();
+ break;
+ default:
+ break;
+ }
+}
+
+void U2fHidDevice::DeviceTransact(scoped_refptr<U2fApduCommand> command,
+ const DeviceCallback& callback) {
+ Transition(command, callback);
+}
+
+void U2fHidDevice::Transition(scoped_refptr<U2fApduCommand> command,
+ const U2fDevice::DeviceCallback& callback) {
+ 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
+ case State::INIT:
+ Connect(base::Bind(&U2fHidDevice::OnConnect, weak_factory_.GetWeakPtr(),
+ command, callback));
+ break;
+ case State::CONNECTED:
+ AllocateChannel(command, callback);
+ break;
+ case State::IDLE: {
+ scoped_refptr<U2fMessage> msg = U2fMessage::Create(
+ channel_id_, U2fMessage::Type::CMD_MSG, command->GetEncodedCommand());
+ WriteMessage(msg, true,
+ base::Bind(&U2fHidDevice::MessageReceived,
+ weak_factory_.GetWeakPtr(), callback));
+ break;
+ }
+ case State::DEVICE_ERROR:
+ default:
+ callback.Run(false, nullptr);
+ break;
+ }
+}
+
+void U2fHidDevice::Connect(const HidService::ConnectCallback& callback) {
+ HidService* hid_service = DeviceClient::Get()->GetHidService();
+
+ hid_service->Connect(device_info_->device_id(), callback);
+}
+
+void U2fHidDevice::OnConnect(scoped_refptr<U2fApduCommand> command,
+ const DeviceCallback& callback,
+ scoped_refptr<HidConnection> connection) {
+ if (connection != nullptr) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:33 if (connection)
Casey Piper 2017/03/04 02:06:28 Done.
+ connection_ = connection;
+ state_ = State::CONNECTED;
+ } else {
+ state_ = State::DEVICE_ERROR;
+ }
+ Transition(command, callback);
+}
+
+void U2fHidDevice::AllocateChannel(scoped_refptr<U2fApduCommand> command,
+ const DeviceCallback& callback) {
+ uint8_t rand[8];
+ // 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.
+ base::RandBytes(rand, 8);
+ 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.
+ scoped_refptr<U2fMessage> message =
+ U2fMessage::Create(channel_id_, U2fMessage::Type::CMD_INIT, nonce);
+
+ WriteMessage(
+ message, true,
+ base::Bind(&U2fHidDevice::OnAllocateChannel, weak_factory_.GetWeakPtr(),
+ nonce, command, callback));
+}
+
+void U2fHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce,
+ scoped_refptr<U2fApduCommand> command,
+ const DeviceCallback& callback,
+ bool success,
+ scoped_refptr<U2fMessage> message) {
+ // Channel allocation response is defined as:
+ // 0: 8 byte nonce
+ // 8: 4 byte channel id
+ // 12: Protocol version id
+ // 13: Major device version
+ // 14: Minor device version
+ // 15: Build device version
+ // 16: Capabilities
+ std::vector<uint8_t> payload = message->GetMessagePayload();
+ if (payload.size() != 17) {
+ callback.Run(false, nullptr);
+ return;
+ }
+
+ std::vector<uint8_t> received_nonce(std::begin(payload),
+ std::begin(payload) + 8);
+ if (nonce != received_nonce) {
+ callback.Run(false, nullptr);
+ return;
+ }
+
+ size_t index = 8;
+ channel_id_ = payload[index++] << 24;
+ channel_id_ |= payload[index++] << 16;
+ channel_id_ |= payload[index++] << 8;
+ channel_id_ |= payload[index++];
+ capabilities_ = payload[16];
+
+ state_ = State::IDLE;
+ Transition(command, callback);
+}
+
+void U2fHidDevice::WriteMessage(scoped_refptr<U2fMessage> message,
+ bool response_expected,
+ const U2fHidMessageCallback& callback) {
+ 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.
+ message->NumPackets() == 0) {
+ callback.Run(false, nullptr);
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> buffer = message->PopNextPacket();
+
+ connection_->Write(
+ buffer, buffer->size(),
+ base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
+ message, true, callback));
+}
+
+void U2fHidDevice::PacketWritten(scoped_refptr<U2fMessage> message,
+ bool response_expected,
+ const U2fHidMessageCallback& callback,
+ bool success) {
+ if (success && message->NumPackets() > 0) {
+ 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.
+ connection_->Write(
+ buffer, buffer->size(),
+ base::Bind(&U2fHidDevice::PacketWritten, weak_factory_.GetWeakPtr(),
+ message, response_expected, callback));
+ } else if (success && response_expected) {
+ ReadMessage(callback);
+ } else {
+ callback.Run(success, nullptr);
+ }
+}
+
+void U2fHidDevice::ReadMessage(const U2fHidMessageCallback& callback) {
+ if (connection_ == nullptr) {
Reilly Grant (use Gerrit) 2017/03/01 22:48:32 if (!connection_)
Casey Piper 2017/03/04 02:06:28 Done.
+ callback.Run(false, nullptr);
+ return;
+ }
+
+ connection_->Read(
+ base::Bind(&U2fHidDevice::OnRead, weak_factory_.GetWeakPtr(), callback));
+}
+
+void U2fHidDevice::OnRead(const U2fHidMessageCallback& callback,
+ bool success,
+ scoped_refptr<net::IOBuffer> buf,
+ size_t size) {
+ if (!success) {
+ callback.Run(success, nullptr);
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
+ memcpy(buffer->data(), buf->data(), size);
+ scoped_refptr<U2fMessage> read_message =
+ U2fMessage::CreateFromSerializedData(buffer);
+
+ 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.
+ callback.Run(false, nullptr);
+ return;
+ }
+
+ // Received a message from a different channel, so try again
+ if (channel_id_ != read_message->channel_id()) {
+ connection_->Read(base::Bind(&U2fHidDevice::OnRead,
+ weak_factory_.GetWeakPtr(), callback));
+ return;
+ }
+
+ if (read_message->MessageComplete()) {
+ callback.Run(success, read_message);
+ return;
+ }
+
+ // Continue reading additional packets
+ connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
+ weak_factory_.GetWeakPtr(), read_message,
+ callback));
+}
+
+void U2fHidDevice::OnReadContinuation(scoped_refptr<U2fMessage> message,
+ const U2fHidMessageCallback& callback,
+ bool success,
+ scoped_refptr<net::IOBuffer> buf,
+ size_t size) {
+ if (!success) {
+ callback.Run(success, nullptr);
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
+ memcpy(buffer->data(), buf->data(), size);
+ message->AddContinuationPacket(buffer);
+ if (message->MessageComplete()) {
+ callback.Run(success, message);
+ return;
+ }
+ connection_->Read(base::Bind(&U2fHidDevice::OnReadContinuation,
+ weak_factory_.GetWeakPtr(), message, callback));
+}
+
+void U2fHidDevice::MessageReceived(const DeviceCallback& callback,
+ bool success,
+ scoped_refptr<U2fMessage> message) {
+ scoped_refptr<U2fApduResponse> response = nullptr;
+ 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.
+ response = U2fApduResponse::CreateFromMessage(message->GetMessagePayload());
+ if (!success)
+ state_ = State::DEVICE_ERROR;
+ callback.Run(success, response);
+}
+
+void U2fHidDevice::TryWink(const WinkCallback& callback) {
+ // Only try to wink if device claims support
+ 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.
+ callback.Run();
+ return;
+ }
+
+ scoped_refptr<U2fMessage> wink_message = device::U2fMessage::Create(
+ channel_id_, U2fMessage::Type::CMD_WINK, std::vector<uint8_t>());
+ WriteMessage(
+ wink_message, true,
+ base::Bind(&U2fHidDevice::OnWink, weak_factory_.GetWeakPtr(), callback));
+}
+
+void U2fHidDevice::OnWink(const WinkCallback& callback,
+ bool success,
+ scoped_refptr<U2fMessage> response) {
+ callback.Run();
+}
+
+std::string U2fHidDevice::Id() {
+ std::ostringstream id("hid:");
+ id << device_info_->device_id();
+ 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
+}
+
+// static
+bool U2fHidDevice::IsTestEnabled() {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ return command_line->HasSwitch(switches::kEnableU2fHidTest);
+}
+
+} // namespace device

Powered by Google App Engine
This is Rietveld 408576698