1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <apploader/cbor.h>
18 #include <cstdint>
19 #include <limits>
20 #include <optional>
21 #include <span>
22 #include <vector>
23
24 #include "dice/cbor_reader.h"
25 #include "dice/cbor_writer.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28
29 #include "cppbor.h"
30 #include "cppbor_parse.h"
31
TEST(CborTest,ReadCborBoolean)32 TEST(CborTest, ReadCborBoolean) {
33 uint8_t buffer[8];
34 struct CborOut out;
35 CborOutInit(buffer, sizeof(buffer), &out);
36 CborWriteTrue(&out);
37 CborWriteFalse(&out);
38 CborWriteNull(&out);
39 ASSERT_FALSE(CborOutOverflowed(&out));
40
41 struct CborIn in;
42 CborInInit(buffer, CborOutSize(&out), &in);
43 std::optional<bool> res;
44
45 res = cbor::readCborBoolean(in);
46 ASSERT_TRUE(res.has_value());
47 EXPECT_TRUE(res.value());
48
49 res = cbor::readCborBoolean(in);
50 ASSERT_TRUE(res.has_value());
51 EXPECT_FALSE(res.value());
52
53 res = cbor::readCborBoolean(in);
54 EXPECT_FALSE(res.has_value());
55 }
56
TEST(CborTest,EncodedSizeOf)57 TEST(CborTest, EncodedSizeOf) {
58 std::vector<uint64_t> cases = {0,
59 1,
60 23,
61 24,
62 255,
63 256,
64 65535,
65 65536,
66 4294967295,
67 4294967296,
68 std::numeric_limits<uint64_t>::max()};
69
70 for (uint64_t val : cases) {
71 EXPECT_EQ(cppbor::headerSize(val), cbor::encodedSizeOf(val));
72 }
73 }
74
75 static std::vector<int64_t> int64_cases = {std::numeric_limits<int64_t>::min(),
76 -4294967297,
77 -4294967296,
78 -65537,
79 -65536,
80 -257,
81 -256,
82 -25,
83 -24,
84 -1,
85 0,
86 1,
87 23,
88 24,
89 255,
90 256,
91 65535,
92 65536,
93 4294967295,
94 4294967296,
95 std::numeric_limits<int64_t>::max()};
96
TEST(CborTest,EncodedSizeOfInt)97 TEST(CborTest, EncodedSizeOfInt) {
98 for (int64_t val : int64_cases) {
99 size_t expected = val < 0 ? cppbor::headerSize(-1ll - val)
100 : cppbor::headerSize(val);
101 EXPECT_EQ(expected, cbor::encodedSizeOfInt(val));
102 }
103 }
104
TEST(CborTest,EncodeBstrHeader)105 TEST(CborTest, EncodeBstrHeader) {
106 uint8_t cborBuf[16], cppborBuf[16];
107 std::vector<uint64_t> cases = {
108 0, 1, 23, 24, 255, 256, 65535, 65536, 4294967295, 4294967296,
109 };
110 for (uint64_t payloadSize : cases) {
111 uint8_t* cborHeaderEnd =
112 cbor::encodeBstrHeader(payloadSize, sizeof(cborBuf), cborBuf);
113 uint8_t* cppborHeaderEnd =
114 cppbor::encodeHeader(cppbor::BSTR, payloadSize, cppborBuf,
115 cppborBuf + sizeof(cppborBuf));
116
117 ASSERT_NE(cborHeaderEnd, nullptr);
118 const ptrdiff_t cborOutputLen = cborHeaderEnd - cborBuf;
119 ASSERT_LT(cborOutputLen, (ptrdiff_t)sizeof(cborBuf));
120
121 ASSERT_NE(cppborHeaderEnd, (uint8_t*)NULL);
122 const ptrdiff_t cppborOutputLen = cppborHeaderEnd - cppborBuf;
123 ASSERT_LT(cppborOutputLen, (ptrdiff_t)sizeof(cppborBuf));
124
125 std::span<const uint8_t> cborOutput(cborBuf, cborOutputLen);
126 std::span<const uint8_t> cppborOutput(cppborBuf, cppborOutputLen);
127 ASSERT_THAT(cborOutput, testing::ElementsAreArray(cppborOutput));
128 }
129
130 // Pass in a string that's longer than we could possibly handle
131 auto res = cbor::encodeBstrHeader(std::numeric_limits<uint64_t>::max(),
132 sizeof(cborBuf), cborBuf);
133 EXPECT_EQ(res, nullptr);
134 }
135
TEST(CborTest,MergeMapsEmpty)136 TEST(CborTest, MergeMapsEmpty) {
137 cppbor::Map empty;
138 std::vector<uint8_t> emptyEncoded = empty.encode();
139 auto res = cbor::mergeMaps(emptyEncoded, emptyEncoded);
140 ASSERT_TRUE(res.has_value());
141
142 // parse the bytes we got to ensure it is an empty map
143 auto [item, _, err] = cppbor::parse(res.value());
144 ASSERT_NE(item, nullptr);
145 auto resMap = item->asMap();
146 ASSERT_NE(resMap, nullptr);
147 EXPECT_EQ(resMap->size(), 0lu);
148 }
149
TEST(CborTest,MergeMapsCanonical)150 TEST(CborTest, MergeMapsCanonical) {
151 cppbor::Map first, second, expected;
152
153 for (auto k = int64_cases.rbegin(); k != int64_cases.rend(); k++) {
154 int64_t key = *k;
155 if (key & 1) {
156 first.add(key, key);
157 } else {
158 second.add(key, key);
159 }
160 expected.add(key, key);
161 }
162
163 expected.canonicalize();
164
165 auto firstEncoded = first.encode();
166 auto secondEncoded = second.encode();
167 auto merged = cbor::mergeMaps(firstEncoded, secondEncoded);
168 ASSERT_TRUE(merged.has_value());
169
170 EXPECT_TRUE(expected.isCanonical());
171 auto expectedEncoded = expected.encode();
172 ASSERT_TRUE(merged.value() == expectedEncoded);
173 }
174
TEST(CborTest,MergeMapsCanonicalNoncanonicalInput)175 TEST(CborTest, MergeMapsCanonicalNoncanonicalInput) {
176 cppbor::Map first, second, expected;
177 /*
178 * -1 comes before 1000 in CBOR order so this map will be non-canonical
179 * because cppbor stores map items in the order they were added and we do
180 * not make it canonical before encoding.
181 */
182 first.add(1000, 1000);
183 first.add(-1, -1);
184 /* check that cppbor::Map preserves insertion order */
185 ASSERT_EQ(first[0].first->asInt()->value(), 1000);
186 ASSERT_EQ(first[1].first->asInt()->value(), -1);
187
188 const std::vector<uint8_t> firstEncoded = first.encode();
189
190 second.add(1, 1);
191 second.add(42, 42);
192 const std::vector<uint8_t> secondEncoded = second.encode();
193 auto merged = cbor::mergeMaps(firstEncoded, secondEncoded);
194 ASSERT_TRUE(merged.has_value());
195
196 for (int64_t key : {-1, 1, 42, 1000}) {
197 expected.add(key, key);
198 }
199 expected.canonicalize();
200
201 EXPECT_TRUE(expected.isCanonical());
202 auto expectedEncoded = expected.encode();
203 ASSERT_TRUE(merged.value() == expectedEncoded);
204 }
205
TEST(CborTest,MapEncodingIsCanonical)206 TEST(CborTest, MapEncodingIsCanonical) {
207 cbor::VectorCborEncoder canonical;
208 canonical.encodeMap([&](auto& enc) {
209 for (int64_t key : {-1, 1000}) {
210 enc.encodeKeyValue(key, key);
211 }
212 });
213 EXPECT_EQ(canonical.state(), cbor::VectorCborEncoder::State::kEncoding);
214
215 cbor::VectorCborEncoder innerCanonical;
216 innerCanonical.encodeMap([&](auto& enc) {
217 for (int64_t key : {-1, 42}) {
218 enc.encodeKeyValue(key, [&](auto& kvenc) {
219 kvenc.encodeMap([&](auto& innerenc) {
220 for (int64_t key : {1, 1000}) {
221 innerenc.encodeKeyValue(key, key);
222 }
223 });
224 });
225 }
226 });
227 EXPECT_EQ(innerCanonical.state(),
228 cbor::VectorCborEncoder::State::kEncoding);
229
230 cbor::VectorCborEncoder nonCanonical;
231 nonCanonical.encodeMap([&](auto& enc) {
232 /* -1 comes before 1000 in CBOR order so map will be non-canonical */
233 for (int64_t key : {1000, -1}) {
234 enc.encodeKeyValue(key, key);
235 }
236 });
237
238 EXPECT_EQ(nonCanonical.state(), cbor::VectorCborEncoder::State::kInvalid);
239
240 cbor::VectorCborEncoder innerNonCanonical;
241 innerNonCanonical.encodeMap([&](auto& enc) {
242 for (int64_t key : {-1, 1000}) {
243 enc.encodeKeyValue(key, [&](auto& kvenc) {
244 kvenc.encodeMap([&](auto& innerenc) {
245 /* write inner map keys in non-canonical order */
246 for (int64_t key : {1000, -1}) {
247 innerenc.encodeKeyValue(key, key);
248 }
249 });
250 });
251 }
252 });
253
254 EXPECT_EQ(innerNonCanonical.state(),
255 cbor::VectorCborEncoder::State::kInvalid);
256 }
257
TEST(CborTest,EncodeKeyValue)258 TEST(CborTest, EncodeKeyValue) {
259 cbor::VectorCborEncoder enc;
260 enc.encodeArray([&](auto& enc) {
261 /*
262 * calling encodeKeyValue outside of an encodeMap operation is an error
263 */
264 enc.encodeKeyValue(1, 1);
265 });
266 EXPECT_EQ(enc.state(), cbor::VectorCborEncoder::State::kInvalid);
267 }
268
TEST(CborTest,MustCallEncodeArrayTagOrMap)269 TEST(CborTest, MustCallEncodeArrayTagOrMap) {
270 const char* err =
271 "Call encodeArray, encodeTag, or encodeMap before this method";
272
273 EXPECT_DEBUG_DEATH(
274 {
275 cbor::VectorCborEncoder bstrEnc;
276 const std::span<const uint8_t> empty;
277 bstrEnc.encodeBstr(empty);
278 },
279 err);
280
281 EXPECT_DEBUG_DEATH(
282 {
283 cbor::VectorCborEncoder emptyBstrEnc;
284 emptyBstrEnc.encodeEmptyBstr();
285 },
286 err);
287
288 EXPECT_DEBUG_DEATH(
289 {
290 const char* testStr = "Carsten Bormann";
291 cbor::VectorCborEncoder tstrEnc;
292 tstrEnc.encodeBstr(testStr);
293 },
294 err);
295
296 EXPECT_DEBUG_DEATH(
297 {
298 cbor::VectorCborEncoder intEnc;
299 intEnc.encodeInt(42);
300 },
301 err);
302
303 EXPECT_DEBUG_DEATH(
304 {
305 cbor::VectorCborEncoder uintEnc;
306 uintEnc.encodeUint(42u);
307 },
308 err);
309 }
310
TEST(CborTest,EncodeArrayOfTstr)311 TEST(CborTest, EncodeArrayOfTstr) {
312 const char* testStr = "Carsten Bormann";
313 const size_t testStrlen = strlen(testStr);
314
315 cbor::VectorCborEncoder enc;
316 enc.encodeArray([&](auto& enc) { enc.encodeTstr(testStr); });
317
318 auto res = enc.view();
319 const char* decodedStr;
320 size_t arrLen, decodedStrLen;
321 struct CborIn in;
322 enum CborReadResult rr;
323 CborInInit(res.data(), res.size(), &in);
324
325 rr = CborReadArray(&in, &arrLen);
326 ASSERT_EQ(rr, CBOR_READ_RESULT_OK);
327 ASSERT_EQ(arrLen, 1u);
328
329 CborReadTstr(&in, &decodedStrLen, &decodedStr);
330 ASSERT_EQ(rr, CBOR_READ_RESULT_OK);
331 ASSERT_EQ(CborInAtEnd(&in), true);
332
333 ASSERT_EQ(testStrlen, decodedStrLen);
334 /* Use memcmp instead of strcmp since decodedStr isn't null terminated */
335 ASSERT_EQ(0, memcmp(testStr, decodedStr, decodedStrLen));
336 }
337
TEST(CborTest,ViewsAndVectors)338 TEST(CborTest, ViewsAndVectors) {
339 cbor::VectorCborEncoder initialEnc;
340 auto initialView = initialEnc.view();
341 EXPECT_EQ(initialView.size(), 0u);
342 auto initialVec = initialEnc.intoVec();
343 EXPECT_EQ(initialVec.size(), 0u);
344 EXPECT_DEATH({ initialEnc.intoVec(); }, "buffer was moved out of encoder");
345 EXPECT_DEATH({ initialEnc.view(); },
346 "requested view of buffer from encoder in invalid state");
347
348 cbor::VectorCborEncoder enc;
349 enc.encodeArray([&](auto& enc) { enc.encodeEmptyBstr(); });
350 auto view = enc.view();
351 EXPECT_EQ(view.size(), 2u);
352 auto vec = enc.intoVec();
353 EXPECT_EQ(vec.size(), 2u);
354 EXPECT_DEATH({ enc.intoVec(); }, "buffer was moved out of encoder");
355 EXPECT_DEATH({ enc.view(); },
356 "requested view of buffer from encoder in invalid state");
357 }
358
359 #if __clang_major__ < 15
TEST(CborTest,EncodeArrayOfFakeBstrOverflows)360 TEST(CborTest, EncodeArrayOfFakeBstrOverflows) {
361 #else
362 // Newer clang compilers, e.g. clang-r458507 15.0.1, have ASAN that
363 // can detect stack overview in enc.encodeBstr(fake).
364 // So this test will fail at that point with newer compilers.
365 TEST(CborTest, EncodeArrayOfFakeBstrOverflows) __attribute__((no_sanitize("address"))) {
366 #endif
367 cbor::VectorCborEncoder enc;
368 const std::span<const uint8_t> fake(static_cast<const uint8_t*>(nullptr),
369 std::numeric_limits<size_t>::max());
370 enc.encodeArray([&](auto& enc) { enc.encodeBstr(fake); });
371 EXPECT_EQ(enc.state(), cbor::VectorCborEncoder::State::kOverflowed);
372 EXPECT_DEATH({ enc.size(); }, "requested encoding size after overflow");
373 EXPECT_DEATH({ enc.intoVec(); },
374 "buffer was too small to hold cbor encoded content");
375 EXPECT_DEATH({ enc.view(); },
376 "requested view of buffer from encoder in invalid state");
377 }
378
379 TEST(CborTest, CopyBytes) {
380 const uint64_t ans = 42;
381 cbor::VectorCborEncoder innerEncoder, outerEncoder;
382 innerEncoder.encodeArray([&](auto& enc) { enc.encodeUint(ans); });
383 const auto view = innerEncoder.view();
384
385 outerEncoder.encodeArray([&](auto& enc) { enc.copyBytes(view); });
386
387 const auto vec = outerEncoder.intoVec();
388 auto [item, _, err] = cppbor::parse(vec);
389 ASSERT_NE(item, nullptr);
390 const auto outerArray = item->asArray();
391 ASSERT_NE(outerArray, nullptr);
392 ASSERT_EQ(outerArray->size(), 1u);
393 const auto innerArray = outerArray->get(0)->asArray();
394 ASSERT_NE(innerArray, nullptr);
395 ASSERT_EQ(innerArray->size(), 1u);
396 const auto innerInt = innerArray->get(0)->asUint();
397 ASSERT_NE(innerInt, nullptr);
398 ASSERT_EQ(innerInt->unsignedValue(), ans);
399 }
400