Commit de3a54f1 authored by piperc's avatar piperc Committed by Commit bot

Add FIDO U2F message and packet classes

FIDO U2F defines initialization packets and continuation packets.
This adds class definitions for those packets, and for an
enclosing message class. The enclosing message class constructs
the initialization and continuation packets based on the payload
length, queueing them in a list format for consumption by a
device connection.

BUG=NONE

Review-Url: https://codereview.chromium.org/2502103002
Cr-Commit-Position: refs/heads/master@{#438925}
parent 77f2784e
......@@ -145,6 +145,7 @@ test("device_unittests") {
"test/test_device_client.cc",
"test/test_device_client.h",
"test/usb_test_gadget_impl.cc",
"u2f/u2f_message_unittest.cc",
"usb/mojo/device_impl_unittest.cc",
"usb/mojo/device_manager_impl_unittest.cc",
"usb/mojo/mock_permission_provider.cc",
......@@ -159,6 +160,7 @@ test("device_unittests") {
deps += [
"//device/base",
"//device/base:mocks",
"//device/u2f",
"//device/usb",
"//device/usb:test_support",
"//device/usb/mojo",
......
# Copyright 2016 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.
import("//build/config/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
source_set("u2f") {
sources = [
"u2f_message.cc",
"u2f_message.h",
"u2f_packet.cc",
"u2f_packet.h",
]
deps = [
"//base",
"//net",
]
}
fuzzer_test("u2f_message_fuzzer") {
sources = [
"u2f_message_fuzzer.cc",
]
deps = [
":u2f",
"//net",
]
libfuzzer_options = [ "max_len=2048" ]
}
include_rules = [
"+net/base",
]
// Copyright 2016 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 "device/u2f/u2f_message.h"
#include "device/u2f/u2f_packet.h"
#include "net/base/io_buffer.h"
namespace device {
// static
scoped_refptr<U2fMessage> U2fMessage::Create(uint32_t channel_id,
Type type,
const std::vector<uint8_t>& data) {
if (data.size() > kMaxMessageSize)
return nullptr;
return make_scoped_refptr(new U2fMessage(channel_id, type, data));
}
// static
scoped_refptr<U2fMessage> U2fMessage::CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf) {
size_t remaining_size = 0;
if (buf == nullptr ||
static_cast<size_t>(buf->size()) > U2fPacket::kPacketSize ||
static_cast<size_t>(buf->size()) < kInitPacketHeader)
return nullptr;
scoped_refptr<U2fInitPacket> init_packet =
U2fInitPacket::CreateFromSerializedData(buf, &remaining_size);
if (init_packet == nullptr)
return nullptr;
return make_scoped_refptr(new U2fMessage(init_packet, remaining_size));
}
U2fMessage::U2fMessage(scoped_refptr<U2fInitPacket> init_packet,
size_t remaining_size)
: remaining_size_(remaining_size) {
channel_id_ = init_packet->channel_id();
packets_.push_back(init_packet);
}
U2fMessage::U2fMessage(uint32_t channel_id,
Type type,
const std::vector<uint8_t>& data)
: packets_(), remaining_size_(), channel_id_(channel_id) {
size_t remaining_bytes = data.size();
uint8_t sequence = 0;
std::vector<uint8_t>::const_iterator first = data.begin();
std::vector<uint8_t>::const_iterator last;
if (remaining_bytes > kInitPacketDataSize) {
last = data.begin() + kInitPacketDataSize;
remaining_bytes -= kInitPacketDataSize;
} else {
last = data.begin() + remaining_bytes;
remaining_bytes = 0;
}
packets_.push_back(make_scoped_refptr(
new U2fInitPacket(channel_id, static_cast<uint8_t>(type),
std::vector<uint8_t>(first, last), data.size())));
while (remaining_bytes > 0) {
first = last;
if (remaining_bytes > kContinuationPacketDataSize) {
last = first + kContinuationPacketDataSize;
remaining_bytes -= kContinuationPacketDataSize;
} else {
last = first + remaining_bytes;
remaining_bytes = 0;
}
packets_.push_back(make_scoped_refptr(new U2fContinuationPacket(
channel_id, sequence, std::vector<uint8_t>(first, last))));
sequence++;
}
}
U2fMessage::~U2fMessage() {}
std::list<scoped_refptr<U2fPacket>>::const_iterator U2fMessage::begin() {
return packets_.cbegin();
}
std::list<scoped_refptr<U2fPacket>>::const_iterator U2fMessage::end() {
return packets_.cend();
}
scoped_refptr<net::IOBufferWithSize> U2fMessage::PopNextPacket() {
if (NumPackets() > 0) {
scoped_refptr<net::IOBufferWithSize> buf =
packets_.front()->GetSerializedBuffer();
packets_.pop_front();
return buf;
}
return nullptr;
}
bool U2fMessage::AddContinuationPacket(
scoped_refptr<net::IOBufferWithSize> buf) {
size_t remaining_size = remaining_size_;
scoped_refptr<U2fContinuationPacket> cont_packet =
U2fContinuationPacket::CreateFromSerializedData(buf, &remaining_size);
// Reject packets with a different channel id
if (cont_packet == nullptr || channel_id_ != cont_packet->channel_id())
return false;
remaining_size_ = remaining_size;
packets_.push_back(cont_packet);
return true;
}
bool U2fMessage::MessageComplete() {
return remaining_size_ == 0;
}
std::vector<uint8_t> U2fMessage::GetMessagePayload() const {
std::vector<uint8_t> data;
for (const auto& packet : packets_) {
std::vector<uint8_t> packet_data = packet->GetPacketPayload();
data.insert(std::end(data), packet_data.cbegin(), packet_data.cend());
}
return data;
}
size_t U2fMessage::NumPackets() {
return packets_.size();
}
} // namespace device
// Copyright 2016 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.
#ifndef DEVICE_U2F_U2F_MESSAGE_H_
#define DEVICE_U2F_U2F_MESSAGE_H_
#include <list>
#include <vector>
#include "base/gtest_prod_util.h"
#include "device/u2f/u2f_packet.h"
namespace net {
class IOBufferWithSize;
} // namespace net
namespace device {
// U2fMessages are defined by the specification at
// http://fidoalliance.org/specs/u2f-specs-1.0-bt-nfc-id-amendment/fido-u2f-hid-protocol.html
// A U2fMessage object represents a list of U2fPackets. Basic information
// about the message are available through class methods.
class U2fMessage : public base::RefCountedThreadSafe<U2fMessage> {
public:
enum class Type : uint8_t {
CMD_PING = 0x81,
CMD_MSG = 0x83,
CMD_INIT = 0x86,
CMD_WINK = 0x88,
CMD_ERROR = 0xbf,
};
static scoped_refptr<U2fMessage> Create(uint32_t channel_id,
Type type,
const std::vector<uint8_t>& data);
// Reconstruct a message from serialized message data
static scoped_refptr<U2fMessage> CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf);
// Pop front of queue with next packet
scoped_refptr<net::IOBufferWithSize> PopNextPacket();
// Adds a continuation packet to the packet list, from the serialized
// response value
bool AddContinuationPacket(scoped_refptr<net::IOBufferWithSize> packet_buf);
size_t NumPackets();
// Returns entire message payload
std::vector<uint8_t> GetMessagePayload() const;
uint32_t channel_id() { return channel_id_; }
// Message construction complete
bool MessageComplete();
std::list<scoped_refptr<U2fPacket>>::const_iterator begin();
std::list<scoped_refptr<U2fPacket>>::const_iterator end();
protected:
virtual ~U2fMessage();
private:
friend class base::RefCountedThreadSafe<U2fMessage>;
FRIEND_TEST_ALL_PREFIXES(U2fMessageTest, TestMaxLengthPacketConstructors);
FRIEND_TEST_ALL_PREFIXES(U2fMessageTest, TestMessagePartitoning);
FRIEND_TEST_ALL_PREFIXES(U2fMessageTest, TestDeconstruct);
FRIEND_TEST_ALL_PREFIXES(U2fMessageTest, TestMaxSize);
FRIEND_TEST_ALL_PREFIXES(U2fMessageTest, TestDeserialize);
static constexpr size_t kInitPacketHeader = 7;
static constexpr size_t kContinuationPacketHeader = 5;
static constexpr size_t kMaxHidPacketSize = 64;
static constexpr size_t kInitPacketDataSize =
kMaxHidPacketSize - kInitPacketHeader;
static constexpr size_t kContinuationPacketDataSize =
kMaxHidPacketSize - kContinuationPacketHeader;
// Messages are limited to an init packet and 128 continuation packets
// Maximum payload length therefore is 64-7 + 128 * (64-5) = 7609 bytes
static constexpr size_t kMaxMessageSize = 7609;
U2fMessage(uint32_t channel_id, Type type, const std::vector<uint8_t>& data);
U2fMessage(scoped_refptr<U2fInitPacket> init_packet, size_t remaining_size);
std::list<scoped_refptr<U2fPacket>> packets_;
size_t remaining_size_;
uint32_t channel_id_;
};
} // namespace device
#endif // DEVICE_U2F_U2F_MESSAGE_H_
// Copyright 2016 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 <stddef.h>
#include <stdint.h>
#include <algorithm>
#include "net/base/io_buffer.h"
#include "u2f_message.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
size_t packet_size = 65;
size_t remaining_buffer = size;
uint8_t* start = const_cast<uint8_t*>(data);
scoped_refptr<net::IOBufferWithSize> buf(
new net::IOBufferWithSize(packet_size));
memcpy(buf->data(), start, std::min(packet_size, remaining_buffer));
scoped_refptr<device::U2fMessage> msg =
device::U2fMessage::CreateFromSerializedData(buf);
remaining_buffer -= std::min(remaining_buffer, packet_size);
start += packet_size;
while (remaining_buffer > 0) {
size_t buffer_size = std::min(packet_size, remaining_buffer);
scoped_refptr<net::IOBufferWithSize> tmp_buf(
new net::IOBufferWithSize(buffer_size));
memcpy(tmp_buf->data(), start, buffer_size);
msg->AddContinuationPacket(tmp_buf);
remaining_buffer -= std::min(remaining_buffer, buffer_size);
start += buffer_size;
}
return 0;
}
// Copyright 2016 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 "device/u2f/u2f_message.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
class U2fMessageTest : public testing::Test {};
// Packets should be 64 bytes + 1 report ID byte
TEST_F(U2fMessageTest, TestPacketSize) {
uint32_t channel_id = 0x05060708;
std::vector<uint8_t> data;
scoped_refptr<U2fPacket> init_packet(
new U2fInitPacket(channel_id, 0, data, data.size()));
EXPECT_EQ(65, init_packet->GetSerializedBuffer()->size());
scoped_refptr<U2fPacket> continuation_packet(
new U2fContinuationPacket(channel_id, 0, data));
EXPECT_EQ(65, continuation_packet->GetSerializedBuffer()->size());
}
/*
* U2f Init Packets are of the format:
* Byte 0: 0
* Byte 1-4: Channel ID
* Byte 5: Command byte
* Byte 6-7: Big Endian size of data
* Byte 8-n: Data block
*
* Remaining buffer is padded with 0
*/
TEST_F(U2fMessageTest, TestPacketData) {
uint32_t channel_id = 0xF5060708;
std::vector<uint8_t> data{10, 11};
uint8_t cmd = static_cast<uint8_t>(U2fMessage::Type::CMD_WINK);
scoped_refptr<U2fPacket> init_packet(
new U2fInitPacket(channel_id, cmd, data, data.size()));
int index = 0;
EXPECT_EQ(0, init_packet->GetSerializedBuffer()->data()[index++]);
EXPECT_EQ((channel_id >> 24) & 0xff,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ((channel_id >> 16) & 0xff,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ((channel_id >> 8) & 0xff,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ(channel_id & 0xff,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ(cmd, static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ(data.size() >> 8,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ(data.size() & 0xff,
static_cast<uint8_t>(
init_packet->GetSerializedBuffer()->data()[index++]));
EXPECT_EQ(data.at(0), init_packet->GetSerializedBuffer()->data()[index++]);
EXPECT_EQ(data.at(1), init_packet->GetSerializedBuffer()->data()[index++]);
for (; index < init_packet->GetSerializedBuffer()->size(); index++) {
EXPECT_EQ(0, init_packet->GetSerializedBuffer()->data()[index])
<< "mismatch at index " << index;
}
}
TEST_F(U2fMessageTest, TestPacketConstructors) {
uint32_t channel_id = 0x05060708;
std::vector<uint8_t> data{10, 11};
uint8_t cmd = static_cast<uint8_t>(U2fMessage::Type::CMD_WINK);
scoped_refptr<U2fInitPacket> orig_packet(
new U2fInitPacket(channel_id, cmd, data, data.size()));
size_t payload_length = static_cast<size_t>(orig_packet->payload_length());
scoped_refptr<U2fInitPacket> reconstructed_packet =
U2fInitPacket::CreateFromSerializedData(
orig_packet->GetSerializedBuffer(), &payload_length);
EXPECT_EQ(orig_packet->command(), reconstructed_packet->command());
EXPECT_EQ(orig_packet->payload_length(),
reconstructed_packet->payload_length());
EXPECT_THAT(orig_packet->GetPacketPayload(),
testing::ContainerEq(reconstructed_packet->GetPacketPayload()));
EXPECT_EQ(channel_id, reconstructed_packet->channel_id());
ASSERT_EQ(orig_packet->GetSerializedBuffer()->size(),
reconstructed_packet->GetSerializedBuffer()->size());
for (size_t i = 0;
i < static_cast<size_t>(orig_packet->GetSerializedBuffer()->size());
++i) {
EXPECT_EQ(orig_packet->GetSerializedBuffer()->data()[i],
reconstructed_packet->GetSerializedBuffer()->data()[i]);
}
}
TEST_F(U2fMessageTest, TestMaxLengthPacketConstructors) {
uint32_t channel_id = 0xAAABACAD;
std::vector<uint8_t> data;
for (size_t i = 0; i < U2fMessage::kMaxMessageSize; ++i)
data.push_back(static_cast<uint8_t>(i % 0xff));
U2fMessage::Type cmd = U2fMessage::Type::CMD_MSG;
scoped_refptr<U2fMessage> orig_msg =
U2fMessage::Create(channel_id, cmd, data);
auto it = orig_msg->begin();
scoped_refptr<U2fMessage> new_msg =
U2fMessage::CreateFromSerializedData((*it)->GetSerializedBuffer());
it++;
for (; it != orig_msg->end(); ++it)
new_msg->AddContinuationPacket((*it)->GetSerializedBuffer());
auto orig_it = orig_msg->begin();
auto new_it = new_msg->begin();
for (; orig_it != orig_msg->end() && new_it != new_msg->end();
++orig_it, ++new_it) {
EXPECT_THAT((*orig_it)->GetPacketPayload(),
testing::ContainerEq((*new_it)->GetPacketPayload()));
EXPECT_EQ((*orig_it)->channel_id(), (*new_it)->channel_id());
ASSERT_EQ((*orig_it)->GetSerializedBuffer()->size(),
(*new_it)->GetSerializedBuffer()->size());
for (size_t i = 0;
i < static_cast<size_t>((*new_it)->GetSerializedBuffer()->size());
++i) {
EXPECT_EQ((*orig_it)->GetSerializedBuffer()->data()[i],
(*new_it)->GetSerializedBuffer()->data()[i]);
}
}
}
TEST_F(U2fMessageTest, TestMessagePartitoning) {
uint32_t channel_id = 0x01010203;
std::vector<uint8_t> data(U2fMessage::kInitPacketDataSize + 1);
scoped_refptr<U2fMessage> two_packet_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
EXPECT_EQ(2U, two_packet_message->NumPackets());
data.resize(U2fMessage::kInitPacketDataSize);
scoped_refptr<U2fMessage> one_packet_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
EXPECT_EQ(1U, one_packet_message->NumPackets());
data.resize(U2fMessage::kInitPacketDataSize +
U2fMessage::kContinuationPacketDataSize + 1);
scoped_refptr<U2fMessage> three_packet_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
EXPECT_EQ(3U, three_packet_message->NumPackets());
}
TEST_F(U2fMessageTest, TestMaxSize) {
uint32_t channel_id = 0x00010203;
std::vector<uint8_t> data(U2fMessage::kMaxMessageSize + 1);
scoped_refptr<U2fMessage> oversize_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
EXPECT_EQ(nullptr, oversize_message);
}
TEST_F(U2fMessageTest, TestDeconstruct) {
uint32_t channel_id = 0x0A0B0C0D;
std::vector<uint8_t> data(U2fMessage::kMaxMessageSize, 0x7F);
scoped_refptr<U2fMessage> filled_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
EXPECT_THAT(data, testing::ContainerEq(filled_message->GetMessagePayload()));
}
TEST_F(U2fMessageTest, TestDeserialize) {
uint32_t channel_id = 0x0A0B0C0D;
std::vector<uint8_t> data(U2fMessage::kMaxMessageSize);
scoped_refptr<U2fMessage> orig_message =
U2fMessage::Create(channel_id, U2fMessage::Type::CMD_PING, data);
std::list<scoped_refptr<net::IOBufferWithSize>> orig_list;
scoped_refptr<net::IOBufferWithSize> buf = orig_message->PopNextPacket();
orig_list.push_back(buf);
scoped_refptr<U2fMessage> new_message =
U2fMessage::CreateFromSerializedData(buf);
while (!new_message->MessageComplete()) {
buf = orig_message->PopNextPacket();
orig_list.push_back(buf);
new_message->AddContinuationPacket(buf);
}
while ((buf = new_message->PopNextPacket())) {
ASSERT_EQ(buf->size(), orig_list.front()->size());
EXPECT_EQ(0, memcmp(buf->data(), orig_list.front()->data(), buf->size()));
orig_list.pop_front();
}
}
} // namespace device
// Copyright 2016 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 "device/u2f/u2f_packet.h"
#include "net/base/io_buffer.h"
namespace device {
U2fPacket::U2fPacket(const std::vector<uint8_t> data, const uint32_t channel_id)
: data_(data), channel_id_(channel_id) {}
U2fPacket::U2fPacket() {}
U2fPacket::~U2fPacket() {}
scoped_refptr<net::IOBufferWithSize> U2fPacket::GetSerializedBuffer() {
if (serialized_)
return serialized_;
else
return make_scoped_refptr(new net::IOBufferWithSize(0));
}
std::vector<uint8_t> U2fPacket::GetPacketPayload() const {
return data_;
}
// U2F Initialization packet is defined as:
// Offset Length
// 0 4 Channel ID
// 4 1 Command ID
// 5 1 High order packet payload size
// 6 1 Low order packet payload size
// 7 (s-7) Payload data
U2fInitPacket::U2fInitPacket(const uint32_t channel_id,
const uint8_t cmd,
const std::vector<uint8_t> data,
const uint16_t payload_length)
: U2fPacket(data, channel_id), command_(cmd) {
serialized_ = new net::IOBufferWithSize(kPacketSize);
size_t index = 0;
// Byte at offset 0 is the report ID, which is always 0
serialized_->data()[index++] = 0;
serialized_->data()[index++] = (channel_id_ >> 24) & 0xff;
serialized_->data()[index++] = (channel_id_ >> 16) & 0xff;
serialized_->data()[index++] = (channel_id_ >> 8) & 0xff;
serialized_->data()[index++] = channel_id_ & 0xff;
serialized_->data()[index++] = command_;
payload_length_ = payload_length;
serialized_->data()[index++] = (payload_length >> 8) & 0xff;
serialized_->data()[index++] = payload_length & 0xff;
for (size_t data_idx = 0; data_idx < data_.size(); ++data_idx)
serialized_->data()[index++] = data_.at(data_idx);
while (static_cast<int>(index) < serialized_->size())
serialized_->data()[index++] = 0;
}
// static
scoped_refptr<U2fInitPacket> U2fInitPacket::CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size) {
if (buf == nullptr || remaining_size == nullptr || buf->size() != kPacketSize)
return nullptr;
return make_scoped_refptr(new U2fInitPacket(buf, remaining_size));
}
U2fInitPacket::U2fInitPacket(scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size) {
// Report ID is at index 0, so start at index 1 for channel ID
size_t index = 1;
uint16_t payload_size = 0;
uint16_t data_size = 0;
channel_id_ = (buf->data()[index++] & 0xff) << 24;
channel_id_ |= (buf->data()[index++] & 0xff) << 16;
channel_id_ |= (buf->data()[index++] & 0xff) << 8;
channel_id_ |= buf->data()[index++] & 0xff;
command_ = buf->data()[index++];
payload_size = buf->data()[index++] << 8;
payload_size |= static_cast<uint8_t>(buf->data()[index++]);
payload_length_ = payload_size;
// Check to see if payload is less than maximum size and padded with 0s
data_size =
std::min(payload_size, static_cast<uint16_t>(kPacketSize - index));
// Update remaining size to determine the payload size of follow on packets
*remaining_size = payload_size - data_size;
data_.insert(data_.end(), &buf->data()[index],
&buf->data()[index + data_size]);
for (int i = index + data_size; i < buf->size(); ++i)
buf->data()[i] = 0;
serialized_ = buf;
}
U2fInitPacket::~U2fInitPacket() {}
// U2F Continuation packet is defined as:
// Offset Length
// 0 4 Channel ID
// 4 1 Packet sequence 0x00..0x7f
// 5 (s-5) Payload data
U2fContinuationPacket::U2fContinuationPacket(const uint32_t channel_id,
const uint8_t sequence,
std::vector<uint8_t> data)
: U2fPacket(data, channel_id), sequence_(sequence) {
serialized_ = new net::IOBufferWithSize(kPacketSize);
size_t index = 0;
// Byte at offset 0 is the report ID, which is always 0
serialized_->data()[index++] = 0;
serialized_->data()[index++] = (channel_id_ >> 24) & 0xff;
serialized_->data()[index++] = (channel_id_ >> 16) & 0xff;
serialized_->data()[index++] = (channel_id_ >> 8) & 0xff;
serialized_->data()[index++] = channel_id_ & 0xff;
serialized_->data()[index++] = sequence_;
for (size_t idx = 0; idx < data_.size(); ++idx)
serialized_->data()[index++] = data_.at(idx);
while (static_cast<int>(index) < serialized_->size())
serialized_->data()[index++] = 0;
}
// static
scoped_refptr<U2fContinuationPacket>
U2fContinuationPacket::CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size) {
if (buf == nullptr || remaining_size == nullptr || buf->size() != kPacketSize)
return nullptr;
return make_scoped_refptr(new U2fContinuationPacket(buf, remaining_size));
}
U2fContinuationPacket::U2fContinuationPacket(
scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size) {
// Report ID is at index 0, so start at index 1 for channel ID
size_t index = 1;
size_t data_size;
channel_id_ = (buf->data()[index++] & 0xff) << 24;
channel_id_ |= (buf->data()[index++] & 0xff) << 16;
channel_id_ |= (buf->data()[index++] & 0xff) << 8;
channel_id_ |= buf->data()[index++] & 0xff;
sequence_ = buf->data()[index++];
// Check to see if packet payload is less than maximum size and padded with 0s
data_size = std::min(*remaining_size, kPacketSize - index);
*remaining_size -= data_size;
data_.insert(std::end(data_), &buf->data()[index],
&buf->data()[index + data_size]);
// Incoming buffer may not be padded with 0's, so manually update buffer
for (int i = index + data_size; i < buf->size(); ++i)
buf->data()[i] = 0;
serialized_ = buf;
}
U2fContinuationPacket::~U2fContinuationPacket() {}
} // namespace device
// Copyright 2016 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.
#ifndef DEVICE_U2F_U2F_PACKET_H_
#define DEVICE_U2F_U2F_PACKET_H_
#include <algorithm>
#include <vector>
#include "base/memory/ref_counted.h"
namespace net {
class IOBufferWithSize;
} // namespace net
namespace device {
// U2fPackets are defined by the specification at
// http://fidoalliance.org/specs/u2f-specs-1.0-bt-nfc-id-amendment/fido-u2f-hid-protocol.html
// Packets are one of two types, initialization packets and continuation
// packets,
// (U2fInitPacket and U2fContinuationPacket). U2fPackets have header information
// and a payload. If a U2fInitPacket cannot store the entire payload, further
// payload information is stored in U2fContinuationPackets.
class U2fPacket : public base::RefCountedThreadSafe<U2fPacket> {
public:
U2fPacket(const std::vector<uint8_t> data, uint32_t channel_id);
scoped_refptr<net::IOBufferWithSize> GetSerializedBuffer();
std::vector<uint8_t> GetPacketPayload() const;
uint32_t channel_id() { return channel_id_; }
protected:
U2fPacket();
virtual ~U2fPacket();
// Packet size of 64 bytes + 1 byte report ID
static constexpr size_t kPacketSize = 65;
std::vector<uint8_t> data_;
uint32_t channel_id_;
scoped_refptr<net::IOBufferWithSize> serialized_;
private:
friend class base::RefCountedThreadSafe<U2fPacket>;
friend class U2fMessage;
};
// U2fInitPacket, based on the U2f specification consists of a header with data
// that is serialized into a IOBuffer. A channel identifier is allocated by the
// U2F device to ensure its system-wide uniqueness. Command identifiers
// determine the type of message the packet corresponds to. Payload length
// is the length of the entire message payload, and the data is only the portion
// of the payload that will fit into the U2fInitPacket.
class U2fInitPacket : public U2fPacket {
public:
U2fInitPacket(uint32_t channel_id,
uint8_t cmd,
const std::vector<uint8_t> data,
uint16_t payload_length);
// Creates a packet from the serialized data of an initialization packet. As
// this is the first packet, the payload length of the entire message will be
// included within the serialized data. Remaining size will be returned to
// inform the callee how many additional packets to expect.
static scoped_refptr<U2fInitPacket> CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size);
uint8_t command() { return command_; }
uint16_t payload_length() { return payload_length_; }
protected:
~U2fInitPacket() final;
private:
U2fInitPacket(scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size);
uint8_t command_;
uint16_t payload_length_;
};
// U2fContinuationPacket, based on the U2f Specification consists of a header
// with data that is serialized into an IOBuffer. The channel identifier will
// be identical to the identifier in all other packets of the message. The
// packet sequence will be the sequence number of this particular packet, from
// 0x00 to 0x7f.
class U2fContinuationPacket : public U2fPacket {
public:
U2fContinuationPacket(const uint32_t channel_id,
const uint8_t sequence,
std::vector<uint8_t> data);
// Creates a packet from the serialized data of a continuation packet. As an
// U2fInitPacket would have arrived earlier with the total payload size,
// the remaining size should be passed to inform the packet of how much data
// to expect.
static scoped_refptr<U2fContinuationPacket> CreateFromSerializedData(
scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size);
uint8_t sequence() { return sequence_; }
protected:
~U2fContinuationPacket() final;
private:
U2fContinuationPacket(scoped_refptr<net::IOBufferWithSize> buf,
size_t* remaining_size);
uint8_t sequence_;
};
} // namespace device
#endif // DEVICE_U2F_U2F_PACKET_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment