fido_hid_message.cc 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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. #include "device/fido/hid/fido_hid_message.h"
  5. #include <algorithm>
  6. #include <numeric>
  7. #include <utility>
  8. #include "base/memory/ptr_util.h"
  9. #include "base/numerics/safe_conversions.h"
  10. #include "device/fido/fido_parsing_utils.h"
  11. namespace device {
  12. // static
  13. absl::optional<FidoHidMessage> FidoHidMessage::Create(
  14. uint32_t channel_id,
  15. FidoHidDeviceCommand type,
  16. size_t max_report_size,
  17. base::span<const uint8_t> data) {
  18. if (data.size() > kHidMaxMessageSize)
  19. return absl::nullopt;
  20. if (max_report_size <= kHidInitPacketHeaderSize ||
  21. max_report_size > kHidMaxPacketSize) {
  22. NOTREACHED();
  23. return absl::nullopt;
  24. }
  25. switch (type) {
  26. case FidoHidDeviceCommand::kPing:
  27. break;
  28. case FidoHidDeviceCommand::kMsg:
  29. case FidoHidDeviceCommand::kCbor: {
  30. if (data.empty())
  31. return absl::nullopt;
  32. break;
  33. }
  34. case FidoHidDeviceCommand::kCancel:
  35. case FidoHidDeviceCommand::kWink: {
  36. if (!data.empty())
  37. return absl::nullopt;
  38. break;
  39. }
  40. case FidoHidDeviceCommand::kLock: {
  41. if (data.size() != 1 || data[0] > kHidMaxLockSeconds)
  42. return absl::nullopt;
  43. break;
  44. }
  45. case FidoHidDeviceCommand::kInit: {
  46. if (data.size() != 8)
  47. return absl::nullopt;
  48. break;
  49. }
  50. case FidoHidDeviceCommand::kKeepAlive:
  51. case FidoHidDeviceCommand::kError:
  52. if (data.size() != 1)
  53. return absl::nullopt;
  54. }
  55. return FidoHidMessage(channel_id, type, max_report_size, data);
  56. }
  57. // static
  58. absl::optional<FidoHidMessage> FidoHidMessage::CreateFromSerializedData(
  59. base::span<const uint8_t> serialized_data) {
  60. size_t remaining_size = 0;
  61. if (serialized_data.size() > kHidMaxPacketSize ||
  62. serialized_data.size() < kHidInitPacketHeaderSize)
  63. return absl::nullopt;
  64. auto init_packet = FidoHidInitPacket::CreateFromSerializedData(
  65. serialized_data, &remaining_size);
  66. if (init_packet == nullptr)
  67. return absl::nullopt;
  68. return FidoHidMessage(std::move(init_packet), remaining_size);
  69. }
  70. FidoHidMessage::FidoHidMessage(FidoHidMessage&& that) = default;
  71. FidoHidMessage& FidoHidMessage::operator=(FidoHidMessage&& other) = default;
  72. FidoHidMessage::~FidoHidMessage() = default;
  73. bool FidoHidMessage::MessageComplete() const {
  74. return remaining_size_ == 0;
  75. }
  76. std::vector<uint8_t> FidoHidMessage::GetMessagePayload() const {
  77. std::vector<uint8_t> data;
  78. size_t data_size = 0;
  79. for (const auto& packet : packets_) {
  80. data_size += packet->GetPacketPayload().size();
  81. }
  82. data.reserve(data_size);
  83. for (const auto& packet : packets_) {
  84. const auto& packet_data = packet->GetPacketPayload();
  85. data.insert(std::end(data), packet_data.cbegin(), packet_data.cend());
  86. }
  87. return data;
  88. }
  89. std::vector<uint8_t> FidoHidMessage::PopNextPacket() {
  90. if (packets_.empty())
  91. return {};
  92. std::vector<uint8_t> data = packets_.front()->GetSerializedData();
  93. packets_.pop_front();
  94. return data;
  95. }
  96. bool FidoHidMessage::AddContinuationPacket(base::span<const uint8_t> buf) {
  97. size_t remaining_size = remaining_size_;
  98. auto cont_packet =
  99. FidoHidContinuationPacket::CreateFromSerializedData(buf, &remaining_size);
  100. // Reject packets with a different channel id.
  101. if (!cont_packet || channel_id_ != cont_packet->channel_id())
  102. return false;
  103. remaining_size_ = remaining_size;
  104. packets_.push_back(std::move(cont_packet));
  105. return true;
  106. }
  107. size_t FidoHidMessage::NumPackets() const {
  108. return packets_.size();
  109. }
  110. FidoHidMessage::FidoHidMessage(uint32_t channel_id,
  111. FidoHidDeviceCommand type,
  112. size_t max_report_size,
  113. base::span<const uint8_t> data)
  114. : channel_id_(channel_id) {
  115. static_assert(
  116. kHidInitPacketHeaderSize >= kHidContinuationPacketHeaderSize,
  117. "init header is expected to be larger than continuation header");
  118. DCHECK_GT(max_report_size, kHidInitPacketHeaderSize);
  119. const size_t init_packet_data_size =
  120. max_report_size - kHidInitPacketHeaderSize;
  121. const size_t continuation_packet_data_size =
  122. max_report_size - kHidContinuationPacketHeaderSize;
  123. uint8_t sequence = 0;
  124. auto init_data = data.first(std::min(init_packet_data_size, data.size()));
  125. packets_.push_back(std::make_unique<FidoHidInitPacket>(
  126. channel_id, type,
  127. std::vector<uint8_t>(init_data.begin(), init_data.end()), data.size()));
  128. data = data.subspan(init_data.size());
  129. for (auto cont_data :
  130. fido_parsing_utils::SplitSpan(data, continuation_packet_data_size)) {
  131. packets_.push_back(std::make_unique<FidoHidContinuationPacket>(
  132. channel_id, sequence++, fido_parsing_utils::Materialize(cont_data)));
  133. }
  134. }
  135. FidoHidMessage::FidoHidMessage(std::unique_ptr<FidoHidInitPacket> init_packet,
  136. size_t remaining_size)
  137. : remaining_size_(remaining_size) {
  138. channel_id_ = init_packet->channel_id();
  139. cmd_ = init_packet->command();
  140. packets_.push_back(std::move(init_packet));
  141. }
  142. } // namespace device