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 "FidoHidPacket.h"
32
33#if ENABLE(WEB_AUTHN)
34
35#include <algorithm>
36
37namespace fido {
38
39FidoHidPacket::FidoHidPacket(Vector<uint8_t>&& data, uint32_t channelId)
40 : m_data(WTFMove(data))
41 , m_channelId(channelId)
42{
43}
44
45// static
46std::unique_ptr<FidoHidInitPacket> FidoHidInitPacket::createFromSerializedData(const Vector<uint8_t>& serialized, size_t* remainingSize)
47{
48 if (!remainingSize || serialized.size() != kHidPacketSize)
49 return nullptr;
50
51 size_t index = 0;
52 auto channelId = (serialized[index++] & 0xff) << 24;
53 channelId |= (serialized[index++] & 0xff) << 16;
54 channelId |= (serialized[index++] & 0xff) << 8;
55 channelId |= serialized[index++] & 0xff;
56
57 auto command = static_cast<FidoHidDeviceCommand>(serialized[index++] & 0x7f);
58 if (!isFidoHidDeviceCommand(command))
59 return nullptr;
60
61 uint16_t payloadSize = serialized[index++] << 8;
62 payloadSize |= serialized[index++];
63
64 // Check to see if payload is less than maximum size and padded with 0s.
65 uint16_t dataSize = std::min(payloadSize, static_cast<uint16_t>(kHidPacketSize - index));
66
67 // Update remaining size to determine the payload size of follow on packets.
68 *remainingSize = payloadSize - dataSize;
69
70 auto data = Vector<uint8_t>();
71 data.append(serialized.begin() + index, dataSize);
72
73 return std::make_unique<FidoHidInitPacket>(channelId, command, WTFMove(data), payloadSize);
74}
75
76// U2F Initialization packet is defined as:
77// Offset Length
78// 0 4 Channel ID
79// 4 1 Command ID
80// 5 1 High order packet payload size
81// 6 1 Low order packet payload size
82// 7 (s-7) Payload data
83FidoHidInitPacket::FidoHidInitPacket(uint32_t channelId, FidoHidDeviceCommand cmd, Vector<uint8_t>&& data, uint16_t payloadLength)
84 : FidoHidPacket(WTFMove(data), channelId)
85 , m_command(cmd)
86 , m_payloadLength(payloadLength)
87{
88}
89
90Vector<uint8_t> FidoHidInitPacket::getSerializedData() const
91{
92 Vector<uint8_t> serialized;
93 serialized.reserveInitialCapacity(kHidPacketSize);
94 serialized.append((m_channelId >> 24) & 0xff);
95 serialized.append((m_channelId >> 16) & 0xff);
96 serialized.append((m_channelId >> 8) & 0xff);
97 serialized.append(m_channelId & 0xff);
98 serialized.append(static_cast<uint8_t>(m_command) | 0x80);
99 serialized.append((m_payloadLength >> 8) & 0xff);
100 serialized.append(m_payloadLength & 0xff);
101 serialized.append(m_data.begin(), m_data.size());
102 serialized.grow(kHidPacketSize);
103
104 return serialized;
105}
106
107// static
108std::unique_ptr<FidoHidContinuationPacket> FidoHidContinuationPacket::createFromSerializedData(const Vector<uint8_t>& serialized, size_t* remainingSize)
109{
110 if (!remainingSize || serialized.size() != kHidPacketSize)
111 return nullptr;
112
113 size_t index = 0;
114 auto channelId = (serialized[index++] & 0xff) << 24;
115 channelId |= (serialized[index++] & 0xff) << 16;
116 channelId |= (serialized[index++] & 0xff) << 8;
117 channelId |= serialized[index++] & 0xff;
118 auto sequence = serialized[index++];
119
120 // Check to see if packet payload is less than maximum size and padded with 0s.
121 size_t dataSize = std::min(*remainingSize, kHidPacketSize - index);
122 *remainingSize -= dataSize;
123 auto data = Vector<uint8_t>();
124 data.append(serialized.begin() + index, dataSize);
125
126 return std::make_unique<FidoHidContinuationPacket>(channelId, sequence, WTFMove(data));
127}
128
129// U2F Continuation packet is defined as:
130// Offset Length
131// 0 4 Channel ID
132// 4 1 Packet sequence 0x00..0x7f
133// 5 (s-5) Payload data
134FidoHidContinuationPacket::FidoHidContinuationPacket(const uint32_t channelId, const uint8_t sequence, Vector<uint8_t>&& data)
135 : FidoHidPacket(WTFMove(data), channelId)
136 , m_sequence(sequence)
137{
138}
139
140Vector<uint8_t> FidoHidContinuationPacket::getSerializedData() const
141{
142 Vector<uint8_t> serialized;
143 serialized.reserveInitialCapacity(kHidPacketSize);
144 serialized.append((m_channelId >> 24) & 0xff);
145 serialized.append((m_channelId >> 16) & 0xff);
146 serialized.append((m_channelId >> 8) & 0xff);
147 serialized.append(m_channelId & 0xff);
148 serialized.append(m_sequence);
149 serialized.append(m_data.begin(), m_data.size());
150 serialized.grow(kHidPacketSize);
151
152 return serialized;
153}
154
155} // namespace fido
156
157#endif // ENABLE(WEB_AUTHN)
158