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 "CBORWriter.h"
32
33#if ENABLE(WEB_AUTHN)
34
35#include "CBORBinary.h"
36
37namespace cbor {
38
39CBORWriter::~CBORWriter()
40{
41}
42
43// static
44Optional<Vector<uint8_t>> CBORWriter::write(const CBORValue& node, size_t maxNestingLevel)
45{
46 Vector<uint8_t> cbor;
47 CBORWriter writer(&cbor);
48 if (writer.encodeCBOR(node, static_cast<int>(maxNestingLevel)))
49 return cbor;
50 return WTF::nullopt;
51}
52
53CBORWriter::CBORWriter(Vector<uint8_t>* cbor)
54 : m_encodedCBOR(cbor)
55{
56}
57
58bool CBORWriter::encodeCBOR(const CBORValue& node, int maxNestingLevel)
59{
60 if (maxNestingLevel < 0)
61 return false;
62
63 switch (node.type()) {
64 case CBORValue::Type::None: {
65 startItem(CBORValue::Type::ByteString, 0);
66 return true;
67 }
68 // Represents unsigned integers.
69 case CBORValue::Type::Unsigned: {
70 int64_t value = node.getUnsigned();
71 startItem(CBORValue::Type::Unsigned, static_cast<uint64_t>(value));
72 return true;
73 }
74 // Represents negative integers.
75 case CBORValue::Type::Negative: {
76 int64_t value = node.getNegative();
77 startItem(CBORValue::Type::Negative, static_cast<uint64_t>(-(value + 1)));
78 return true;
79 }
80 // Represents a byte string.
81 case CBORValue::Type::ByteString: {
82 const CBORValue::BinaryValue& bytes = node.getByteString();
83 startItem(CBORValue::Type::ByteString, static_cast<uint64_t>(bytes.size()));
84 // Add the bytes.
85 m_encodedCBOR->appendVector(bytes);
86 return true;
87 }
88 case CBORValue::Type::String: {
89 // WTFString uses UTF16 but RFC7049 requires UTF8
90 auto utf8String = node.getString().utf8();
91 startItem(CBORValue::Type::String, static_cast<uint64_t>(utf8String.length()));
92 // Add the characters.
93 m_encodedCBOR->append(utf8String.data(), utf8String.length());
94 return true;
95 }
96 // Represents an array.
97 case CBORValue::Type::Array: {
98 const CBORValue::ArrayValue& array = node.getArray();
99 startItem(CBORValue::Type::Array, array.size());
100 for (const auto& value : array) {
101 if (!encodeCBOR(value, maxNestingLevel - 1))
102 return false;
103 }
104 return true;
105 }
106 // Represents a map.
107 case CBORValue::Type::Map: {
108 const CBORValue::MapValue& map = node.getMap();
109 startItem(CBORValue::Type::Map, map.size());
110
111 for (const auto& value : map) {
112 if (!encodeCBOR(value.first, maxNestingLevel - 1))
113 return false;
114 if (!encodeCBOR(value.second, maxNestingLevel - 1))
115 return false;
116 }
117 return true;
118 }
119 // Represents a simple value.
120 case CBORValue::Type::SimpleValue: {
121 const CBORValue::SimpleValue simpleValue = node.getSimpleValue();
122 startItem(CBORValue::Type::SimpleValue, static_cast<uint64_t>(simpleValue));
123 return true;
124 }
125 default:
126 break;
127 }
128
129 ASSERT_NOT_REACHED();
130 return false;
131}
132
133void CBORWriter::startItem(CBORValue::Type type, uint64_t size)
134{
135 m_encodedCBOR->append(static_cast<uint8_t>(static_cast<unsigned>(type) << constants::kMajorTypeBitShift));
136 setUint(size);
137}
138
139void CBORWriter::setAdditionalInformation(uint8_t additionalInformation)
140{
141 ASSERT(!m_encodedCBOR->isEmpty());
142 ASSERT((additionalInformation & constants::kAdditionalInformationMask) == additionalInformation);
143 m_encodedCBOR->last() |= (additionalInformation & constants::kAdditionalInformationMask);
144}
145
146void CBORWriter::setUint(uint64_t value)
147{
148 size_t count = getNumUintBytes(value);
149 int shift = -1;
150 // Values under 24 are encoded directly in the initial byte.
151 // Otherwise, the last 5 bits of the initial byte contains the length
152 // of unsigned integer, which is encoded in following bytes.
153 switch (count) {
154 case 0:
155 setAdditionalInformation(static_cast<uint8_t>(value));
156 break;
157 case 1:
158 setAdditionalInformation(constants::kAdditionalInformation1Byte);
159 shift = 0;
160 break;
161 case 2:
162 setAdditionalInformation(constants::kAdditionalInformation2Bytes);
163 shift = 1;
164 break;
165 case 4:
166 setAdditionalInformation(constants::kAdditionalInformation4Bytes);
167 shift = 3;
168 break;
169 case 8:
170 setAdditionalInformation(constants::kAdditionalInformation8Bytes);
171 shift = 7;
172 break;
173 default:
174 ASSERT_NOT_REACHED();
175 break;
176 }
177 for (; shift >= 0; shift--)
178 m_encodedCBOR->append(0xFF & (value >> (shift * 8)));
179}
180
181size_t CBORWriter::getNumUintBytes(uint64_t value)
182{
183 if (value < 24)
184 return 0;
185 if (value <= 0xFF)
186 return 1;
187 if (value <= 0xFFFF)
188 return 2;
189 if (value <= 0xFFFFFFFF)
190 return 4;
191 return 8;
192}
193
194} // namespace cbor
195
196#endif // ENABLE(WEB_AUTHN)
197