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 "device/fido/hid/fido_hid_message.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/numerics/safe_conversions.h"
9 #include "device/fido/fido_constants.h"
10 #include "device/fido/hid/fido_hid_packet.h"
11 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace device {
15
16 static const size_t kDefaultInitDataSize =
17 kHidMaxPacketSize - kHidInitPacketHeaderSize;
18 static const size_t kDefaultContinuationDataSize =
19 kHidMaxPacketSize - kHidContinuationPacketHeaderSize;
20
21 /*
22 * U2f Init Packets are of the format:
23 * Byte 0: 0
24 * Byte 1-4: Channel ID
25 * Byte 5: Command byte
26 * Byte 6-7: Big Endian size of data
27 * Byte 8-n: Data block
28 *
29 * Remaining buffer is padded with 0
30 */
TEST(FidoHidMessageTest,TestPacketData)31 TEST(FidoHidMessageTest, TestPacketData) {
32 uint32_t channel_id = 0xF5060708;
33 std::vector<uint8_t> data{10, 11};
34 FidoHidDeviceCommand cmd = FidoHidDeviceCommand::kWink;
35 auto init_packet =
36 std::make_unique<FidoHidInitPacket>(channel_id, cmd, data, data.size());
37 size_t index = 0;
38
39 std::vector<uint8_t> serialized = init_packet->GetSerializedData();
40 EXPECT_EQ((channel_id >> 24) & 0xff, serialized[index++]);
41 EXPECT_EQ((channel_id >> 16) & 0xff, serialized[index++]);
42 EXPECT_EQ((channel_id >> 8) & 0xff, serialized[index++]);
43 EXPECT_EQ(channel_id & 0xff, serialized[index++]);
44 EXPECT_EQ(base::strict_cast<uint8_t>(cmd), serialized[index++] & 0x7f);
45
46 EXPECT_EQ(data.size() >> 8, serialized[index++]);
47 EXPECT_EQ(data.size() & 0xff, serialized[index++]);
48 EXPECT_EQ(data[0], serialized[index++]);
49 EXPECT_EQ(data[1], serialized[index++]);
50 for (; index < serialized.size(); index++)
51 EXPECT_EQ(0, serialized[index]) << "mismatch at index " << index;
52 }
53
TEST(FidoHidMessageTest,TestPacketConstructors)54 TEST(FidoHidMessageTest, TestPacketConstructors) {
55 uint32_t channel_id = 0x05060708;
56 std::vector<uint8_t> data{10, 11};
57 FidoHidDeviceCommand cmd = FidoHidDeviceCommand::kWink;
58 auto orig_packet =
59 std::make_unique<FidoHidInitPacket>(channel_id, cmd, data, data.size());
60
61 size_t payload_length = static_cast<size_t>(orig_packet->payload_length());
62 std::vector<uint8_t> orig_data = orig_packet->GetSerializedData();
63
64 auto reconstructed_packet =
65 FidoHidInitPacket::CreateFromSerializedData(orig_data, &payload_length);
66 EXPECT_EQ(orig_packet->command(), reconstructed_packet->command());
67 EXPECT_EQ(orig_packet->payload_length(),
68 reconstructed_packet->payload_length());
69 EXPECT_THAT(orig_packet->GetPacketPayload(),
70 ::testing::ContainerEq(reconstructed_packet->GetPacketPayload()));
71
72 EXPECT_EQ(channel_id, reconstructed_packet->channel_id());
73
74 ASSERT_EQ(orig_packet->GetSerializedData().size(),
75 reconstructed_packet->GetSerializedData().size());
76 for (size_t index = 0; index < orig_packet->GetSerializedData().size();
77 ++index) {
78 EXPECT_EQ(orig_packet->GetSerializedData()[index],
79 reconstructed_packet->GetSerializedData()[index])
80 << "mismatch at index " << index;
81 }
82 }
83
TEST(FidoHidMessageTest,TestMaxLengthPacketConstructors)84 TEST(FidoHidMessageTest, TestMaxLengthPacketConstructors) {
85 uint32_t channel_id = 0xAAABACAD;
86 std::vector<uint8_t> data;
87 for (size_t i = 0; i < kHidMaxMessageSize; ++i)
88 data.push_back(static_cast<uint8_t>(i % 0xff));
89
90 for (size_t report_size = kHidInitPacketHeaderSize + 1;
91 report_size <= kHidMaxPacketSize; report_size++) {
92 auto orig_msg = FidoHidMessage::Create(
93 channel_id, FidoHidDeviceCommand::kMsg, report_size, data);
94 ASSERT_TRUE(orig_msg);
95
96 const auto& original_msg_packets = orig_msg->GetPacketsForTesting();
97 auto it = original_msg_packets.begin();
98 auto msg_data = (*it)->GetSerializedData();
99 auto new_msg = FidoHidMessage::CreateFromSerializedData(msg_data);
100 it++;
101
102 for (; it != original_msg_packets.end(); ++it) {
103 msg_data = (*it)->GetSerializedData();
104 new_msg->AddContinuationPacket(msg_data);
105 }
106
107 EXPECT_EQ(new_msg->NumPackets(), orig_msg->NumPackets());
108
109 auto orig_it = original_msg_packets.begin();
110 const auto& new_msg_packets = new_msg->GetPacketsForTesting();
111 auto new_msg_it = new_msg_packets.begin();
112
113 for (; orig_it != original_msg_packets.end() &&
114 new_msg_it != new_msg_packets.end();
115 ++orig_it, ++new_msg_it) {
116 EXPECT_THAT((*orig_it)->GetPacketPayload(),
117 ::testing::ContainerEq((*new_msg_it)->GetPacketPayload()));
118
119 EXPECT_EQ((*orig_it)->channel_id(), (*new_msg_it)->channel_id());
120
121 ASSERT_EQ((*orig_it)->GetSerializedData().size(),
122 (*new_msg_it)->GetSerializedData().size());
123 for (size_t index = 0; index < (*orig_it)->GetSerializedData().size();
124 ++index) {
125 EXPECT_EQ((*orig_it)->GetSerializedData()[index],
126 (*new_msg_it)->GetSerializedData()[index])
127 << "mismatch at index " << index;
128 }
129 }
130
131 EXPECT_TRUE(orig_it == original_msg_packets.end());
132 EXPECT_TRUE(new_msg_it == new_msg_packets.end());
133 }
134 }
135
TEST(FidoHidMessageTest,TestMessagePartitoning)136 TEST(FidoHidMessageTest, TestMessagePartitoning) {
137 uint32_t channel_id = 0x01010203;
138 std::vector<uint8_t> data(kDefaultInitDataSize + 1);
139 auto two_packet_message = FidoHidMessage::Create(
140 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
141 ASSERT_TRUE(two_packet_message);
142 EXPECT_EQ(2U, two_packet_message->NumPackets());
143
144 data.resize(kDefaultInitDataSize);
145 auto one_packet_message = FidoHidMessage::Create(
146 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
147 ASSERT_TRUE(one_packet_message);
148 EXPECT_EQ(1U, one_packet_message->NumPackets());
149
150 data.resize(kDefaultInitDataSize + kDefaultContinuationDataSize + 1);
151 auto three_packet_message = FidoHidMessage::Create(
152 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
153 ASSERT_TRUE(three_packet_message);
154 EXPECT_EQ(3U, three_packet_message->NumPackets());
155
156 // With the minimal report size, only a single byte of data will fit in an
157 // init message, followed by three bytes in each continuation message.
158 data.resize(1 + 3 + 3);
159 auto three_small_messages =
160 FidoHidMessage::Create(channel_id, FidoHidDeviceCommand::kPing,
161 kHidInitPacketHeaderSize + 1, data);
162 ASSERT_TRUE(three_small_messages);
163 EXPECT_EQ(3U, three_small_messages->NumPackets());
164 }
165
TEST(FidoHidMessageTest,TooLarge)166 TEST(FidoHidMessageTest, TooLarge) {
167 std::vector<uint8_t> data;
168
169 #if DCHECK_IS_ON()
170 #define EXPECT_SIZE_FAILURE(x) EXPECT_DEATH_IF_SUPPORTED(x, "")
171 #else
172 #define EXPECT_SIZE_FAILURE(x) EXPECT_FALSE(x.has_value())
173 #endif
174
175 // kHidInitPacketHeaderSize is too small a report size to be valid.
176 EXPECT_SIZE_FAILURE(FidoHidMessage::Create(kHidBroadcastChannel,
177 FidoHidDeviceCommand::kPing,
178 kHidInitPacketHeaderSize, data));
179
180 // kHidMaxPacketSize + 1 is too large a report size.
181 EXPECT_SIZE_FAILURE(FidoHidMessage::Create(kHidBroadcastChannel,
182 FidoHidDeviceCommand::kPing,
183 kHidMaxPacketSize + 1, data));
184
185 #undef EXPECT_SIZE_FAILURE
186 }
187
TEST(FidoHidMessageTest,TestMaxSize)188 TEST(FidoHidMessageTest, TestMaxSize) {
189 uint32_t channel_id = 0x00010203;
190 std::vector<uint8_t> data(kHidMaxMessageSize + 1);
191 auto oversize_message = FidoHidMessage::Create(
192 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
193 EXPECT_FALSE(oversize_message);
194 }
195
TEST(FidoHidMessageTest,TestDeconstruct)196 TEST(FidoHidMessageTest, TestDeconstruct) {
197 uint32_t channel_id = 0x0A0B0C0D;
198 std::vector<uint8_t> data(kHidMaxMessageSize, 0x7F);
199 auto filled_message = FidoHidMessage::Create(
200 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
201 ASSERT_TRUE(filled_message);
202 EXPECT_THAT(data,
203 ::testing::ContainerEq(filled_message->GetMessagePayload()));
204 }
205
TEST(FidoHidMessageTest,TestDeserialize)206 TEST(FidoHidMessageTest, TestDeserialize) {
207 uint32_t channel_id = 0x0A0B0C0D;
208 std::vector<uint8_t> data(kHidMaxMessageSize);
209
210 auto orig_message = FidoHidMessage::Create(
211 channel_id, FidoHidDeviceCommand::kPing, kHidMaxPacketSize, data);
212 ASSERT_TRUE(orig_message);
213
214 base::circular_deque<std::vector<uint8_t>> orig_list;
215 auto buf = orig_message->PopNextPacket();
216 orig_list.push_back(buf);
217
218 auto new_message = FidoHidMessage::CreateFromSerializedData(buf);
219 while (!new_message->MessageComplete()) {
220 buf = orig_message->PopNextPacket();
221 orig_list.push_back(buf);
222 new_message->AddContinuationPacket(buf);
223 }
224
225 while (!(buf = new_message->PopNextPacket()).empty()) {
226 EXPECT_EQ(buf, orig_list.front());
227 orig_list.pop_front();
228 }
229 }
230
231 } // namespace device
232