1 /*
2 * Copyright 2019 Google LLC
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 * https://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 "cppbor.h"
18
19 #include <inttypes.h>
20 #include <openssl/sha.h>
21 #include <cstdint>
22 #include <cstdio>
23
24 #include "cppbor_parse.h"
25
26 using std::string;
27 using std::vector;
28
29 #ifndef __TRUSTY__
30 #include <android-base/logging.h>
31 #define LOG_TAG "CppBor"
32 #else
33 #define CHECK(x) (void)(x)
34 #endif
35
36 namespace cppbor {
37
38 namespace {
39
40 template <typename T, typename Iterator, typename = std::enable_if<std::is_unsigned<T>::value>>
41 Iterator writeBigEndian(T value, Iterator pos) {
42 for (unsigned i = 0; i < sizeof(value); ++i) {
43 *pos++ = static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1)));
44 value = static_cast<T>(value << 8);
45 }
46 return pos;
47 }
48
49 template <typename T, typename = std::enable_if<std::is_unsigned<T>::value>>
writeBigEndian(T value,std::function<void (uint8_t)> & cb)50 void writeBigEndian(T value, std::function<void(uint8_t)>& cb) {
51 for (unsigned i = 0; i < sizeof(value); ++i) {
52 cb(static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1))));
53 value = static_cast<T>(value << 8);
54 }
55 }
56
cborAreAllElementsNonCompound(const Item * compoundItem)57 bool cborAreAllElementsNonCompound(const Item* compoundItem) {
58 if (compoundItem->type() == ARRAY) {
59 const Array* array = compoundItem->asArray();
60 for (size_t n = 0; n < array->size(); n++) {
61 const Item* entry = (*array)[n].get();
62 switch (entry->type()) {
63 case ARRAY:
64 case MAP:
65 return false;
66 default:
67 break;
68 }
69 }
70 } else {
71 const Map* map = compoundItem->asMap();
72 for (auto& [keyEntry, valueEntry] : *map) {
73 switch (keyEntry->type()) {
74 case ARRAY:
75 case MAP:
76 return false;
77 default:
78 break;
79 }
80 switch (valueEntry->type()) {
81 case ARRAY:
82 case MAP:
83 return false;
84 default:
85 break;
86 }
87 }
88 }
89 return true;
90 }
91
prettyPrintInternal(const Item * item,string & out,size_t indent,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)92 bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t maxBStrSize,
93 const vector<string>& mapKeysToNotPrint) {
94 if (!item) {
95 out.append("<NULL>");
96 return false;
97 }
98
99 char buf[80];
100
101 string indentString(indent, ' ');
102
103 size_t tagCount = item->semanticTagCount();
104 while (tagCount > 0) {
105 --tagCount;
106 snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount));
107 out.append(buf);
108 }
109
110 switch (item->type()) {
111 case SEMANTIC:
112 // Handled above.
113 break;
114
115 case UINT:
116 snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue());
117 out.append(buf);
118 break;
119
120 case NINT:
121 snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value());
122 out.append(buf);
123 break;
124
125 case BSTR: {
126 const uint8_t* valueData;
127 size_t valueSize;
128 const Bstr* bstr = item->asBstr();
129 if (bstr != nullptr) {
130 const vector<uint8_t>& value = bstr->value();
131 valueData = value.data();
132 valueSize = value.size();
133 } else {
134 const ViewBstr* viewBstr = item->asViewBstr();
135 assert(viewBstr != nullptr);
136
137 valueData = viewBstr->view().data();
138 valueSize = viewBstr->view().size();
139 }
140
141 if (valueSize > maxBStrSize) {
142 unsigned char digest[SHA_DIGEST_LENGTH];
143 SHA_CTX ctx;
144 SHA1_Init(&ctx);
145 SHA1_Update(&ctx, valueData, valueSize);
146 SHA1_Final(digest, &ctx);
147 char buf2[SHA_DIGEST_LENGTH * 2 + 1];
148 for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) {
149 snprintf(buf2 + n * 2, 3, "%02x", digest[n]);
150 }
151 snprintf(buf, sizeof(buf), "<bstr size=%zd sha1=%s>", valueSize, buf2);
152 out.append(buf);
153 } else {
154 out.append("{");
155 for (size_t n = 0; n < valueSize; n++) {
156 if (n > 0) {
157 out.append(", ");
158 }
159 snprintf(buf, sizeof(buf), "0x%02x", valueData[n]);
160 out.append(buf);
161 }
162 out.append("}");
163 }
164 } break;
165
166 case TSTR:
167 out.append("'");
168 {
169 // TODO: escape "'" characters
170 if (item->asTstr() != nullptr) {
171 out.append(item->asTstr()->value().c_str());
172 } else {
173 const ViewTstr* viewTstr = item->asViewTstr();
174 assert(viewTstr != nullptr);
175 out.append(viewTstr->view());
176 }
177 }
178 out.append("'");
179 break;
180
181 case ARRAY: {
182 const Array* array = item->asArray();
183 if (array->size() == 0) {
184 out.append("[]");
185 } else if (cborAreAllElementsNonCompound(array)) {
186 out.append("[");
187 for (size_t n = 0; n < array->size(); n++) {
188 if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
189 mapKeysToNotPrint)) {
190 return false;
191 }
192 out.append(", ");
193 }
194 out.append("]");
195 } else {
196 out.append("[\n" + indentString);
197 for (size_t n = 0; n < array->size(); n++) {
198 out.append(" ");
199 if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
200 mapKeysToNotPrint)) {
201 return false;
202 }
203 out.append(",\n" + indentString);
204 }
205 out.append("]");
206 }
207 } break;
208
209 case MAP: {
210 const Map* map = item->asMap();
211
212 if (map->size() == 0) {
213 out.append("{}");
214 } else {
215 out.append("{\n" + indentString);
216 for (auto& [map_key, map_value] : *map) {
217 out.append(" ");
218
219 if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize,
220 mapKeysToNotPrint)) {
221 return false;
222 }
223 out.append(" : ");
224 if (map_key->type() == TSTR &&
225 std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(),
226 map_key->asTstr()->value()) != mapKeysToNotPrint.end()) {
227 out.append("<not printed>");
228 } else {
229 if (!prettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize,
230 mapKeysToNotPrint)) {
231 return false;
232 }
233 }
234 out.append(",\n" + indentString);
235 }
236 out.append("}");
237 }
238 } break;
239
240 case SIMPLE:
241 switch (item->asSimple()->simpleType()) {
242 case BOOLEAN:
243 out.append(item->asSimple()->asBool()->value() ? "true" : "false");
244 break;
245 case NULL_T:
246 out.append("null");
247 break;
248 case FLOAT:
249 #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
250 snprintf(buf, sizeof(buf), "%f", item->asSimple()->asFloat()->value());
251 out.append(buf);
252 break;
253 #else
254 #ifndef __TRUSTY__
255 LOG(ERROR) << "float not supported for this platform.";
256 #endif // __TRUSTY__
257 return false;
258 #endif // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
259 case DOUBLE:
260 #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
261 snprintf(buf, sizeof(buf), "%f", item->asSimple()->asDouble()->value());
262 out.append(buf);
263 break;
264 #else
265 #ifndef __TRUSTY__
266 LOG(ERROR) << "double not supported for this platform.";
267 #endif // __TRUSTY__
268 return false;
269 #endif // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
270 default:
271 #ifndef __TRUSTY__
272 LOG(ERROR) << "Only boolean/null/float/double is implemented for SIMPLE";
273 #endif // __TRUSTY__
274 return false;
275 }
276 break;
277 }
278
279 return true;
280 }
281
282 } // namespace
283
headerSize(uint64_t addlInfo)284 size_t headerSize(uint64_t addlInfo) {
285 if (addlInfo < ONE_BYTE_LENGTH) return 1;
286 if (addlInfo <= std::numeric_limits<uint8_t>::max()) return 2;
287 if (addlInfo <= std::numeric_limits<uint16_t>::max()) return 3;
288 if (addlInfo <= std::numeric_limits<uint32_t>::max()) return 5;
289 return 9;
290 }
291
encodeHeader(MajorType type,uint64_t addlInfo,uint8_t * pos,const uint8_t * end)292 uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) {
293 size_t sz = headerSize(addlInfo);
294 if (end - pos < static_cast<ssize_t>(sz)) return nullptr;
295 switch (sz) {
296 case 1:
297 *pos++ = type | static_cast<uint8_t>(addlInfo);
298 return pos;
299 case 2:
300 *pos++ = type | static_cast<MajorType>(ONE_BYTE_LENGTH);
301 *pos++ = static_cast<uint8_t>(addlInfo);
302 return pos;
303 case 3:
304 *pos++ = type | static_cast<MajorType>(TWO_BYTE_LENGTH);
305 return writeBigEndian(static_cast<uint16_t>(addlInfo), pos);
306 case 5:
307 *pos++ = type | static_cast<MajorType>(FOUR_BYTE_LENGTH);
308 return writeBigEndian(static_cast<uint32_t>(addlInfo), pos);
309 case 9:
310 *pos++ = type | static_cast<MajorType>(EIGHT_BYTE_LENGTH);
311 return writeBigEndian(addlInfo, pos);
312 default:
313 CHECK(false); // Impossible to get here.
314 return nullptr;
315 }
316 }
317
encodeHeader(MajorType type,uint64_t addlInfo,EncodeCallback encodeCallback)318 void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) {
319 size_t sz = headerSize(addlInfo);
320 switch (sz) {
321 case 1:
322 encodeCallback(type | static_cast<uint8_t>(addlInfo));
323 break;
324 case 2:
325 encodeCallback(type | static_cast<MajorType>(ONE_BYTE_LENGTH));
326 encodeCallback(static_cast<uint8_t>(addlInfo));
327 break;
328 case 3:
329 encodeCallback(type | static_cast<MajorType>(TWO_BYTE_LENGTH));
330 writeBigEndian(static_cast<uint16_t>(addlInfo), encodeCallback);
331 break;
332 case 5:
333 encodeCallback(type | static_cast<MajorType>(FOUR_BYTE_LENGTH));
334 writeBigEndian(static_cast<uint32_t>(addlInfo), encodeCallback);
335 break;
336 case 9:
337 encodeCallback(type | static_cast<MajorType>(EIGHT_BYTE_LENGTH));
338 writeBigEndian(addlInfo, encodeCallback);
339 break;
340 default:
341 CHECK(false); // Impossible to get here.
342 }
343 }
344
operator ==(const Item & other) const345 bool Item::operator==(const Item& other) const& {
346 if (type() != other.type()) return false;
347 switch (type()) {
348 case UINT:
349 return *asUint() == *(other.asUint());
350 case NINT:
351 return *asNint() == *(other.asNint());
352 case BSTR:
353 if (asBstr() != nullptr && other.asBstr() != nullptr) {
354 return *asBstr() == *(other.asBstr());
355 }
356 if (asViewBstr() != nullptr && other.asViewBstr() != nullptr) {
357 return *asViewBstr() == *(other.asViewBstr());
358 }
359 // Interesting corner case: comparing a Bstr and ViewBstr with
360 // identical contents. The function currently returns false for
361 // this case.
362 // TODO: if it should return true, this needs a deep comparison
363 return false;
364 case TSTR:
365 if (asTstr() != nullptr && other.asTstr() != nullptr) {
366 return *asTstr() == *(other.asTstr());
367 }
368 if (asViewTstr() != nullptr && other.asViewTstr() != nullptr) {
369 return *asViewTstr() == *(other.asViewTstr());
370 }
371 // Same corner case as Bstr
372 return false;
373 case ARRAY:
374 return *asArray() == *(other.asArray());
375 case MAP:
376 return *asMap() == *(other.asMap());
377 case SIMPLE:
378 return *asSimple() == *(other.asSimple());
379 case SEMANTIC:
380 return *asSemanticTag() == *(other.asSemanticTag());
381 default:
382 CHECK(false); // Impossible to get here.
383 return false;
384 }
385 }
386
Nint(int64_t v)387 Nint::Nint(int64_t v) : mValue(v) {
388 CHECK(v < 0);
389 }
390
operator ==(const Simple & other) const391 bool Simple::operator==(const Simple& other) const& {
392 if (simpleType() != other.simpleType()) return false;
393
394 switch (simpleType()) {
395 case BOOLEAN:
396 return *asBool() == *(other.asBool());
397 case NULL_T:
398 return true;
399 #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
400 case FLOAT:
401 return *asFloat() == *(other.asFloat());
402 #endif // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
403 #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
404 case DOUBLE:
405 return *asDouble() == *(other.asDouble());
406 #endif // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
407 default:
408 CHECK(false); // Impossible to get here.
409 return false;
410 }
411 }
412
encode(uint8_t * pos,const uint8_t * end) const413 uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const {
414 pos = encodeHeader(mValue.size(), pos, end);
415 if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
416 return std::copy(mValue.begin(), mValue.end(), pos);
417 }
418
encodeValue(EncodeCallback encodeCallback) const419 void Bstr::encodeValue(EncodeCallback encodeCallback) const {
420 for (auto c : mValue) {
421 encodeCallback(c);
422 }
423 }
424
encode(uint8_t * pos,const uint8_t * end) const425 uint8_t* ViewBstr::encode(uint8_t* pos, const uint8_t* end) const {
426 pos = encodeHeader(mView.size(), pos, end);
427 if (!pos || end - pos < static_cast<ptrdiff_t>(mView.size())) return nullptr;
428 return std::copy(mView.begin(), mView.end(), pos);
429 }
430
encodeValue(EncodeCallback encodeCallback) const431 void ViewBstr::encodeValue(EncodeCallback encodeCallback) const {
432 for (auto c : mView) {
433 encodeCallback(static_cast<uint8_t>(c));
434 }
435 }
436
encode(uint8_t * pos,const uint8_t * end) const437 uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const {
438 pos = encodeHeader(mValue.size(), pos, end);
439 if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
440 return std::copy(mValue.begin(), mValue.end(), pos);
441 }
442
encodeValue(EncodeCallback encodeCallback) const443 void Tstr::encodeValue(EncodeCallback encodeCallback) const {
444 for (auto c : mValue) {
445 encodeCallback(static_cast<uint8_t>(c));
446 }
447 }
448
encode(uint8_t * pos,const uint8_t * end) const449 uint8_t* ViewTstr::encode(uint8_t* pos, const uint8_t* end) const {
450 pos = encodeHeader(mView.size(), pos, end);
451 if (!pos || end - pos < static_cast<ptrdiff_t>(mView.size())) return nullptr;
452 return std::copy(mView.begin(), mView.end(), pos);
453 }
454
encodeValue(EncodeCallback encodeCallback) const455 void ViewTstr::encodeValue(EncodeCallback encodeCallback) const {
456 for (auto c : mView) {
457 encodeCallback(static_cast<uint8_t>(c));
458 }
459 }
460
operator ==(const Array & other) const461 bool Array::operator==(const Array& other) const& {
462 return size() == other.size()
463 // Can't use vector::operator== because the contents are pointers. std::equal lets us
464 // provide a predicate that does the dereferencing.
465 && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
466 [](auto& a, auto& b) -> bool { return *a == *b; });
467 }
468
encode(uint8_t * pos,const uint8_t * end) const469 uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const {
470 pos = encodeHeader(size(), pos, end);
471 if (!pos) return nullptr;
472 for (auto& entry : mEntries) {
473 pos = entry->encode(pos, end);
474 if (!pos) return nullptr;
475 }
476 return pos;
477 }
478
encode(EncodeCallback encodeCallback) const479 void Array::encode(EncodeCallback encodeCallback) const {
480 encodeHeader(size(), encodeCallback);
481 for (auto& entry : mEntries) {
482 entry->encode(encodeCallback);
483 }
484 }
485
clone() const486 std::unique_ptr<Item> Array::clone() const {
487 auto res = std::make_unique<Array>();
488 for (size_t i = 0; i < mEntries.size(); i++) {
489 res->add(mEntries[i]->clone());
490 }
491 return res;
492 }
493
operator ==(const Map & other) const494 bool Map::operator==(const Map& other) const& {
495 return size() == other.size()
496 // Can't use vector::operator== because the contents are pairs of pointers. std::equal
497 // lets us provide a predicate that does the dereferencing.
498 && std::equal(begin(), end(), other.begin(), [](auto& a, auto& b) {
499 return *a.first == *b.first && *a.second == *b.second;
500 });
501 }
502
encode(uint8_t * pos,const uint8_t * end) const503 uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const {
504 pos = encodeHeader(size(), pos, end);
505 if (!pos) return nullptr;
506 for (auto& entry : mEntries) {
507 pos = entry.first->encode(pos, end);
508 if (!pos) return nullptr;
509 pos = entry.second->encode(pos, end);
510 if (!pos) return nullptr;
511 }
512 return pos;
513 }
514
encode(EncodeCallback encodeCallback) const515 void Map::encode(EncodeCallback encodeCallback) const {
516 encodeHeader(size(), encodeCallback);
517 for (auto& entry : mEntries) {
518 entry.first->encode(encodeCallback);
519 entry.second->encode(encodeCallback);
520 }
521 }
522
keyLess(const Item * a,const Item * b)523 bool Map::keyLess(const Item* a, const Item* b) {
524 // CBOR map canonicalization rules are:
525
526 // 1. If two keys have different lengths, the shorter one sorts earlier.
527 if (a->encodedSize() < b->encodedSize()) return true;
528 if (a->encodedSize() > b->encodedSize()) return false;
529
530 // 2. If two keys have the same length, the one with the lower value in (byte-wise) lexical
531 // order sorts earlier. This requires encoding both items.
532 auto encodedA = a->encode();
533 auto encodedB = b->encode();
534
535 return std::lexicographical_compare(encodedA.begin(), encodedA.end(), //
536 encodedB.begin(), encodedB.end());
537 }
538
recursivelyCanonicalize(std::unique_ptr<Item> & item)539 void recursivelyCanonicalize(std::unique_ptr<Item>& item) {
540 switch (item->type()) {
541 case UINT:
542 case NINT:
543 case BSTR:
544 case TSTR:
545 case SIMPLE:
546 return;
547
548 case ARRAY:
549 std::for_each(item->asArray()->begin(), item->asArray()->end(),
550 recursivelyCanonicalize);
551 return;
552
553 case MAP:
554 item->asMap()->canonicalize(true /* recurse */);
555 return;
556
557 case SEMANTIC:
558 // This can't happen. SemanticTags delegate their type() method to the contained Item's
559 // type.
560 assert(false);
561 return;
562 }
563 }
564
canonicalize(bool recurse)565 Map& Map::canonicalize(bool recurse) & {
566 if (recurse) {
567 for (auto& entry : mEntries) {
568 recursivelyCanonicalize(entry.first);
569 recursivelyCanonicalize(entry.second);
570 }
571 }
572
573 if (size() < 2 || mCanonicalized) {
574 // Trivially or already canonical; do nothing.
575 return *this;
576 }
577
578 std::sort(begin(), end(),
579 [](auto& a, auto& b) { return keyLess(a.first.get(), b.first.get()); });
580 mCanonicalized = true;
581 return *this;
582 }
583
clone() const584 std::unique_ptr<Item> Map::clone() const {
585 auto res = std::make_unique<Map>();
586 for (auto& [key, value] : *this) {
587 res->add(key->clone(), value->clone());
588 }
589 res->mCanonicalized = mCanonicalized;
590 return res;
591 }
592
clone() const593 std::unique_ptr<Item> SemanticTag::clone() const {
594 return std::make_unique<SemanticTag>(mValue, mTaggedItem->clone());
595 }
596
encode(uint8_t * pos,const uint8_t * end) const597 uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const {
598 // Can't use the encodeHeader() method that calls type() to get the major type, since that will
599 // return the tagged Item's type.
600 pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end);
601 if (!pos) return nullptr;
602 return mTaggedItem->encode(pos, end);
603 }
604
encode(EncodeCallback encodeCallback) const605 void SemanticTag::encode(EncodeCallback encodeCallback) const {
606 // Can't use the encodeHeader() method that calls type() to get the major type, since that will
607 // return the tagged Item's type.
608 ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback);
609 mTaggedItem->encode(encodeCallback);
610 }
611
semanticTagCount() const612 size_t SemanticTag::semanticTagCount() const {
613 size_t levelCount = 1; // Count this level.
614 const SemanticTag* cur = this;
615 while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount;
616 return levelCount;
617 }
618
semanticTag(size_t nesting) const619 uint64_t SemanticTag::semanticTag(size_t nesting) const {
620 // Getting the value of a specific nested tag is a bit tricky, because we start with the outer
621 // tag and don't know how many are inside. We count the number of nesting levels to find out
622 // how many there are in total, then to get the one we want we have to walk down levelCount -
623 // nesting steps.
624 size_t levelCount = semanticTagCount();
625 if (nesting >= levelCount) return 0;
626
627 levelCount -= nesting;
628 const SemanticTag* cur = this;
629 while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag();
630
631 return cur->mValue;
632 }
633
prettyPrint(const Item * item,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)634 string prettyPrint(const Item* item, size_t maxBStrSize, const vector<string>& mapKeysToNotPrint) {
635 string out;
636 prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint);
637 return out;
638 }
prettyPrint(const vector<uint8_t> & encodedCbor,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)639 string prettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize,
640 const vector<string>& mapKeysToNotPrint) {
641 auto [item, _, message] = parse(encodedCbor);
642 if (item == nullptr) {
643 #ifndef __TRUSTY__
644 LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message;
645 #endif // __TRUSTY__
646 return "";
647 }
648
649 return prettyPrint(item.get(), maxBStrSize, mapKeysToNotPrint);
650 }
651
652 } // namespace cppbor
653