[WebAuthN] Import CTAP HID message and packet structure from Chromium
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebCore / FidoHidMessageTest.cpp
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2018 Apple Inc. All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //    * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //    * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //    * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31
32 #if ENABLE(WEB_AUTHN)
33
34 #include <WebCore/FidoConstants.h>
35 #include <WebCore/FidoHidMessage.h>
36 #include <WebCore/FidoHidPacket.h>
37 #include <wtf/Deque.h>
38 #include <wtf/Vector.h>
39
40 namespace TestWebKitAPI {
41
42 using namespace fido;
43
44 // Packets should be 64 bytes + 1 report ID byte.
45 TEST(FidoHidMessageTest, TestPacketSize)
46 {
47     uint32_t channelId = 0x05060708;
48     Vector<uint8_t> data;
49
50     auto initPacket = std::make_unique<FidoHidInitPacket>(channelId, FidoHidDeviceCommand::kInit, Vector<uint8_t>(data), data.size());
51     EXPECT_EQ(64u, initPacket->getSerializedData().size());
52
53     auto continuationPacket = std::make_unique<FidoHidContinuationPacket>(channelId, 0, WTFMove(data));
54     EXPECT_EQ(64u, continuationPacket->getSerializedData().size());
55 }
56
57 /*
58  * U2f Init Packets are of the format:
59  * Byte 0:    0
60  * Byte 1-4:  Channel ID
61  * Byte 5:    Command byte
62  * Byte 6-7:  Big Endian size of data
63  * Byte 8-n:  Data block
64  *
65  * Remaining buffer is padded with 0
66  */
67 TEST(FidoHidMessageTest, TestPacketData)
68 {
69     uint32_t channelId = 0xF5060708;
70     Vector<uint8_t> data {10, 11};
71     FidoHidDeviceCommand cmd = FidoHidDeviceCommand::kWink;
72     auto initPacket = std::make_unique<FidoHidInitPacket>(channelId, cmd, Vector<uint8_t>(data), data.size());
73     size_t index = 0;
74
75     Vector<uint8_t> serialized = initPacket->getSerializedData();
76     EXPECT_EQ((channelId >> 24) & 0xff, serialized[index++]);
77     EXPECT_EQ((channelId >> 16) & 0xff, serialized[index++]);
78     EXPECT_EQ((channelId >> 8) & 0xff, serialized[index++]);
79     EXPECT_EQ(channelId & 0xff, serialized[index++]);
80     EXPECT_EQ(static_cast<uint8_t>(cmd), serialized[index++] & 0x7f);
81
82     EXPECT_EQ(data.size() >> 8, serialized[index++]);
83     EXPECT_EQ(data.size() & 0xff, serialized[index++]);
84     EXPECT_EQ(data[0], serialized[index++]);
85     EXPECT_EQ(data[1], serialized[index++]);
86     for (; index < serialized.size(); index++)
87         EXPECT_EQ(0, serialized[index]) << "mismatch at index " << index;
88 }
89
90 TEST(FidoHidMessageTest, TestPacketConstructors)
91 {
92     uint32_t channelId = 0x05060708;
93     Vector<uint8_t> data {10, 11};
94     FidoHidDeviceCommand cmd = FidoHidDeviceCommand::kWink;
95     size_t length = data.size();
96     auto origPacket = std::make_unique<FidoHidInitPacket>(channelId, cmd, WTFMove(data), length);
97
98     size_t payloadLength = static_cast<size_t>(origPacket->payloadLength());
99     Vector<uint8_t> origData = origPacket->getSerializedData();
100
101     auto reconstructedPacket = FidoHidInitPacket::createFromSerializedData(origData, &payloadLength);
102     EXPECT_EQ(origPacket->command(), reconstructedPacket->command());
103     EXPECT_EQ(origPacket->payloadLength(), reconstructedPacket->payloadLength());
104     EXPECT_EQ(origPacket->getPacketPayload(), reconstructedPacket->getPacketPayload());
105
106     EXPECT_EQ(channelId, reconstructedPacket->channelId());
107
108     ASSERT_EQ(origPacket->getSerializedData().size(), reconstructedPacket->getSerializedData().size());
109     for (size_t index = 0; index < origPacket->getSerializedData().size(); ++index)
110         EXPECT_EQ(origPacket->getSerializedData()[index], reconstructedPacket->getSerializedData()[index]) << "mismatch at index " << index;
111 }
112
113 TEST(FidoHidMessageTest, TestMaxLengthPacketConstructors)
114 {
115     uint32_t channelId = 0xAAABACAD;
116     Vector<uint8_t> data;
117     for (size_t i = 0; i < kHidMaxMessageSize; ++i)
118         data.append(static_cast<uint8_t>(i % 0xff));
119
120     auto origMsg = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kMsg, data);
121     ASSERT_TRUE(origMsg);
122
123     const auto& originalMsgPackets = origMsg->getPacketsForTesting();
124     auto it = originalMsgPackets.begin();
125     auto msgData = (*it)->getSerializedData();
126     auto newMsg = FidoHidMessage::createFromSerializedData(msgData);
127     ++it;
128
129     for (; it != originalMsgPackets.end(); ++it) {
130         msgData = (*it)->getSerializedData();
131         EXPECT_TRUE(newMsg->addContinuationPacket(msgData));
132     }
133
134     auto origIt = originalMsgPackets.begin();
135     const auto& newMsgPackets = newMsg->getPacketsForTesting();
136     auto newMsgIt = newMsgPackets.begin();
137
138     EXPECT_EQ(origMsg->numPackets(), newMsg->numPackets());
139     for (; origIt != originalMsgPackets.end() || newMsgIt != newMsgPackets.end(); ++origIt, ++newMsgIt) {
140         EXPECT_EQ((*origIt)->getPacketPayload(), (*newMsgIt)->getPacketPayload());
141
142         EXPECT_EQ((*origIt)->channelId(), (*newMsgIt)->channelId());
143
144         ASSERT_EQ((*origIt)->getSerializedData().size(), (*newMsgIt)->getSerializedData().size());
145         for (size_t index = 0; index < (*origIt)->getSerializedData().size(); ++index)
146             EXPECT_EQ((*origIt)->getSerializedData()[index], (*newMsgIt)->getSerializedData()[index]) << "mismatch at index " << index;
147     }
148 }
149
150 TEST(FidoHidMessageTest, TestMessagePartitoning)
151 {
152     uint32_t channelId = 0x01010203;
153     Vector<uint8_t> data(kHidInitPacketDataSize + 1);
154     auto twoPacketMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
155     ASSERT_TRUE(twoPacketMessage);
156     EXPECT_EQ(2U, twoPacketMessage->numPackets());
157
158     data.resize(kHidInitPacketDataSize);
159     auto onePacketMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
160     ASSERT_TRUE(onePacketMessage);
161     EXPECT_EQ(1U, onePacketMessage->numPackets());
162
163     data.resize(kHidInitPacketDataSize + kHidContinuationPacketDataSize + 1);
164     auto threePacketMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
165     ASSERT_TRUE(threePacketMessage);
166     EXPECT_EQ(3U, threePacketMessage->numPackets());
167 }
168
169 TEST(FidoHidMessageTest, TestMaxSize)
170 {
171     uint32_t channelId = 0x00010203;
172     Vector<uint8_t> data(kHidMaxMessageSize + 1);
173     auto oversizeMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
174     EXPECT_FALSE(oversizeMessage);
175 }
176
177 TEST(FidoHidMessageTest, TestDeconstruct)
178 {
179     uint32_t channelId = 0x0A0B0C0D;
180     Vector<uint8_t> data(kHidMaxMessageSize, 0x7F);
181     auto filledMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
182     ASSERT_TRUE(filledMessage);
183     EXPECT_EQ(data, filledMessage->getMessagePayload());
184 }
185
186 TEST(FidoHidMessageTest, TestDeserialize)
187 {
188     uint32_t channelId = 0x0A0B0C0D;
189     Vector<uint8_t> data(kHidMaxMessageSize);
190
191     auto origMessage = FidoHidMessage::create(channelId, FidoHidDeviceCommand::kPing, data);
192     ASSERT_TRUE(origMessage);
193
194     Deque<Vector<uint8_t>> origList;
195     auto buf = origMessage->popNextPacket();
196     origList.append(buf);
197
198     auto newMessage = FidoHidMessage::createFromSerializedData(buf);
199     while (!newMessage->messageComplete()) {
200         buf = origMessage->popNextPacket();
201         origList.append(buf);
202         newMessage->addContinuationPacket(buf);
203     }
204
205     while (!(buf = newMessage->popNextPacket()).isEmpty()) {
206         EXPECT_EQ(buf, origList.first());
207         origList.removeFirst();
208     }
209 }
210
211 } // namespace TestWebKitAPI
212
213 #endif // ENABLE(WEB_AUTHN)