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#include "FidoHidMessage.h"
32
33#if ENABLE(WEB_AUTHN)
34
35#include "FidoParsingUtils.h"
36#include <wtf/Optional.h>
37#include <wtf/Vector.h>
38
39namespace fido {
40
41// static
42Optional<FidoHidMessage> FidoHidMessage::create(uint32_t channelId, FidoHidDeviceCommand type, const Vector<uint8_t>& data)
43{
44 if (data.size() > kHidMaxMessageSize)
45 return WTF::nullopt;
46
47 switch (type) {
48 case FidoHidDeviceCommand::kPing:
49 break;
50 case FidoHidDeviceCommand::kMsg:
51 case FidoHidDeviceCommand::kCbor: {
52 if (data.isEmpty())
53 return WTF::nullopt;
54 break;
55 }
56
57 case FidoHidDeviceCommand::kCancel:
58 case FidoHidDeviceCommand::kWink: {
59 if (!data.isEmpty())
60 return WTF::nullopt;
61 break;
62 }
63 case FidoHidDeviceCommand::kLock: {
64 if (data.size() != 1 || data[0] > kHidMaxLockSeconds)
65 return WTF::nullopt;
66 break;
67 }
68 case FidoHidDeviceCommand::kInit: {
69 if (data.size() != 8)
70 return WTF::nullopt;
71 break;
72 }
73 case FidoHidDeviceCommand::kKeepAlive:
74 case FidoHidDeviceCommand::kError:
75 if (data.size() != 1)
76 return WTF::nullopt;
77 }
78
79 return FidoHidMessage(channelId, type, data);
80}
81
82// static
83Optional<FidoHidMessage> FidoHidMessage::createFromSerializedData(const Vector<uint8_t>& serializedData)
84{
85 size_t remainingSize = 0;
86 if (serializedData.size() > kHidPacketSize || serializedData.size() < kHidInitPacketHeaderSize)
87 return WTF::nullopt;
88
89 auto initPacket = FidoHidInitPacket::createFromSerializedData(serializedData, &remainingSize);
90
91 if (!initPacket)
92 return WTF::nullopt;
93
94 return FidoHidMessage(WTFMove(initPacket), remainingSize);
95}
96
97bool FidoHidMessage::messageComplete() const
98{
99 return !m_remainingSize;
100}
101
102Vector<uint8_t> FidoHidMessage::getMessagePayload() const
103{
104 Vector<uint8_t> data;
105 size_t dataSize = 0;
106 for (const auto& packet : m_packets)
107 dataSize += packet->getPacketPayload().size();
108 data.reserveInitialCapacity(dataSize);
109
110 for (const auto& packet : m_packets) {
111 const auto& packet_data = packet->getPacketPayload();
112 data.appendVector(packet_data);
113 }
114
115 return data;
116}
117
118Vector<uint8_t> FidoHidMessage::popNextPacket()
119{
120 if (m_packets.isEmpty())
121 return { };
122
123 Vector<uint8_t> data = m_packets.first()->getSerializedData();
124 m_packets.removeFirst();
125 return data;
126}
127
128bool FidoHidMessage::addContinuationPacket(const Vector<uint8_t>& buf)
129{
130 size_t remainingSize = m_remainingSize;
131 auto contPacket = FidoHidContinuationPacket::createFromSerializedData(buf, &remainingSize);
132
133 // Reject packets with a different channel id.
134 if (!contPacket || m_channelId != contPacket->channelId())
135 return false;
136
137 m_remainingSize = remainingSize;
138 m_packets.append(WTFMove(contPacket));
139 return true;
140}
141
142size_t FidoHidMessage::numPackets() const
143{
144 return m_packets.size();
145}
146
147FidoHidMessage::FidoHidMessage(uint32_t channelId, FidoHidDeviceCommand type, const Vector<uint8_t>& data)
148 : m_channelId(channelId)
149{
150 uint8_t sequence = 0;
151
152 size_t pos = data.size() > kHidInitPacketDataSize ? kHidInitPacketDataSize : data.size();
153 m_packets.append(std::make_unique<FidoHidInitPacket>(channelId, type, getInitPacketData(data), data.size()));
154 for (; pos < data.size(); pos += kHidContinuationPacketDataSize)
155 m_packets.append(std::make_unique<FidoHidContinuationPacket>(channelId, sequence++, getContinuationPacketData(data, pos)));
156}
157
158FidoHidMessage::FidoHidMessage(std::unique_ptr<FidoHidInitPacket> initPacket, size_t remainingSize)
159 : m_remainingSize(remainingSize)
160{
161 m_channelId = initPacket->channelId();
162 m_cmd = initPacket->command();
163 m_packets.append(WTFMove(initPacket));
164}
165
166} // namespace fido
167
168#endif // ENABLE(WEB_AUTHN)
169