blob: d95598c4a627232717d3c877b3072c35523b54b5 [file] [log] [blame]
// 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 "device/fido/fido_hid_message.h"
#include <algorithm>
#include <numeric>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "device/fido/fido_parsing_utils.h"
namespace device {
// static
base::Optional<FidoHidMessage> FidoHidMessage::Create(
uint32_t channel_id,
FidoHidDeviceCommand type,
base::span<const uint8_t> data) {
if (data.size() > kHidMaxMessageSize)
return base::nullopt;
switch (type) {
case FidoHidDeviceCommand::kPing:
break;
case FidoHidDeviceCommand::kMsg:
case FidoHidDeviceCommand::kCbor: {
if (data.empty())
return base::nullopt;
break;
}
case FidoHidDeviceCommand::kCancel:
case FidoHidDeviceCommand::kWink: {
if (!data.empty())
return base::nullopt;
break;
}
case FidoHidDeviceCommand::kLock: {
if (data.size() != 1 || data[0] > kHidMaxLockSeconds)
return base::nullopt;
break;
}
case FidoHidDeviceCommand::kInit: {
if (data.size() != 8)
return base::nullopt;
break;
}
case FidoHidDeviceCommand::kKeepAlive:
case FidoHidDeviceCommand::kError:
if (data.size() != 1)
return base::nullopt;
}
return FidoHidMessage(channel_id, type, data);
}
// static
base::Optional<FidoHidMessage> FidoHidMessage::CreateFromSerializedData(
base::span<const uint8_t> serialized_data) {
size_t remaining_size = 0;
if (serialized_data.size() > kHidPacketSize ||
serialized_data.size() < kHidInitPacketHeaderSize)
return base::nullopt;
auto init_packet = FidoHidInitPacket::CreateFromSerializedData(
serialized_data, &remaining_size);
if (init_packet == nullptr)
return base::nullopt;
return FidoHidMessage(std::move(init_packet), remaining_size);
}
FidoHidMessage::FidoHidMessage(FidoHidMessage&& that) = default;
FidoHidMessage& FidoHidMessage::operator=(FidoHidMessage&& other) = default;
FidoHidMessage::~FidoHidMessage() = default;
bool FidoHidMessage::MessageComplete() const {
return remaining_size_ == 0;
}
std::vector<uint8_t> FidoHidMessage::GetMessagePayload() const {
std::vector<uint8_t> data;
size_t data_size = 0;
for (const auto& packet : packets_) {
data_size += packet->GetPacketPayload().size();
}
data.reserve(data_size);
for (const auto& packet : packets_) {
const auto& packet_data = packet->GetPacketPayload();
data.insert(std::end(data), packet_data.cbegin(), packet_data.cend());
}
return data;
}
std::vector<uint8_t> FidoHidMessage::PopNextPacket() {
if (packets_.empty())
return {};
std::vector<uint8_t> data = packets_.front()->GetSerializedData();
packets_.pop_front();
return data;
}
bool FidoHidMessage::AddContinuationPacket(base::span<const uint8_t> buf) {
size_t remaining_size = remaining_size_;
auto cont_packet =
FidoHidContinuationPacket::CreateFromSerializedData(buf, &remaining_size);
// Reject packets with a different channel id.
if (!cont_packet || channel_id_ != cont_packet->channel_id())
return false;
remaining_size_ = remaining_size;
packets_.push_back(std::move(cont_packet));
return true;
}
size_t FidoHidMessage::NumPackets() const {
return packets_.size();
}
FidoHidMessage::FidoHidMessage(uint32_t channel_id,
FidoHidDeviceCommand type,
base::span<const uint8_t> data)
: channel_id_(channel_id) {
uint8_t sequence = 0;
auto init_data = data.first(std::min(kHidInitPacketDataSize, data.size()));
packets_.push_back(std::make_unique<FidoHidInitPacket>(
channel_id, type,
std::vector<uint8_t>(init_data.begin(), init_data.end()), data.size()));
data = data.subspan(init_data.size());
for (auto cont_data :
fido_parsing_utils::SplitSpan(data, kHidContinuationPacketDataSize)) {
packets_.push_back(std::make_unique<FidoHidContinuationPacket>(
channel_id, sequence++, fido_parsing_utils::Materialize(cont_data)));
}
}
FidoHidMessage::FidoHidMessage(std::unique_ptr<FidoHidInitPacket> init_packet,
size_t remaining_size)
: remaining_size_(remaining_size) {
channel_id_ = init_packet->channel_id();
cmd_ = init_packet->command();
packets_.push_back(std::move(init_packet));
}
} // namespace device