diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-31 16:33:43 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-06 16:33:22 +0000 |
commit | da51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch) | |
tree | 4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/components/cbor | |
parent | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff) | |
download | qtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz |
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules
Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/cbor')
96 files changed, 2955 insertions, 0 deletions
diff --git a/chromium/components/cbor/BUILD.gn b/chromium/components/cbor/BUILD.gn new file mode 100644 index 00000000000..ae9ec5b2260 --- /dev/null +++ b/chromium/components/cbor/BUILD.gn @@ -0,0 +1,51 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//testing/libfuzzer/fuzzer_test.gni") + +component("cbor") { + sources = [ + "cbor_binary.h", + "cbor_reader.cc", + "cbor_reader.h", + "cbor_values.cc", + "cbor_values.h", + "cbor_writer.cc", + "cbor_writer.h", + ] + + defines = [ "CBOR_IMPLEMENTATION" ] + + deps = [ + "//base", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "cbor_reader_unittest.cc", + "cbor_values_unittest.cc", + "cbor_writer_unittest.cc", + ] + + deps = [ + ":cbor", + "//base", + "//testing/gmock", + "//testing/gtest", + ] +} + +fuzzer_test("cbor_reader_fuzzer") { + sources = [ + "cbor_reader_fuzzer.cc", + ] + deps = [ + ":cbor", + "//base", + ] + seed_corpus = "cbor_reader_fuzzer_corpus/" + libfuzzer_options = [ "max_len=65535" ] +} diff --git a/chromium/components/cbor/OWNERS b/chromium/components/cbor/OWNERS new file mode 100644 index 00000000000..85cda96ded9 --- /dev/null +++ b/chromium/components/cbor/OWNERS @@ -0,0 +1,2 @@ +jochen@chromium.org +engedy@chromium.org diff --git a/chromium/components/cbor/cbor_binary.h b/chromium/components/cbor/cbor_binary.h new file mode 100644 index 00000000000..f1790658a92 --- /dev/null +++ b/chromium/components/cbor/cbor_binary.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#ifndef COMPONENTS_CBOR_CBOR_BINARY_H_ +#define COMPONENTS_CBOR_CBOR_BINARY_H_ + +namespace cbor { +namespace constants { + +// Mask selecting the low-order 5 bits of the "initial byte", which is where +// the additional information is encoded. +static constexpr uint8_t kAdditionalInformationMask = 0x1F; +// Mask selecting the high-order 3 bits of the "initial byte", which indicates +// the major type of the encoded value. +static constexpr uint8_t kMajorTypeMask = 0xE0; +// Indicates the number of bits the "initial byte" needs to be shifted to the +// right after applying |kMajorTypeMask| to produce the major type in the +// lowermost bits. +static constexpr uint8_t kMajorTypeBitShift = 5u; +// Indicates the integer is in the following byte. +static constexpr uint8_t kAdditionalInformation1Byte = 24u; +// Indicates the integer is in the next 2 bytes. +static constexpr uint8_t kAdditionalInformation2Bytes = 25u; +// Indicates the integer is in the next 4 bytes. +static constexpr uint8_t kAdditionalInformation4Bytes = 26u; +// Indicates the integer is in the next 8 bytes. +static constexpr uint8_t kAdditionalInformation8Bytes = 27u; + +} // namespace constants +} // namespace cbor + +#endif // COMPONENTS_CBOR_CBOR_BINARY_H_ diff --git a/chromium/components/cbor/cbor_export.h b/chromium/components/cbor/cbor_export.h new file mode 100644 index 00000000000..1b932e68111 --- /dev/null +++ b/chromium/components/cbor/cbor_export.h @@ -0,0 +1,29 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CBOR_CBOR_EXPORT_H_ +#define COMPONENTS_CBOR_CBOR_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(CBOR_IMPLEMENTATION) +#define CBOR_EXPORT __declspec(dllexport) +#else +#define CBOR_EXPORT __declspec(dllimport) +#endif // defined(CBOR_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(CBOR_IMPLEMENTATION) +#define CBOR_EXPORT __attribute__((visibility("default"))) +#else +#define CBOR_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define CBOR_EXPORT +#endif + +#endif // COMPONENTS_CBOR_CBOR_EXPORT_H_ diff --git a/chromium/components/cbor/cbor_reader.cc b/chromium/components/cbor/cbor_reader.cc new file mode 100644 index 00000000000..c21f4df3cb9 --- /dev/null +++ b/chromium/components/cbor/cbor_reader.cc @@ -0,0 +1,349 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cbor/cbor_reader.h" + +#include <math.h> +#include <utility> + +#include "base/numerics/checked_math.h" +#include "base/numerics/safe_conversions.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "components/cbor/cbor_binary.h" + +namespace cbor { + +namespace { + +CBORValue::Type GetMajorType(uint8_t initial_data_byte) { + return static_cast<CBORValue::Type>( + (initial_data_byte & constants::kMajorTypeMask) >> + constants::kMajorTypeBitShift); +} + +uint8_t GetAdditionalInfo(uint8_t initial_data_byte) { + return initial_data_byte & constants::kAdditionalInformationMask; +} + +// Error messages that correspond to each of the error codes. +const char kNoError[] = "Successfully deserialized to a CBOR value."; +const char kUnsupportedMajorType[] = "Unsupported major type."; +const char kUnknownAdditionalInfo[] = + "Unknown additional info format in the first byte."; +const char kIncompleteCBORData[] = + "Prematurely terminated CBOR data byte array."; +const char kIncorrectMapKeyType[] = + "Map keys other than utf-8 encoded strings are not allowed."; +const char kTooMuchNesting[] = "Too much nesting."; +const char kInvalidUTF8[] = "String encoding other than utf8 are not allowed."; +const char kExtraneousData[] = "Trailing data bytes are not allowed."; +const char kDuplicateKey[] = "Duplicate map keys are not allowed."; +const char kMapKeyOutOfOrder[] = + "Map keys must be sorted by byte length and then by byte-wise lexical " + "order."; +const char kNonMinimalCBOREncoding[] = + "Unsigned integers must be encoded with minimum number of bytes."; +const char kUnsupportedSimpleValue[] = + "Unsupported or unassigned simple value."; +const char kUnsupportedFloatingPointValue[] = + "Floating point numbers are not supported."; +const char kOutOfRangeIntegerValue[] = + "Integer values must be between INT64_MIN and INT64_MAX."; + +} // namespace + +CBORReader::CBORReader(Bytes::const_iterator it, Bytes::const_iterator end) + : it_(it), end_(end), error_code_(DecoderError::CBOR_NO_ERROR) {} +CBORReader::~CBORReader() {} + +// static +base::Optional<CBORValue> CBORReader::Read(const Bytes& data, + DecoderError* error_code_out, + int max_nesting_level) { + CBORReader reader(data.begin(), data.end()); + base::Optional<CBORValue> decoded_cbor = reader.DecodeCBOR(max_nesting_level); + + if (decoded_cbor) + reader.CheckExtraneousData(); + if (error_code_out) + *error_code_out = reader.GetErrorCode(); + + if (reader.GetErrorCode() != DecoderError::CBOR_NO_ERROR) + return base::nullopt; + return decoded_cbor; +} + +base::Optional<CBORValue> CBORReader::DecodeCBOR(int max_nesting_level) { + if (max_nesting_level < 0 || max_nesting_level > kCBORMaxDepth) { + error_code_ = DecoderError::TOO_MUCH_NESTING; + return base::nullopt; + } + + if (!CanConsume(1)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + const uint8_t initial_byte = *it_++; + const auto major_type = GetMajorType(initial_byte); + const uint8_t additional_info = GetAdditionalInfo(initial_byte); + + uint64_t value; + if (!ReadVariadicLengthInteger(additional_info, &value)) + return base::nullopt; + + switch (major_type) { + case CBORValue::Type::UNSIGNED: + return DecodeValueToUnsigned(value); + case CBORValue::Type::NEGATIVE: + return DecodeValueToNegative(value); + case CBORValue::Type::BYTE_STRING: + return ReadBytes(value); + case CBORValue::Type::STRING: + return ReadString(value); + case CBORValue::Type::ARRAY: + return ReadCBORArray(value, max_nesting_level); + case CBORValue::Type::MAP: + return ReadCBORMap(value, max_nesting_level); + case CBORValue::Type::SIMPLE_VALUE: + return ReadSimpleValue(additional_info, value); + case CBORValue::Type::NONE: + break; + } + + error_code_ = DecoderError::UNSUPPORTED_MAJOR_TYPE; + return base::nullopt; +} + +bool CBORReader::ReadVariadicLengthInteger(uint8_t additional_info, + uint64_t* value) { + uint8_t additional_bytes = 0; + if (additional_info < 24) { + *value = additional_info; + return true; + } else if (additional_info == 24) { + additional_bytes = 1; + } else if (additional_info == 25) { + additional_bytes = 2; + } else if (additional_info == 26) { + additional_bytes = 4; + } else if (additional_info == 27) { + additional_bytes = 8; + } else { + error_code_ = DecoderError::UNKNOWN_ADDITIONAL_INFO; + return false; + } + + if (!CanConsume(additional_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return false; + } + + uint64_t int_data = 0; + for (uint8_t i = 0; i < additional_bytes; ++i) { + int_data <<= 8; + int_data |= *it_++; + } + + *value = int_data; + return CheckMinimalEncoding(additional_bytes, int_data); +} + +base::Optional<CBORValue> CBORReader::DecodeValueToNegative(uint64_t value) { + auto negative_value = -base::CheckedNumeric<int64_t>(value) - 1; + if (!negative_value.IsValid()) { + error_code_ = DecoderError::OUT_OF_RANGE_INTEGER_VALUE; + return base::nullopt; + } + return CBORValue(negative_value.ValueOrDie()); +} + +base::Optional<CBORValue> CBORReader::DecodeValueToUnsigned(uint64_t value) { + auto unsigned_value = base::CheckedNumeric<int64_t>(value); + if (!unsigned_value.IsValid()) { + error_code_ = DecoderError::OUT_OF_RANGE_INTEGER_VALUE; + return base::nullopt; + } + return CBORValue(unsigned_value.ValueOrDie()); +} + +base::Optional<CBORValue> CBORReader::ReadSimpleValue(uint8_t additional_info, + uint64_t value) { + // Floating point numbers are not supported. + if (additional_info > 24 && additional_info < 28) { + error_code_ = DecoderError::UNSUPPORTED_FLOATING_POINT_VALUE; + return base::nullopt; + } + + CHECK_LE(value, 255u); + CBORValue::SimpleValue possibly_unsupported_simple_value = + static_cast<CBORValue::SimpleValue>(static_cast<int>(value)); + switch (possibly_unsupported_simple_value) { + case CBORValue::SimpleValue::FALSE_VALUE: + case CBORValue::SimpleValue::TRUE_VALUE: + case CBORValue::SimpleValue::NULL_VALUE: + case CBORValue::SimpleValue::UNDEFINED: + return CBORValue(possibly_unsupported_simple_value); + } + + error_code_ = DecoderError::UNSUPPORTED_SIMPLE_VALUE; + return base::nullopt; +} + +base::Optional<CBORValue> CBORReader::ReadString(uint64_t num_bytes) { + if (!CanConsume(num_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + std::string cbor_string(it_, it_ + num_bytes); + it_ += num_bytes; + + return HasValidUTF8Format(cbor_string) + ? base::make_optional<CBORValue>(CBORValue(std::move(cbor_string))) + : base::nullopt; +} + +base::Optional<CBORValue> CBORReader::ReadBytes(uint64_t num_bytes) { + if (!CanConsume(num_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + Bytes cbor_byte_string(it_, it_ + num_bytes); + it_ += num_bytes; + + return CBORValue(std::move(cbor_byte_string)); +} + +base::Optional<CBORValue> CBORReader::ReadCBORArray(uint64_t length, + int max_nesting_level) { + CBORValue::ArrayValue cbor_array; + while (length-- > 0) { + base::Optional<CBORValue> cbor_element = DecodeCBOR(max_nesting_level - 1); + if (!cbor_element.has_value()) + return base::nullopt; + cbor_array.push_back(std::move(cbor_element.value())); + } + return CBORValue(std::move(cbor_array)); +} + +base::Optional<CBORValue> CBORReader::ReadCBORMap(uint64_t length, + int max_nesting_level) { + CBORValue::MapValue cbor_map; + while (length-- > 0) { + base::Optional<CBORValue> key = DecodeCBOR(max_nesting_level - 1); + base::Optional<CBORValue> value = DecodeCBOR(max_nesting_level - 1); + if (!key.has_value() || !value.has_value()) + return base::nullopt; + + // Only CBOR maps with integer or string type keys are allowed. + if (key.value().type() != CBORValue::Type::STRING && + key.value().type() != CBORValue::Type::UNSIGNED) { + error_code_ = DecoderError::INCORRECT_MAP_KEY_TYPE; + return base::nullopt; + } + if (!CheckDuplicateKey(key.value(), &cbor_map) || + !CheckOutOfOrderKey(key.value(), &cbor_map)) { + return base::nullopt; + } + + cbor_map.insert_or_assign(std::move(key.value()), std::move(value.value())); + } + return CBORValue(std::move(cbor_map)); +} + +bool CBORReader::CanConsume(uint64_t bytes) { + if (base::checked_cast<uint64_t>(std::distance(it_, end_)) >= bytes) { + return true; + } + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return false; +} + +bool CBORReader::CheckMinimalEncoding(uint8_t additional_bytes, + uint64_t uint_data) { + if ((additional_bytes == 1 && uint_data < 24) || + uint_data <= (1ULL << 8 * (additional_bytes >> 1)) - 1) { + error_code_ = DecoderError::NON_MINIMAL_CBOR_ENCODING; + return false; + } + return true; +} + +void CBORReader::CheckExtraneousData() { + if (it_ != end_) + error_code_ = DecoderError::EXTRANEOUS_DATA; +} + +bool CBORReader::CheckDuplicateKey(const CBORValue& new_key, + CBORValue::MapValue* map) { + if (base::ContainsKey(*map, new_key)) { + error_code_ = DecoderError::DUPLICATE_KEY; + return false; + } + return true; +} + +bool CBORReader::HasValidUTF8Format(const std::string& string_data) { + if (!base::IsStringUTF8(string_data)) { + error_code_ = DecoderError::INVALID_UTF8; + return false; + } + return true; +} + +bool CBORReader::CheckOutOfOrderKey(const CBORValue& new_key, + CBORValue::MapValue* map) { + auto comparator = map->key_comp(); + if (!map->empty() && comparator(new_key, map->rbegin()->first)) { + error_code_ = DecoderError::OUT_OF_ORDER_KEY; + return false; + } + return true; +} + +CBORReader::DecoderError CBORReader::GetErrorCode() { + return error_code_; +} + +// static +const char* CBORReader::ErrorCodeToString(DecoderError error) { + switch (error) { + case DecoderError::CBOR_NO_ERROR: + return kNoError; + case DecoderError::UNSUPPORTED_MAJOR_TYPE: + return kUnsupportedMajorType; + case DecoderError::UNKNOWN_ADDITIONAL_INFO: + return kUnknownAdditionalInfo; + case DecoderError::INCOMPLETE_CBOR_DATA: + return kIncompleteCBORData; + case DecoderError::INCORRECT_MAP_KEY_TYPE: + return kIncorrectMapKeyType; + case DecoderError::TOO_MUCH_NESTING: + return kTooMuchNesting; + case DecoderError::INVALID_UTF8: + return kInvalidUTF8; + case DecoderError::EXTRANEOUS_DATA: + return kExtraneousData; + case DecoderError::DUPLICATE_KEY: + return kDuplicateKey; + case DecoderError::OUT_OF_ORDER_KEY: + return kMapKeyOutOfOrder; + case DecoderError::NON_MINIMAL_CBOR_ENCODING: + return kNonMinimalCBOREncoding; + case DecoderError::UNSUPPORTED_SIMPLE_VALUE: + return kUnsupportedSimpleValue; + case DecoderError::UNSUPPORTED_FLOATING_POINT_VALUE: + return kUnsupportedFloatingPointValue; + case DecoderError::OUT_OF_RANGE_INTEGER_VALUE: + return kOutOfRangeIntegerValue; + default: + NOTREACHED(); + return "Unknown error code."; + } +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_reader.h b/chromium/components/cbor/cbor_reader.h new file mode 100644 index 00000000000..35558891a45 --- /dev/null +++ b/chromium/components/cbor/cbor_reader.h @@ -0,0 +1,120 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CBOR_CBOR_READER_H_ +#define COMPONENTS_CBOR_CBOR_READER_H_ + +#include <stddef.h> +#include <string> +#include <vector> + +#include "base/optional.h" +#include "components/cbor/cbor_export.h" +#include "components/cbor/cbor_values.h" + +// Concise Binary Object Representation (CBOR) decoder as defined by +// https://tools.ietf.org/html/rfc7049. This decoder only accepts canonical +// CBOR as defined by section 3.9. +// Supported: +// * Major types: +// * 0: Unsigned integers, up to 64-bit. +// * 2: Byte strings. +// * 3: UTF-8 strings. +// * 4: Definite-length arrays. +// * 5: Definite-length maps. +// * 7: Simple values. +// +// Requirements for canonical CBOR representation: +// - Duplicate keys for map are not allowed. +// - Keys for map must be sorted first by length and then by byte-wise +// lexical order. +// +// Known limitations and interpretations of the RFC: +// - Does not support negative integers, indefinite data streams and tagging. +// - Floating point representations and BREAK stop code in major +// type 7 are not supported. +// - Non-character codepoint are not supported for Major type 3. +// - Incomplete CBOR data items are treated as syntax errors. +// - Trailing data bytes are treated as errors. +// - Unknown additional information formats are treated as syntax errors. +// - Callers can decode CBOR values with at most 16 nested depth layer. More +// strict restrictions on nesting layer size of CBOR values can be enforced +// by setting |max_nesting_level|. +// - Only CBOR maps with integer or string type keys are supported due to the +// cost of serialization when sorting map keys. +// - Simple values that are unassigned/reserved as per RFC 7049 are not +// supported and treated as errors. + +namespace cbor { + +class CBOR_EXPORT CBORReader { + public: + using Bytes = std::vector<uint8_t>; + + enum class DecoderError { + CBOR_NO_ERROR = 0, + UNSUPPORTED_MAJOR_TYPE, + UNKNOWN_ADDITIONAL_INFO, + INCOMPLETE_CBOR_DATA, + INCORRECT_MAP_KEY_TYPE, + TOO_MUCH_NESTING, + INVALID_UTF8, + EXTRANEOUS_DATA, + DUPLICATE_KEY, + OUT_OF_ORDER_KEY, + NON_MINIMAL_CBOR_ENCODING, + UNSUPPORTED_SIMPLE_VALUE, + UNSUPPORTED_FLOATING_POINT_VALUE, + OUT_OF_RANGE_INTEGER_VALUE, + }; + + // CBOR nested depth sufficient for most use cases. + static const int kCBORMaxDepth = 16; + + ~CBORReader(); + + // Reads and parses |input_data| into a CBORValue. If any one of the syntax + // formats is violated -including unknown additional info and incomplete + // CBOR data- then an empty optional is returned. Optional |error_code_out| + // can be provided by the caller to obtain additional information about + // decoding failures. + static base::Optional<CBORValue> Read(const Bytes& input_data, + DecoderError* error_code_out = nullptr, + int max_nesting_level = kCBORMaxDepth); + + // Translates errors to human-readable error messages. + static const char* ErrorCodeToString(DecoderError error_code); + + private: + CBORReader(Bytes::const_iterator it, const Bytes::const_iterator end); + base::Optional<CBORValue> DecodeCBOR(int max_nesting_level); + base::Optional<CBORValue> DecodeValueToNegative(uint64_t value); + base::Optional<CBORValue> DecodeValueToUnsigned(uint64_t value); + base::Optional<CBORValue> ReadSimpleValue(uint8_t additional_info, + uint64_t value); + bool ReadVariadicLengthInteger(uint8_t additional_info, uint64_t* value); + base::Optional<CBORValue> ReadBytes(uint64_t num_bytes); + base::Optional<CBORValue> ReadString(uint64_t num_bytes); + base::Optional<CBORValue> ReadCBORArray(uint64_t length, + int max_nesting_level); + base::Optional<CBORValue> ReadCBORMap(uint64_t length, int max_nesting_level); + bool CanConsume(uint64_t bytes); + void CheckExtraneousData(); + bool CheckDuplicateKey(const CBORValue& new_key, CBORValue::MapValue* map); + bool HasValidUTF8Format(const std::string& string_data); + bool CheckOutOfOrderKey(const CBORValue& new_key, CBORValue::MapValue* map); + bool CheckMinimalEncoding(uint8_t additional_bytes, uint64_t uint_data); + + DecoderError GetErrorCode(); + + Bytes::const_iterator it_; + const Bytes::const_iterator end_; + DecoderError error_code_; + + DISALLOW_COPY_AND_ASSIGN(CBORReader); +}; + +} // namespace cbor + +#endif // COMPONENTS_CBOR_CBOR_READER_H_ diff --git a/chromium/components/cbor/cbor_reader_fuzzer.cc b/chromium/components/cbor/cbor_reader_fuzzer.cc new file mode 100644 index 00000000000..4809e704adb --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer.cc @@ -0,0 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> +#include <algorithm> + +#include "components/cbor/cbor_reader.h" // nogncheck +#include "components/cbor/cbor_writer.h" // nogncheck + +namespace cbor { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::vector<uint8_t> input(data, data + size); + base::Optional<CBORValue> cbor = CBORReader::Read(input); + + if (cbor.has_value()) { + base::Optional<std::vector<uint8_t>> serialized_cbor = + CBORWriter::Write(cbor.value()); + CHECK(serialized_cbor.has_value()); + if (serialized_cbor.has_value()) { + CHECK(serialized_cbor.value().size() == input.size()); + CHECK(memcmp(serialized_cbor.value().data(), input.data(), + input.size()) == 0); + } + } + return 0; +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor0 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor0 Binary files differnew file mode 100644 index 00000000000..f76dd238ade --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor0 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor1 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor1 new file mode 100644 index 00000000000..6b2aaa76407 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor1 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor10 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor10 new file mode 100644 index 00000000000..fed1e13fcfa --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor10 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor11 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor11 Binary files differnew file mode 100644 index 00000000000..00a3f0c5362 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor11 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor12 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor12 new file mode 100644 index 00000000000..077457ad259 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor12 @@ -0,0 +1 @@ +;
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor13 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor13 Binary files differnew file mode 100644 index 00000000000..b4818a3b38b --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor13 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor14 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor14 new file mode 100644 index 00000000000..0519ecba6ea --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor14 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor15 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor15 new file mode 100644 index 00000000000..e8a0f87653d --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor15 @@ -0,0 +1 @@ +)
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor16 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor16 new file mode 100644 index 00000000000..bfefdad18c4 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor16 @@ -0,0 +1 @@ +8c
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor17 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor17 new file mode 100644 index 00000000000..5de255544fc --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor17 @@ -0,0 +1 @@ +9
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor18 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor18 Binary files differnew file mode 100644 index 00000000000..e43ae5af822 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor18 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor19 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor19 Binary files differnew file mode 100644 index 00000000000..6935671d0de --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor19 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor2 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor2 new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor2 @@ -0,0 +1 @@ + diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor20 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor20 Binary files differnew file mode 100644 index 00000000000..ed788bb3a4c --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor20 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor21 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor21 new file mode 100644 index 00000000000..fc9ea00d188 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor21 @@ -0,0 +1 @@ +?
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor22 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor22 Binary files differnew file mode 100644 index 00000000000..5ac3929dea8 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor22 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor23 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor23 new file mode 100644 index 00000000000..735f4675f10 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor23 @@ -0,0 +1 @@ +{
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor24 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor24 Binary files differnew file mode 100644 index 00000000000..f36a086ed50 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor24 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor25 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor25 new file mode 100644 index 00000000000..16f3345724b --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor25 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor26 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor26 Binary files differnew file mode 100644 index 00000000000..8e79bf519fe --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor26 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor27 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor27 Binary files differnew file mode 100644 index 00000000000..8e144ff3cd2 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor27 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor28 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor28 Binary files differnew file mode 100644 index 00000000000..16b0d43402f --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor28 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor29 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor29 Binary files differnew file mode 100644 index 00000000000..e5b8b4d8adb --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor29 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor3 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor3 new file mode 100644 index 00000000000..c96ab3cc70e --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor3 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor30 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor30 new file mode 100644 index 00000000000..815adc3e417 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor30 @@ -0,0 +1 @@ +ffffff
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor31 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor31 Binary files differnew file mode 100644 index 00000000000..2b119eb6592 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor31 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor32 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor32 Binary files differnew file mode 100644 index 00000000000..d73f58e36a2 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor32 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor33 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor33 Binary files differnew file mode 100644 index 00000000000..7c2eb64047e --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor33 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor34 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor34 Binary files differnew file mode 100644 index 00000000000..82965f3b13a --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor34 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor35 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor35 Binary files differnew file mode 100644 index 00000000000..2a9ccaec94a --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor35 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor36 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor36 Binary files differnew file mode 100644 index 00000000000..a9e0eebe00c --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor36 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor37 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor37 Binary files differnew file mode 100644 index 00000000000..c90d682d848 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor37 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor38 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor38 Binary files differnew file mode 100644 index 00000000000..802efe1966e --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor38 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor39 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor39 Binary files differnew file mode 100644 index 00000000000..7a1f61c5343 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor39 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor4 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor4 new file mode 100644 index 00000000000..a1910b3f6cc --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor4 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor40 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor40 new file mode 100644 index 00000000000..3a6e607aa5a --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor40 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor41 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor41 new file mode 100644 index 00000000000..bb7d13c5e9a --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor41 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor42 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor42 new file mode 100644 index 00000000000..f7a8cadeb57 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor42 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor43 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor43 new file mode 100644 index 00000000000..009080e8e0b --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor43 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor44 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor44 new file mode 100644 index 00000000000..04f7b5be698 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor44 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor45 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor45 new file mode 100644 index 00000000000..33c4e29de19 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor45 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor46 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor46 new file mode 100644 index 00000000000..a61a4624f2c --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor46 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor47 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor47 new file mode 100644 index 00000000000..d2fbff8927a --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor47 @@ -0,0 +1 @@ +t2013-03-21T20:04:00Z
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor48 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor48 new file mode 100644 index 00000000000..4ecf94681e9 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor48 @@ -0,0 +1 @@ +QKg
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor49 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor49 Binary files differnew file mode 100644 index 00000000000..c5c79cf7779 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor49 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor5 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor5 new file mode 100644 index 00000000000..d4e634a6b50 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor5 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor50 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor50 new file mode 100644 index 00000000000..7d0120d5bff --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor50 @@ -0,0 +1 @@ +D
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor51 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor51 new file mode 100644 index 00000000000..2784fd87e9b --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor51 @@ -0,0 +1 @@ +EdIETF
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor52 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor52 new file mode 100644 index 00000000000..b7d2601ab13 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor52 @@ -0,0 +1 @@ + vhttp://www.example.com
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor53 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor53 new file mode 100644 index 00000000000..b516b2c489f --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor53 @@ -0,0 +1 @@ +@
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor54 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor54 new file mode 100644 index 00000000000..352e89a8017 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor54 @@ -0,0 +1 @@ +D
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor55 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor55 new file mode 100644 index 00000000000..64845fb7679 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor55 @@ -0,0 +1 @@ +`
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor56 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor56 new file mode 100644 index 00000000000..7ec9a4b774e --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor56 @@ -0,0 +1 @@ +aa
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor57 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor57 new file mode 100644 index 00000000000..743669bb19c --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor57 @@ -0,0 +1 @@ +dIETF
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor58 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor58 new file mode 100644 index 00000000000..cce6615381d --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor58 @@ -0,0 +1 @@ +b"\
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor59 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor59 new file mode 100644 index 00000000000..9055a92aa61 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor59 @@ -0,0 +1 @@ +bü
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor6 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor6 new file mode 100644 index 00000000000..3b5c90718af --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor6 @@ -0,0 +1 @@ +d
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor60 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor60 new file mode 100644 index 00000000000..0868c9a4288 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor60 @@ -0,0 +1 @@ +c水
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor61 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor61 new file mode 100644 index 00000000000..8ea48a07a27 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor61 @@ -0,0 +1 @@ +d𐅑
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor62 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor62 new file mode 100644 index 00000000000..5416677bc7d --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor62 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor63 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor63 new file mode 100644 index 00000000000..4cdf6ce18a8 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor63 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor64 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor64 new file mode 100644 index 00000000000..80fd3949e51 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor64 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor65 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor65 new file mode 100644 index 00000000000..b4495842ec6 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor65 @@ -0,0 +1,2 @@ + +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor66 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor66 new file mode 100644 index 00000000000..eea1bf0c31f --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor66 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor67 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor67 new file mode 100644 index 00000000000..abc169a8cc5 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor67 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor68 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor68 new file mode 100644 index 00000000000..81e38473ff6 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor68 @@ -0,0 +1 @@ +aaab
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor69 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor69 new file mode 100644 index 00000000000..9b1d08caf44 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor69 @@ -0,0 +1 @@ +aaabac
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor7 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor7 new file mode 100644 index 00000000000..fda74fd9c88 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor7 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor70 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor70 new file mode 100644 index 00000000000..05bb07fc9d1 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor70 @@ -0,0 +1 @@ +aaaAabaBacaCadaDaeaE
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor71 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor71 new file mode 100644 index 00000000000..2681a857cb7 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor71 @@ -0,0 +1 @@ +_BC
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor72 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor72 new file mode 100644 index 00000000000..d0daf282398 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor72 @@ -0,0 +1 @@ +estreadming
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor73 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor73 new file mode 100644 index 00000000000..422ce24eaac --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor73 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor74 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor74 new file mode 100644 index 00000000000..f67cd5703a8 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor74 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor75 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor75 new file mode 100644 index 00000000000..78343ed40e3 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor75 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor76 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor76 new file mode 100644 index 00000000000..a0f1ea25969 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor76 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor77 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor77 new file mode 100644 index 00000000000..dbbf8e51a3f --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor77 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor78 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor78 new file mode 100644 index 00000000000..81c9910e824 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor78 @@ -0,0 +1,2 @@ + +
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor79 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor79 new file mode 100644 index 00000000000..018602c1e13 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor79 @@ -0,0 +1 @@ +aaab
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor8 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor8 Binary files differnew file mode 100644 index 00000000000..916281951cd --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor8 diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor80 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor80 new file mode 100644 index 00000000000..151c13e53d3 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor80 @@ -0,0 +1 @@ +aaabac
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor81 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor81 new file mode 100644 index 00000000000..c0f4fd5eb32 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor81 @@ -0,0 +1 @@ +cFuncAmt!
\ No newline at end of file diff --git a/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor9 b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor9 Binary files differnew file mode 100644 index 00000000000..4cdcd7ccc3f --- /dev/null +++ b/chromium/components/cbor/cbor_reader_fuzzer_corpus/cbor9 diff --git a/chromium/components/cbor/cbor_reader_unittest.cc b/chromium/components/cbor/cbor_reader_unittest.cc new file mode 100644 index 00000000000..e9819bf9498 --- /dev/null +++ b/chromium/components/cbor/cbor_reader_unittest.cc @@ -0,0 +1,821 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <limits> +#include <utility> + +#include "components/cbor/cbor_reader.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +/* Leveraging RFC 7049 examples from + https://github.com/cbor/test-vectors/blob/master/appendix_a.json. */ +namespace cbor { + +TEST(CBORReaderTest, TestReadUint) { + struct UintTestCase { + const int64_t value; + const std::vector<uint8_t> cbor_data; + }; + + static const UintTestCase kUintTestCases[] = { + {0, {0x00}}, + {1, {0x01}}, + {23, {0x17}}, + {24, {0x18, 0x18}}, + {std::numeric_limits<uint8_t>::max(), {0x18, 0xff}}, + {1LL << 8, {0x19, 0x01, 0x00}}, + {std::numeric_limits<uint16_t>::max(), {0x19, 0xff, 0xff}}, + {1LL << 16, {0x1a, 0x00, 0x01, 0x00, 0x00}}, + {std::numeric_limits<uint32_t>::max(), {0x1a, 0xff, 0xff, 0xff, 0xff}}, + {1LL << 32, {0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, + {std::numeric_limits<int64_t>::max(), + {0x1b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + }; + + for (const UintTestCase& test_case : kUintTestCases) { + SCOPED_TRACE(testing::Message() << "testing uint: " << test_case.value); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor.value().GetInteger(), test_case.value); + } +} + +TEST(CBORReaderTest, TestUintEncodedWithNonMinimumByteLength) { + static const std::vector<uint8_t> non_minimal_uint_encodings[] = { + // Uint 23 encoded with 1 byte. + {0x18, 0x17}, + // Uint 255 encoded with 2 bytes. + {0x19, 0x00, 0xff}, + // Uint 65535 encoded with 4 byte. + {0x1a, 0x00, 0x00, 0xff, 0xff}, + // Uint 4294967295 encoded with 8 byte. + {0x1b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}, + + // When decoding byte has more than one syntax error, the first syntax + // error encountered during deserialization is returned as the error code. + { + 0xa2, // map with non-minimally encoded key + 0x17, // key 24 + 0x61, 0x42, // value :"B" + + 0x18, 0x17, // key 23 encoded with extra byte + 0x61, 0x45 // value "E" + }, + { + 0xa2, // map with out of order and non-minimally encoded key + 0x18, 0x17, // key 23 encoded with extra byte + 0x61, 0x45, // value "E" + 0x17, // key 23 + 0x61, 0x42 // value :"B" + }, + { + 0xa2, // map with duplicate non-minimally encoded key + 0x18, 0x17, // key 23 encoded with extra byte + 0x61, 0x45, // value "E" + 0x18, 0x17, // key 23 encoded with extra byte + 0x61, 0x42 // value :"B" + }, + }; + + int test_case_index = 0; + CBORReader::DecoderError error_code; + for (const auto& non_minimal_uint : non_minimal_uint_encodings) { + SCOPED_TRACE(testing::Message() + << "testing element at index : " << test_case_index++); + + base::Optional<CBORValue> cbor = + CBORReader::Read(non_minimal_uint, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::NON_MINIMAL_CBOR_ENCODING); + } +} + +TEST(CBORReaderTest, TestReadNegativeInt) { + struct NegativeIntTestCase { + const int64_t negative_int; + const std::vector<uint8_t> cbor_data; + }; + + static const NegativeIntTestCase kNegativeIntTestCases[] = { + {-1LL, {0x20}}, + {-24LL, {0x37}}, + {-25LL, {0x38, 0x18}}, + {-256LL, {0x38, 0xff}}, + {-1000LL, {0x39, 0x03, 0xe7}}, + {-1000000LL, {0x3a, 0x00, 0x0f, 0x42, 0x3f}}, + {-4294967296LL, {0x3a, 0xff, 0xff, 0xff, 0xff}}, + {std::numeric_limits<int64_t>::min(), + {0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}; + + for (const NegativeIntTestCase& test_case : kNegativeIntTestCases) { + SCOPED_TRACE(testing::Message() + << "testing negative int : " << test_case.negative_int); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::NEGATIVE); + EXPECT_EQ(cbor.value().GetInteger(), test_case.negative_int); + } +} + +TEST(CBORReaderTest, TestReadBytes) { + struct ByteTestCase { + const std::vector<uint8_t> value; + const std::vector<uint8_t> cbor_data; + }; + + static const ByteTestCase kByteStringTestCases[] = { + // clang-format off + {{}, {0x40}}, + {{0x01, 0x02, 0x03, 0x04}, {0x44, 0x01, 0x02, 0x03, 0x04}}, + // clang-format on + }; + + int element_index = 0; + for (const ByteTestCase& test_case : kByteStringTestCases) { + SCOPED_TRACE(testing::Message() + << "testing string test case at : " << element_index++); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::BYTE_STRING); + EXPECT_EQ(cbor.value().GetBytestring(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadString) { + struct StringTestCase { + const std::string value; + const std::vector<uint8_t> cbor_data; + }; + + static const StringTestCase kStringTestCases[] = { + {"", {0x60}}, + {"a", {0x61, 0x61}}, + {"IETF", {0x64, 0x49, 0x45, 0x54, 0x46}}, + {"\"\\", {0x62, 0x22, 0x5c}}, + {"\xc3\xbc", {0x62, 0xc3, 0xbc}}, + {"\xe6\xb0\xb4", {0x63, 0xe6, 0xb0, 0xb4}}, + {"\xf0\x90\x85\x91", {0x64, 0xf0, 0x90, 0x85, 0x91}}, + }; + + for (const StringTestCase& test_case : kStringTestCases) { + SCOPED_TRACE(testing::Message() + << "testing string value : " << test_case.value); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::STRING); + EXPECT_EQ(cbor.value().GetString(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadStringWithNUL) { + static const struct { + const std::string value; + const std::vector<uint8_t> cbor_data; + } kStringTestCases[] = { + {std::string("string_without_nul"), + {0x72, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x5F, 0x77, 0x69, 0x74, 0x68, + 0x6F, 0x75, 0x74, 0x5F, 0x6E, 0x75, 0x6C}}, + {std::string("nul_terminated_string\0", 22), + {0x76, 0x6E, 0x75, 0x6C, 0x5F, 0x74, 0x65, 0x72, 0x6D, 0x69, 0x6E, 0x61, + 0x74, 0x65, 0x64, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x00}}, + {std::string("embedded\0nul", 12), + {0x6C, 0x65, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00, 0x6E, 0x75, + 0x6C}}, + {std::string("trailing_nuls\0\0", 15), + {0x6F, 0x74, 0x72, 0x61, 0x69, 0x6C, 0x69, 0x6E, 0x67, 0x5F, 0x6E, 0x75, + 0x6C, 0x73, 0x00, 0x00}}, + }; + + for (const auto& test_case : kStringTestCases) { + SCOPED_TRACE(testing::Message() + << "testing string with nul bytes :" << test_case.value); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::STRING); + EXPECT_EQ(cbor.value().GetString(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadStringWithInvalidByteSequenceAfterNUL) { + // UTF-8 validation should not stop at the first NUL character in the string. + // That is, a string with an invalid byte sequence should fail UTF-8 + // validation even if the invalid character is located after one or more NUL + // characters. Here, 0xA6 is an unexpected continuation byte. + static const std::vector<uint8_t> string_with_invalid_continuation_byte = { + 0x63, 0x00, 0x00, 0xA6}; + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(string_with_invalid_continuation_byte, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INVALID_UTF8); +} + +TEST(CBORReaderTest, TestReadArray) { + static const std::vector<uint8_t> kArrayTestCaseCbor = { + // clang-format off + 0x98, 0x19, // array of 25 elements + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x18, 0x18, 0x19, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kArrayTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + const CBORValue cbor_array = std::move(cbor.value()); + ASSERT_EQ(cbor_array.type(), CBORValue::Type::ARRAY); + ASSERT_THAT(cbor_array.GetArray(), testing::SizeIs(25)); + + std::vector<CBORValue> array; + for (int i = 0; i < 25; i++) { + SCOPED_TRACE(testing::Message() << "testing array element at index " << i); + + ASSERT_EQ(cbor_array.GetArray()[i].type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_array.GetArray()[i].GetInteger(), + static_cast<int64_t>(i + 1)); + } +} + +TEST(CBORReaderTest, TestReadMapWithMapValue) { + static const std::vector<uint8_t> kMapTestCaseCbor = { + // clang-format off + 0xa4, // map with 4 key value pairs: + 0x18, 0x18, // 24 + 0x63, 0x61, 0x62, 0x63, // "abc" + + 0x60, // "" + 0x61, 0x2e, // "." + + 0x61, 0x62, // "b" + 0x61, 0x42, // "B" + + 0x62, 0x61, 0x61, // "aa" + 0x62, 0x41, 0x41, // "AA" + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kMapTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + const CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 4u); + + const CBORValue key_uint(24); + ASSERT_EQ(cbor_val.GetMap().count(key_uint), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_uint)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_uint)->second.GetString(), "abc"); + + const CBORValue key_empty_string(""); + ASSERT_EQ(cbor_val.GetMap().count(key_empty_string), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_empty_string)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_empty_string)->second.GetString(), "."); + + const CBORValue key_b("b"); + ASSERT_EQ(cbor_val.GetMap().count(key_b), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_b)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_b)->second.GetString(), "B"); + + const CBORValue key_aa("aa"); + ASSERT_EQ(cbor_val.GetMap().count(key_aa), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_aa)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_aa)->second.GetString(), "AA"); +} + +TEST(CBORReaderTest, TestReadMapWithIntegerKeys) { + static const std::vector<uint8_t> kMapWithIntegerKeyCbor = { + // clang-format off + 0xA4, // map with 4 key value pairs + 0x01, // key : 1 + 0x61, 0x61, // value : "a" + + 0x09, // key : 9 + 0x61, 0x62, // value : "b" + + 0x19, 0x03, 0xE7, // key : 999 + 0x61, 0x63, // value "c" + + 0x19, 0x04, 0x57, // key : 1111 + 0x61, 0x64, // value : "d" + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kMapWithIntegerKeyCbor); + ASSERT_TRUE(cbor.has_value()); + const CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 4u); + + const CBORValue key_1(1); + ASSERT_EQ(cbor_val.GetMap().count(key_1), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_1)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_1)->second.GetString(), "a"); + + const CBORValue key_9(9); + ASSERT_EQ(cbor_val.GetMap().count(key_9), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_9)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_9)->second.GetString(), "b"); + + const CBORValue key_999(999); + ASSERT_EQ(cbor_val.GetMap().count(key_999), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_999)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_999)->second.GetString(), "c"); + + const CBORValue key_1111(1111); + ASSERT_EQ(cbor_val.GetMap().count(key_1111), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_1111)->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find(key_1111)->second.GetString(), "d"); +} + +TEST(CBORReaderTest, TestReadMapWithArray) { + static const std::vector<uint8_t> kMapArrayTestCaseCbor = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0x82, // array with 2 elements + 0x02, + 0x03, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kMapArrayTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + const CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 2u); + + const CBORValue key_a("a"); + ASSERT_EQ(cbor_val.GetMap().count(key_a), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_a)->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_val.GetMap().find(key_a)->second.GetInteger(), 1u); + + const CBORValue key_b("b"); + ASSERT_EQ(cbor_val.GetMap().count(key_b), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_b)->second.type(), + CBORValue::Type::ARRAY); + + const CBORValue nested_array = cbor_val.GetMap().find(key_b)->second.Clone(); + ASSERT_EQ(nested_array.GetArray().size(), 2u); + for (int i = 0; i < 2; i++) { + ASSERT_THAT(nested_array.GetArray()[i].type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_array.GetArray()[i].GetInteger(), + static_cast<int64_t>(i + 2)); + } +} + +TEST(CBORReaderTest, TestReadNestedMap) { + static const std::vector<uint8_t> kNestedMapTestCase = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0xa2, // map of 2 pairs + 0x61, 0x63, // "c" + 0x02, + + 0x61, 0x64, // "d" + 0x03, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kNestedMapTestCase); + ASSERT_TRUE(cbor.has_value()); + const CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 2u); + + const CBORValue key_a("a"); + ASSERT_EQ(cbor_val.GetMap().count(key_a), 1u); + ASSERT_EQ(cbor_val.GetMap().find(key_a)->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_val.GetMap().find(key_a)->second.GetInteger(), 1u); + + const CBORValue key_b("b"); + ASSERT_EQ(cbor_val.GetMap().count(key_b), 1u); + const CBORValue nested_map = cbor_val.GetMap().find(key_b)->second.Clone(); + ASSERT_EQ(nested_map.type(), CBORValue::Type::MAP); + ASSERT_EQ(nested_map.GetMap().size(), 2u); + + const CBORValue key_c("c"); + ASSERT_EQ(nested_map.GetMap().count(key_c), 1u); + ASSERT_EQ(nested_map.GetMap().find(key_c)->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_map.GetMap().find(key_c)->second.GetInteger(), 2u); + + const CBORValue key_d("d"); + ASSERT_EQ(nested_map.GetMap().count(key_d), 1u); + ASSERT_EQ(nested_map.GetMap().find(key_d)->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_map.GetMap().find(key_d)->second.GetInteger(), 3u); +} + +TEST(CBORReaderTest, TestIntegerRange) { + static const std::vector<uint8_t> kMaxPositiveInt = { + 0x1b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static const std::vector<uint8_t> kMinNegativeInt = { + 0x3b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + base::Optional<CBORValue> max_positive_int = + CBORReader::Read(kMaxPositiveInt); + ASSERT_TRUE(max_positive_int.has_value()); + EXPECT_EQ(max_positive_int.value().GetInteger(), INT64_MAX); + + base::Optional<CBORValue> min_negative_int = + CBORReader::Read(kMinNegativeInt); + ASSERT_TRUE(min_negative_int.has_value()); + EXPECT_EQ(min_negative_int.value().GetInteger(), INT64_MIN); +} + +TEST(CBORReaderTest, TestIntegerOutOfRangeError) { + static const std::vector<uint8_t> kOutOfRangePositiveInt = { + 0x1b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + static const std::vector<uint8_t> kOutOfRangeNegativeInt = { + 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> positive_int_out_of_range_cbor = + CBORReader::Read(kOutOfRangePositiveInt, &error_code); + EXPECT_FALSE(positive_int_out_of_range_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::OUT_OF_RANGE_INTEGER_VALUE); + + base::Optional<CBORValue> negative_int_out_of_range_cbor = + CBORReader::Read(kOutOfRangeNegativeInt, &error_code); + EXPECT_FALSE(negative_int_out_of_range_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::OUT_OF_RANGE_INTEGER_VALUE); +} + +TEST(CBORReaderTest, TestReadSimpleValue) { + static const struct { + const CBORValue::SimpleValue value; + const std::vector<uint8_t> cbor_data; + } kSimpleValueTestCases[] = { + {CBORValue::SimpleValue::FALSE_VALUE, {0xf4}}, + {CBORValue::SimpleValue::TRUE_VALUE, {0xf5}}, + {CBORValue::SimpleValue::NULL_VALUE, {0xf6}}, + {CBORValue::SimpleValue::UNDEFINED, {0xf7}}, + }; + + int test_element_index = 0; + for (const auto& test_case : kSimpleValueTestCases) { + SCOPED_TRACE(testing::Message() + << "testing simple value at index : " << test_element_index++); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::SIMPLE_VALUE); + EXPECT_EQ(cbor.value().GetSimpleValue(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadUnsupportedFloatingPointNumbers) { + static const std::vector<uint8_t> floating_point_cbors[] = { + // 16 bit floating point value. + {0xf9, 0x10, 0x00}, + // 32 bit floating point value. + {0xfa, 0x10, 0x00, 0x00, 0x00}, + // 64 bit floating point value. + {0xfb, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + for (const auto& unsupported_floating_point : floating_point_cbors) { + SCOPED_TRACE(testing::Message() + << "testing unsupported floating point : " + << testing::PrintToString(unsupported_floating_point)); + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(unsupported_floating_point, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, + CBORReader::DecoderError::UNSUPPORTED_FLOATING_POINT_VALUE); + } +} + +TEST(CBORReaderTest, TestIncompleteCBORDataError) { + static const std::vector<uint8_t> incomplete_cbor_list[] = { + // Additional info byte corresponds to unsigned int that corresponds + // to 2 additional bytes. But actual data encoded in one byte. + {0x19, 0x03}, + // CBOR bytestring of length 3 encoded with additional info of length 4. + {0x44, 0x01, 0x02, 0x03}, + // CBOR string data "IETF" of length 4 encoded with additional info of + // length 5. + {0x65, 0x49, 0x45, 0x54, 0x46}, + // CBOR array of length 1 encoded with additional info of length 2. + {0x82, 0x02}, + // CBOR map with single key value pair encoded with additional info of + // length 2. + {0xa2, 0x61, 0x61, 0x01}, + }; + + int test_element_index = 0; + for (const auto& incomplete_data : incomplete_cbor_list) { + SCOPED_TRACE(testing::Message() << "testing incomplete data at index : " + << test_element_index++); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(incomplete_data, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INCOMPLETE_CBOR_DATA); + } +} + +// While RFC 7049 allows CBOR map keys with all types, current decoder only +// supports unsigned integer and string keys. +TEST(CBORReaderTest, TestUnsupportedMapKeyFormatError) { + static const std::vector<uint8_t> kMapWithUintKey = { + // clang-format off + 0xa2, // map of 2 pairs + + 0x82, 0x01, 0x02, // invalid key : [1, 2] + 0x02, // value : 2 + + 0x61, 0x64, // key : "d" + 0x03, // value : 3 + // clang-format on + }; + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(kMapWithUintKey, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INCORRECT_MAP_KEY_TYPE); +} + +TEST(CBORReaderTest, TestUnknownAdditionalInfoError) { + static const std::vector<uint8_t> kUnknownAdditionalInfoList[] = { + // "IETF" encoded with major type 3 and additional info of 28. + {0x7C, 0x49, 0x45, 0x54, 0x46}, + // "\"\\" encoded with major type 3 and additional info of 29. + {0x7D, 0x22, 0x5c}, + // "\xc3\xbc" encoded with major type 3 and additional info of 30. + {0x7E, 0xc3, 0xbc}, + // "\xe6\xb0\xb4" encoded with major type 3 and additional info of 31. + {0x7F, 0xe6, 0xb0, 0xb4}, + // Major type 7, additional information 28: unassigned. + {0xFC}, + // Major type 7, additional information 29: unassigned. + {0xFD}, + // Major type 7, additional information 30: unassigned. + {0xFE}, + // Major type 7, additional information 31: "break" stop code for + // indefinite-length items. + {0xFF}, + }; + + int test_element_index = 0; + for (const auto& incorrect_cbor : kUnknownAdditionalInfoList) { + SCOPED_TRACE(testing::Message() + << "testing data at index : " << test_element_index++); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(incorrect_cbor, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::UNKNOWN_ADDITIONAL_INFO); + } +} + +TEST(CBORReaderTest, TestTooMuchNestingError) { + static const std::vector<uint8_t> kZeroDepthCBORList[] = { + // Unsigned int with value 100. + {0x18, 0x64}, + // CBOR bytestring of length 4. + {0x44, 0x01, 0x02, 0x03, 0x04}, + // CBOR string of corresponding to "IETF. + {0x64, 0x49, 0x45, 0x54, 0x46}, + // Empty CBOR array. + {0x80}, + // Empty CBOR Map + {0xa0}, + }; + + int test_element_index = 0; + for (const auto& zero_depth_data : kZeroDepthCBORList) { + SCOPED_TRACE(testing::Message() + << "testing zero nested data : " << test_element_index++); + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(zero_depth_data, &error_code, 0); + EXPECT_TRUE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::CBOR_NO_ERROR); + } + + // Corresponds to a CBOR structure with a nesting depth of 2: + // {"a": 1, + // "b": [2, 3]} + static const std::vector<uint8_t> kNestedCBORData = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0x82, // array with 2 elements + 0x02, + 0x03, + // clang-format on + }; + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor_single_layer_max = + CBORReader::Read(kNestedCBORData, &error_code, 1); + EXPECT_FALSE(cbor_single_layer_max.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::TOO_MUCH_NESTING); + + base::Optional<CBORValue> cbor_double_layer_max = + CBORReader::Read(kNestedCBORData, &error_code, 2); + EXPECT_TRUE(cbor_double_layer_max.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::CBOR_NO_ERROR); +} + +TEST(CBORReaderTest, TestOutOfOrderKeyError) { + static const std::vector<uint8_t> kMapsWithUnsortedKeys[] = { + // clang-format off + {0xa2, // map with 2 keys with same major type and length + 0x61, 0x62, // key "b" + 0x61, 0x42, // value :"B" + + 0x61, 0x61, // key "a" (out of order byte-wise lexically) + 0x61, 0x45 // value "E" + }, + {0xa2, // map with 2 keys with different major type + 0x61, 0x62, // key "b" + 0x02, // value 2 + + // key 1000 (out of order since lower major type sorts first) + 0x19, 0x03, 0xe8, + 0x61, 0x61, // value a + }, + {0xa2, // map with 2 keys with same major type + 0x19, 0x03, 0xe8, // key 1000 (out of order due to longer length) + 0x61, 0x61, //value "a" + + 0x0a, // key 10 + 0x61, 0x62}, // value "b" + //clang-format on + }; + + int test_element_index = 0; + CBORReader::DecoderError error_code; + for (const auto& unsorted_map : kMapsWithUnsortedKeys) { + testing::Message scope_message; + scope_message << "testing unsorted map : " << test_element_index++; + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> cbor = + CBORReader::Read(unsorted_map, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::OUT_OF_ORDER_KEY); + } +} + +TEST(CBORReaderTest, TestDuplicateKeyError) { + static const std::vector<uint8_t> kMapWithDuplicateKey = { + // clang-format off + 0xa6, // map of 6 pairs: + 0x60, // "" + 0x61, 0x2e, // "." + + 0x61, 0x62, // "b" + 0x61, 0x42, // "B" + + 0x61, 0x62, // "b" (Duplicate key) + 0x61, 0x43, // "C" + + 0x61, 0x64, // "d" + 0x61, 0x44, // "D" + + 0x61, 0x65, // "e" + 0x61, 0x44, // "D" + + 0x62, 0x61, 0x61, // "aa" + 0x62, 0x41, 0x41, // "AA" + // clang-format on + }; + + CBORReader::DecoderError error_code; + + base::Optional<CBORValue> cbor = + CBORReader::Read(kMapWithDuplicateKey, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::DUPLICATE_KEY); +} + +// Leveraging Markus Kuhn’s UTF-8 decoder stress test. See +// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt for details. +TEST(CBORReaderTest, TestIncorrectStringEncodingError) { + static const std::vector<uint8_t> utf8_character_encodings[] = { + // Corresponds to utf8 encoding of "" (section 2.3.1 of stress test). + {0x63, 0xED, 0x9F, 0xBF}, + // Corresponds to utf8 encoding of "" (section 2.3.2 of stress test). + {0x63, 0xEE, 0x80, 0x80}, + // Corresponds to utf8 encoding of "�" (section 2.3.3 of stress test). + {0x63, 0xEF, 0xBF, 0xBD}, + }; + + int test_element_index = 0; + CBORReader::DecoderError error_code; + for (const auto& cbor_byte : utf8_character_encodings) { + SCOPED_TRACE(testing::Message() << "testing cbor data utf8 encoding : " + << test_element_index++); + + base::Optional<CBORValue> correctly_encoded_cbor = + CBORReader::Read(cbor_byte, &error_code); + EXPECT_TRUE(correctly_encoded_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::CBOR_NO_ERROR); + } + + // Incorrect UTF8 encoding referenced by section 3.5.3 of the stress test. + std::vector<uint8_t> impossible_utf_byte{0x64, 0xfe, 0xfe, 0xff, 0xff}; + base::Optional<CBORValue> incorrectly_encoded_cbor = + CBORReader::Read(impossible_utf_byte, &error_code); + EXPECT_FALSE(incorrectly_encoded_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INVALID_UTF8); +} + +TEST(CBORReaderTest, TestExtraneousCBORDataError) { + static const std::vector<uint8_t> zero_padded_cbor_list[] = { + // 1 extra byte after a 2-byte unsigned int. + {0x19, 0x03, 0x05, 0x00}, + // 1 extra byte after a 4-byte cbor byte array. + {0x44, 0x01, 0x02, 0x03, 0x04, 0x00}, + // 1 extra byte after a 4-byte string. + {0x64, 0x49, 0x45, 0x54, 0x46, 0x00}, + // 1 extra byte after CBOR array of length 2. + {0x82, 0x01, 0x02, 0x00}, + // 1 extra key value pair after CBOR map of size 2. + {0xa1, 0x61, 0x63, 0x02, 0x61, 0x64, 0x03}, + }; + + int test_element_index = 0; + for (const auto& extraneous_cbor_data : zero_padded_cbor_list) { + SCOPED_TRACE(testing::Message() + << "testing cbor extraneous data : " << test_element_index++); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(extraneous_cbor_data, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::EXTRANEOUS_DATA); + } +} + +TEST(CBORReaderTest, TestUnsupportedSimplevalue) { + static const std::vector<uint8_t> unsupported_simple_values[] = { + // Simple value (0, unassigned) + {0xE0}, + // Simple value (19, unassigned) + {0xF3}, + // Simple value (24, reserved) + {0xF8, 0x18}, + // Simple value (28, reserved) + {0xF8, 0x1C}, + // Simple value (29, reserved) + {0xF8, 0x1D}, + // Simple value (30, reserved) + {0xF8, 0x1E}, + // Simple value (31, reserved) + {0xF8, 0x1F}, + // Simple value (32, unassigned) + {0xF8, 0x20}, + // Simple value (255, unassigned) + {0xF8, 0xFF}, + }; + + for (const auto& unsupported_simple_val : unsupported_simple_values) { + SCOPED_TRACE(testing::Message() + << "testing unsupported cbor simple value : " + << ::testing::PrintToString(unsupported_simple_val)); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(unsupported_simple_val, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::UNSUPPORTED_SIMPLE_VALUE); + } +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_values.cc b/chromium/components/cbor/cbor_values.cc new file mode 100644 index 00000000000..b4517650b45 --- /dev/null +++ b/chromium/components/cbor/cbor_values.cc @@ -0,0 +1,226 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cbor/cbor_values.h" + +#include <new> +#include <utility> + +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_util.h" + +namespace cbor { + +CBORValue::CBORValue() noexcept : type_(Type::NONE) {} + +CBORValue::CBORValue(CBORValue&& that) noexcept { + InternalMoveConstructFrom(std::move(that)); +} + +CBORValue::CBORValue(Type type) : type_(type) { + // Initialize with the default value. + switch (type_) { + case Type::UNSIGNED: + case Type::NEGATIVE: + integer_value_ = 0; + return; + case Type::BYTE_STRING: + new (&bytestring_value_) BinaryValue(); + return; + case Type::STRING: + new (&string_value_) std::string(); + return; + case Type::ARRAY: + new (&array_value_) ArrayValue(); + return; + case Type::MAP: + new (&map_value_) MapValue(); + return; + case Type::SIMPLE_VALUE: + simple_value_ = CBORValue::SimpleValue::UNDEFINED; + return; + case Type::NONE: + return; + } + NOTREACHED(); +} + +CBORValue::CBORValue(int integer_value) + : CBORValue(base::checked_cast<int64_t>(integer_value)) {} + +CBORValue::CBORValue(int64_t integer_value) : integer_value_(integer_value) { + type_ = integer_value >= 0 ? Type::UNSIGNED : Type::NEGATIVE; +} + +CBORValue::CBORValue(const BinaryValue& in_bytes) + : type_(Type::BYTE_STRING), bytestring_value_(in_bytes) {} + +CBORValue::CBORValue(BinaryValue&& in_bytes) noexcept + : type_(Type::BYTE_STRING), bytestring_value_(std::move(in_bytes)) {} + +CBORValue::CBORValue(const char* in_string) + : CBORValue(std::string(in_string)) {} + +CBORValue::CBORValue(std::string&& in_string) noexcept + : type_(Type::STRING), string_value_(std::move(in_string)) { + DCHECK(base::IsStringUTF8(string_value_)); +} + +CBORValue::CBORValue(base::StringPiece in_string) + : CBORValue(in_string.as_string()) {} + +CBORValue::CBORValue(const ArrayValue& in_array) + : type_(Type::ARRAY), array_value_() { + array_value_.reserve(in_array.size()); + for (const auto& val : in_array) + array_value_.emplace_back(val.Clone()); +} + +CBORValue::CBORValue(ArrayValue&& in_array) noexcept + : type_(Type::ARRAY), array_value_(std::move(in_array)) {} + +CBORValue::CBORValue(const MapValue& in_map) : type_(Type::MAP), map_value_() { + map_value_.reserve(in_map.size()); + for (const auto& it : in_map) + map_value_.emplace_hint(map_value_.end(), it.first.Clone(), + it.second.Clone()); +} + +CBORValue::CBORValue(MapValue&& in_map) noexcept + : type_(Type::MAP), map_value_(std::move(in_map)) {} + +CBORValue::CBORValue(SimpleValue in_simple) + : type_(Type::SIMPLE_VALUE), simple_value_(in_simple) { + CHECK(static_cast<int>(in_simple) >= 20 && static_cast<int>(in_simple) <= 23); +} + +CBORValue& CBORValue::operator=(CBORValue&& that) noexcept { + InternalCleanup(); + InternalMoveConstructFrom(std::move(that)); + + return *this; +} + +CBORValue::~CBORValue() { + InternalCleanup(); +} + +CBORValue CBORValue::Clone() const { + switch (type_) { + case Type::NONE: + return CBORValue(); + case Type::UNSIGNED: + case Type::NEGATIVE: + return CBORValue(integer_value_); + case Type::BYTE_STRING: + return CBORValue(bytestring_value_); + case Type::STRING: + return CBORValue(string_value_); + case Type::ARRAY: + return CBORValue(array_value_); + case Type::MAP: + return CBORValue(map_value_); + case Type::SIMPLE_VALUE: + return CBORValue(simple_value_); + } + + NOTREACHED(); + return CBORValue(); +} + +const int64_t& CBORValue::GetInteger() const { + CHECK(is_integer()); + return integer_value_; +} + +const int64_t& CBORValue::GetUnsigned() const { + CHECK(is_unsigned()); + CHECK_GE(integer_value_, 0); + return integer_value_; +} + +const int64_t& CBORValue::GetNegative() const { + CHECK(is_negative()); + CHECK_LT(integer_value_, 0); + return integer_value_; +} + +const std::string& CBORValue::GetString() const { + CHECK(is_string()); + return string_value_; +} + +const CBORValue::BinaryValue& CBORValue::GetBytestring() const { + CHECK(is_bytestring()); + return bytestring_value_; +} + +const CBORValue::ArrayValue& CBORValue::GetArray() const { + CHECK(is_array()); + return array_value_; +} + +const CBORValue::MapValue& CBORValue::GetMap() const { + CHECK(is_map()); + return map_value_; +} + +CBORValue::SimpleValue CBORValue::GetSimpleValue() const { + CHECK(is_simple()); + return simple_value_; +} + +void CBORValue::InternalMoveConstructFrom(CBORValue&& that) { + type_ = that.type_; + + switch (type_) { + case Type::UNSIGNED: + case Type::NEGATIVE: + integer_value_ = that.integer_value_; + return; + case Type::BYTE_STRING: + new (&bytestring_value_) BinaryValue(std::move(that.bytestring_value_)); + return; + case Type::STRING: + new (&string_value_) std::string(std::move(that.string_value_)); + return; + case Type::ARRAY: + new (&array_value_) ArrayValue(std::move(that.array_value_)); + return; + case Type::MAP: + new (&map_value_) MapValue(std::move(that.map_value_)); + return; + case Type::SIMPLE_VALUE: + simple_value_ = that.simple_value_; + return; + case Type::NONE: + return; + } + NOTREACHED(); +} + +void CBORValue::InternalCleanup() { + switch (type_) { + case Type::BYTE_STRING: + bytestring_value_.~BinaryValue(); + break; + case Type::STRING: + string_value_.~basic_string(); + break; + case Type::ARRAY: + array_value_.~ArrayValue(); + break; + case Type::MAP: + map_value_.~MapValue(); + break; + case Type::NONE: + case Type::UNSIGNED: + case Type::NEGATIVE: + case Type::SIMPLE_VALUE: + break; + } + type_ = Type::NONE; +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_values.h b/chromium/components/cbor/cbor_values.h new file mode 100644 index 00000000000..53eea212a0c --- /dev/null +++ b/chromium/components/cbor/cbor_values.h @@ -0,0 +1,171 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CBOR_CBOR_VALUES_H_ +#define COMPONENTS_CBOR_CBOR_VALUES_H_ + +#include <stdint.h> +#include <string> +#include <tuple> +#include <vector> + +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "components/cbor/cbor_export.h" + +namespace cbor { + +// A class for Concise Binary Object Representation (CBOR) values. +// This does not support: +// * Floating-point numbers. +// * Indefinite-length encodings. +class CBOR_EXPORT CBORValue { + public: + struct CTAPLess { + // Comparison predicate to order keys in a dictionary as required by the + // Client-to-Authenticator Protocol (CTAP) spec 2.0. + // + // The sort order defined in CTAP is: + // • If the major types are different, the one with the lower value in + // numerical order sorts earlier. + // • If two keys have different lengths, the shorter one sorts earlier. + // • If two keys have the same length, the one with the lower value in + // (byte-wise) lexical order sorts earlier. + // + // See section 6 of https://fidoalliance.org/specs/fido-v2.0-rd-20170927/ + // fido-client-to-authenticator-protocol-v2.0-rd-20170927.html. + // + // THE CTAP SORT ORDER IMPLEMENTED HERE DIFFERS FROM THE CANONICAL CBOR + // ORDER defined in https://tools.ietf.org/html/rfc7049#section-3.9, in that + // the latter sorts purely by serialised key and doesn't specify that major + // types are compared first. Thus the shortest key sorts first by the RFC + // rules (irrespective of the major type), but may not by CTAP rules. + bool operator()(const CBORValue& a, const CBORValue& b) const { + DCHECK((a.is_integer() || a.is_string()) && + (b.is_integer() || b.is_string())); + if (a.type() != b.type()) + return a.type() < b.type(); + switch (a.type()) { + case Type::UNSIGNED: + return a.GetInteger() < b.GetInteger(); + case Type::NEGATIVE: + return a.GetInteger() > b.GetInteger(); + case Type::STRING: { + const auto& a_str = a.GetString(); + const size_t a_length = a_str.size(); + const auto& b_str = b.GetString(); + const size_t b_length = b_str.size(); + return std::tie(a_length, a_str) < std::tie(b_length, b_str); + } + default: + break; + } + + NOTREACHED(); + return false; + } + + using is_transparent = void; + }; + + using BinaryValue = std::vector<uint8_t>; + using ArrayValue = std::vector<CBORValue>; + using MapValue = base::flat_map<CBORValue, CBORValue, CTAPLess>; + + enum class Type { + UNSIGNED = 0, + NEGATIVE = 1, + BYTE_STRING = 2, + STRING = 3, + ARRAY = 4, + MAP = 5, + SIMPLE_VALUE = 7, + NONE = -1, + }; + + enum class SimpleValue { + FALSE_VALUE = 20, + TRUE_VALUE = 21, + NULL_VALUE = 22, + UNDEFINED = 23, + }; + + CBORValue(CBORValue&& that) noexcept; + CBORValue() noexcept; // A NONE value. + + explicit CBORValue(Type type); + explicit CBORValue(int integer_value); + explicit CBORValue(int64_t integer_value); + explicit CBORValue(uint64_t integer_value) = delete; + + explicit CBORValue(const BinaryValue& in_bytes); + explicit CBORValue(BinaryValue&& in_bytes) noexcept; + + explicit CBORValue(const char* in_string); + explicit CBORValue(std::string&& in_string) noexcept; + explicit CBORValue(base::StringPiece in_string); + + explicit CBORValue(const ArrayValue& in_array); + explicit CBORValue(ArrayValue&& in_array) noexcept; + + explicit CBORValue(const MapValue& in_map); + explicit CBORValue(MapValue&& in_map) noexcept; + + explicit CBORValue(SimpleValue in_simple); + + CBORValue& operator=(CBORValue&& that) noexcept; + + ~CBORValue(); + + // CBORValue's copy constructor and copy assignment operator are deleted. + // Use this to obtain a deep copy explicitly. + CBORValue Clone() const; + + // Returns the type of the value stored by the current Value object. + Type type() const { return type_; } + + // Returns true if the current object represents a given type. + bool is_type(Type type) const { return type == type_; } + bool is_none() const { return type() == Type::NONE; } + bool is_unsigned() const { return type() == Type::UNSIGNED; } + bool is_negative() const { return type() == Type::NEGATIVE; } + bool is_integer() const { return is_unsigned() || is_negative(); } + bool is_bytestring() const { return type() == Type::BYTE_STRING; } + bool is_string() const { return type() == Type::STRING; } + bool is_array() const { return type() == Type::ARRAY; } + bool is_map() const { return type() == Type::MAP; } + bool is_simple() const { return type() == Type::SIMPLE_VALUE; } + + // These will all fatally assert if the type doesn't match. + SimpleValue GetSimpleValue() const; + const int64_t& GetInteger() const; + const int64_t& GetUnsigned() const; + const int64_t& GetNegative() const; + const BinaryValue& GetBytestring() const; + // Returned string may contain NUL characters. + const std::string& GetString() const; + const ArrayValue& GetArray() const; + const MapValue& GetMap() const; + + private: + Type type_; + + union { + SimpleValue simple_value_; + int64_t integer_value_; + BinaryValue bytestring_value_; + std::string string_value_; + ArrayValue array_value_; + MapValue map_value_; + }; + + void InternalMoveConstructFrom(CBORValue&& that); + void InternalCleanup(); + + DISALLOW_COPY_AND_ASSIGN(CBORValue); +}; +} // namespace cbor + +#endif // COMPONENTS_CBOR_CBOR_VALUES_H_ diff --git a/chromium/components/cbor/cbor_values_unittest.cc b/chromium/components/cbor/cbor_values_unittest.cc new file mode 100644 index 00000000000..b003e83be9b --- /dev/null +++ b/chromium/components/cbor/cbor_values_unittest.cc @@ -0,0 +1,354 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cbor/cbor_values.h" + +#include <string> +#include <utility> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace cbor { + +TEST(CBORValuesTest, TestNothrow) { + static_assert(std::is_nothrow_move_constructible<CBORValue>::value, + "IsNothrowMoveConstructible"); + static_assert(std::is_nothrow_default_constructible<CBORValue>::value, + "IsNothrowDefaultConstructible"); + static_assert(std::is_nothrow_constructible<CBORValue, std::string&&>::value, + "IsNothrowMoveConstructibleFromString"); + static_assert( + std::is_nothrow_constructible<CBORValue, CBORValue::BinaryValue&&>::value, + "IsNothrowMoveConstructibleFromBytestring"); + static_assert( + std::is_nothrow_constructible<CBORValue, CBORValue::ArrayValue&&>::value, + "IsNothrowMoveConstructibleFromArray"); + static_assert(std::is_nothrow_move_assignable<CBORValue>::value, + "IsNothrowMoveAssignable"); +} + +// Test constructors +TEST(CBORValuesTest, ConstructUnsigned) { + CBORValue value(37); + ASSERT_EQ(CBORValue::Type::UNSIGNED, value.type()); + EXPECT_EQ(37u, value.GetInteger()); +} + +TEST(CBORValuesTest, ConstructNegative) { + CBORValue value(-1); + ASSERT_EQ(CBORValue::Type::NEGATIVE, value.type()); + EXPECT_EQ(-1, value.GetInteger()); +} + +TEST(CBORValuesTest, ConstructStringFromConstCharPtr) { + const char* str = "foobar"; + CBORValue value(str); + ASSERT_EQ(CBORValue::Type::STRING, value.type()); + EXPECT_EQ("foobar", value.GetString()); +} + +TEST(CBORValuesTest, ConstructStringFromStdStringConstRef) { + std::string str = "foobar"; + CBORValue value(str); + ASSERT_EQ(CBORValue::Type::STRING, value.type()); + EXPECT_EQ("foobar", value.GetString()); +} + +TEST(CBORValuesTest, ConstructStringFromStdStringRefRef) { + std::string str = "foobar"; + CBORValue value(std::move(str)); + ASSERT_EQ(CBORValue::Type::STRING, value.type()); + EXPECT_EQ("foobar", value.GetString()); +} + +TEST(CBORValuesTest, ConstructBytestring) { + CBORValue value(CBORValue::BinaryValue({0xF, 0x0, 0x0, 0xB, 0xA, 0x2})); + ASSERT_EQ(CBORValue::Type::BYTE_STRING, value.type()); + EXPECT_EQ(CBORValue::BinaryValue({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}), + value.GetBytestring()); +} + +TEST(CBORValuesTest, ConstructArray) { + CBORValue::ArrayValue array; + array.emplace_back(CBORValue("foo")); + { + CBORValue value(array); + ASSERT_EQ(CBORValue::Type::ARRAY, value.type()); + ASSERT_EQ(1u, value.GetArray().size()); + ASSERT_EQ(CBORValue::Type::STRING, value.GetArray()[0].type()); + EXPECT_EQ("foo", value.GetArray()[0].GetString()); + } + + array.back() = CBORValue("bar"); + { + CBORValue value(std::move(array)); + ASSERT_EQ(CBORValue::Type::ARRAY, value.type()); + ASSERT_EQ(1u, value.GetArray().size()); + ASSERT_EQ(CBORValue::Type::STRING, value.GetArray()[0].type()); + EXPECT_EQ("bar", value.GetArray()[0].GetString()); + } +} + +TEST(CBORValuesTest, ConstructMap) { + CBORValue::MapValue map; + const CBORValue key_foo("foo"); + map[CBORValue("foo")] = CBORValue("bar"); + { + CBORValue value(map); + ASSERT_EQ(CBORValue::Type::MAP, value.type()); + ASSERT_EQ(value.GetMap().count(key_foo), 1u); + ASSERT_EQ(CBORValue::Type::STRING, + value.GetMap().find(key_foo)->second.type()); + EXPECT_EQ("bar", value.GetMap().find(key_foo)->second.GetString()); + } + + map[CBORValue("foo")] = CBORValue("baz"); + { + CBORValue value(std::move(map)); + ASSERT_EQ(CBORValue::Type::MAP, value.type()); + ASSERT_EQ(value.GetMap().count(key_foo), 1u); + ASSERT_EQ(CBORValue::Type::STRING, + value.GetMap().find(key_foo)->second.type()); + EXPECT_EQ("baz", value.GetMap().find(key_foo)->second.GetString()); + } +} + +TEST(CBORValuesTest, ConstructSimpleValue) { + CBORValue false_value(CBORValue::SimpleValue::FALSE_VALUE); + ASSERT_EQ(CBORValue::Type::SIMPLE_VALUE, false_value.type()); + EXPECT_EQ(CBORValue::SimpleValue::FALSE_VALUE, false_value.GetSimpleValue()); + + CBORValue true_value(CBORValue::SimpleValue::TRUE_VALUE); + ASSERT_EQ(CBORValue::Type::SIMPLE_VALUE, true_value.type()); + EXPECT_EQ(CBORValue::SimpleValue::TRUE_VALUE, true_value.GetSimpleValue()); + + CBORValue null_value(CBORValue::SimpleValue::NULL_VALUE); + ASSERT_EQ(CBORValue::Type::SIMPLE_VALUE, null_value.type()); + EXPECT_EQ(CBORValue::SimpleValue::NULL_VALUE, null_value.GetSimpleValue()); + + CBORValue undefined_value(CBORValue::SimpleValue::UNDEFINED); + ASSERT_EQ(CBORValue::Type::SIMPLE_VALUE, undefined_value.type()); + EXPECT_EQ(CBORValue::SimpleValue::UNDEFINED, + undefined_value.GetSimpleValue()); +} + +// Test copy constructors +TEST(CBORValuesTest, CopyUnsigned) { + CBORValue value(74); + CBORValue copied_value(value.Clone()); + ASSERT_EQ(value.type(), copied_value.type()); + EXPECT_EQ(value.GetInteger(), copied_value.GetInteger()); + + CBORValue blank; + + blank = value.Clone(); + ASSERT_EQ(value.type(), blank.type()); + EXPECT_EQ(value.GetInteger(), blank.GetInteger()); +} + +TEST(CBORValuesTest, CopyNegativeInt) { + CBORValue value(-74); + CBORValue copied_value(value.Clone()); + ASSERT_EQ(value.type(), copied_value.type()); + EXPECT_EQ(value.GetInteger(), copied_value.GetInteger()); + + CBORValue blank; + + blank = value.Clone(); + ASSERT_EQ(value.type(), blank.type()); + EXPECT_EQ(value.GetInteger(), blank.GetInteger()); +} + +TEST(CBORValuesTest, CopyString) { + CBORValue value("foobar"); + CBORValue copied_value(value.Clone()); + ASSERT_EQ(value.type(), copied_value.type()); + EXPECT_EQ(value.GetString(), copied_value.GetString()); + + CBORValue blank; + + blank = value.Clone(); + ASSERT_EQ(value.type(), blank.type()); + EXPECT_EQ(value.GetString(), blank.GetString()); +} + +TEST(CBORValuesTest, CopyBytestring) { + CBORValue value(CBORValue::BinaryValue({0xF, 0x0, 0x0, 0xB, 0xA, 0x2})); + CBORValue copied_value(value.Clone()); + ASSERT_EQ(value.type(), copied_value.type()); + EXPECT_EQ(value.GetBytestring(), copied_value.GetBytestring()); + + CBORValue blank; + + blank = value.Clone(); + ASSERT_EQ(value.type(), blank.type()); + EXPECT_EQ(value.GetBytestring(), blank.GetBytestring()); +} + +TEST(CBORValuesTest, CopyArray) { + CBORValue::ArrayValue array; + array.emplace_back(123); + CBORValue value(std::move(array)); + + CBORValue copied_value(value.Clone()); + ASSERT_EQ(1u, copied_value.GetArray().size()); + ASSERT_TRUE(copied_value.GetArray()[0].is_unsigned()); + EXPECT_EQ(value.GetArray()[0].GetInteger(), + copied_value.GetArray()[0].GetInteger()); + + CBORValue blank; + blank = value.Clone(); + EXPECT_EQ(1u, blank.GetArray().size()); +} + +TEST(CBORValuesTest, CopyMap) { + CBORValue::MapValue map; + CBORValue key_a("a"); + map[CBORValue("a")] = CBORValue(123); + CBORValue value(std::move(map)); + + CBORValue copied_value(value.Clone()); + EXPECT_EQ(1u, copied_value.GetMap().size()); + ASSERT_EQ(value.GetMap().count(key_a), 1u); + ASSERT_EQ(copied_value.GetMap().count(key_a), 1u); + ASSERT_TRUE(copied_value.GetMap().find(key_a)->second.is_unsigned()); + EXPECT_EQ(value.GetMap().find(key_a)->second.GetInteger(), + copied_value.GetMap().find(key_a)->second.GetInteger()); + + CBORValue blank; + blank = value.Clone(); + EXPECT_EQ(1u, blank.GetMap().size()); + ASSERT_EQ(blank.GetMap().count(key_a), 1u); + ASSERT_TRUE(blank.GetMap().find(key_a)->second.is_unsigned()); + EXPECT_EQ(value.GetMap().find(key_a)->second.GetInteger(), + blank.GetMap().find(key_a)->second.GetInteger()); +} + +TEST(CBORValuesTest, CopySimpleValue) { + CBORValue value(CBORValue::SimpleValue::TRUE_VALUE); + CBORValue copied_value(value.Clone()); + EXPECT_EQ(value.type(), copied_value.type()); + EXPECT_EQ(value.GetSimpleValue(), copied_value.GetSimpleValue()); + + CBORValue blank; + + blank = value.Clone(); + EXPECT_EQ(value.type(), blank.type()); + EXPECT_EQ(value.GetSimpleValue(), blank.GetSimpleValue()); +} + +// Test move constructors and move-assignment +TEST(CBORValuesTest, MoveUnsigned) { + CBORValue value(74); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::UNSIGNED, moved_value.type()); + EXPECT_EQ(74u, moved_value.GetInteger()); + + CBORValue blank; + + blank = CBORValue(654); + EXPECT_EQ(CBORValue::Type::UNSIGNED, blank.type()); + EXPECT_EQ(654u, blank.GetInteger()); +} + +TEST(CBORValuesTest, MoveNegativeInteger) { + CBORValue value(-74); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::NEGATIVE, moved_value.type()); + EXPECT_EQ(-74, moved_value.GetInteger()); + + CBORValue blank; + + blank = CBORValue(-654); + EXPECT_EQ(CBORValue::Type::NEGATIVE, blank.type()); + EXPECT_EQ(-654, blank.GetInteger()); +} + +TEST(CBORValuesTest, MoveString) { + CBORValue value("foobar"); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::STRING, moved_value.type()); + EXPECT_EQ("foobar", moved_value.GetString()); + + CBORValue blank; + + blank = CBORValue("foobar"); + EXPECT_EQ(CBORValue::Type::STRING, blank.type()); + EXPECT_EQ("foobar", blank.GetString()); +} + +TEST(CBORValuesTest, MoveBytestring) { + const CBORValue::BinaryValue bytes({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}); + CBORValue value(bytes); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::BYTE_STRING, moved_value.type()); + EXPECT_EQ(bytes, moved_value.GetBytestring()); + + CBORValue blank; + + blank = CBORValue(bytes); + EXPECT_EQ(CBORValue::Type::BYTE_STRING, blank.type()); + EXPECT_EQ(bytes, blank.GetBytestring()); +} + +TEST(CBORValuesTest, MoveConstructMap) { + CBORValue::MapValue map; + const CBORValue key_a("a"); + map[CBORValue("a")] = CBORValue(123); + + CBORValue value(std::move(map)); + CBORValue moved_value(std::move(value)); + ASSERT_EQ(CBORValue::Type::MAP, moved_value.type()); + ASSERT_EQ(moved_value.GetMap().count(key_a), 1u); + ASSERT_TRUE(moved_value.GetMap().find(key_a)->second.is_unsigned()); + EXPECT_EQ(123u, moved_value.GetMap().find(key_a)->second.GetInteger()); +} + +TEST(CBORValuesTest, MoveAssignMap) { + CBORValue::MapValue map; + const CBORValue key_a("a"); + map[CBORValue("a")] = CBORValue(123); + + CBORValue blank; + blank = CBORValue(std::move(map)); + ASSERT_TRUE(blank.is_map()); + ASSERT_EQ(blank.GetMap().count(key_a), 1u); + ASSERT_TRUE(blank.GetMap().find(key_a)->second.is_unsigned()); + EXPECT_EQ(123u, blank.GetMap().find(key_a)->second.GetInteger()); +} + +TEST(CBORValuesTest, MoveArray) { + CBORValue::ArrayValue array; + array.emplace_back(123); + CBORValue value(array); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::ARRAY, moved_value.type()); + EXPECT_EQ(123u, moved_value.GetArray().back().GetInteger()); + + CBORValue blank; + blank = CBORValue(std::move(array)); + EXPECT_EQ(CBORValue::Type::ARRAY, blank.type()); + EXPECT_EQ(123u, blank.GetArray().back().GetInteger()); +} + +TEST(CBORValuesTest, MoveSimpleValue) { + CBORValue value(CBORValue::SimpleValue::UNDEFINED); + CBORValue moved_value(std::move(value)); + EXPECT_EQ(CBORValue::Type::SIMPLE_VALUE, moved_value.type()); + EXPECT_EQ(CBORValue::SimpleValue::UNDEFINED, moved_value.GetSimpleValue()); + + CBORValue blank; + + blank = CBORValue(CBORValue::SimpleValue::UNDEFINED); + EXPECT_EQ(CBORValue::Type::SIMPLE_VALUE, blank.type()); + EXPECT_EQ(CBORValue::SimpleValue::UNDEFINED, blank.GetSimpleValue()); +} + +TEST(CBORValuesTest, SelfSwap) { + CBORValue test(1); + std::swap(test, test); + EXPECT_EQ(test.GetInteger(), 1u); +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_writer.cc b/chromium/components/cbor/cbor_writer.cc new file mode 100644 index 00000000000..d6e09fa54af --- /dev/null +++ b/chromium/components/cbor/cbor_writer.cc @@ -0,0 +1,176 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cbor/cbor_writer.h" + +#include <string> + +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_piece.h" +#include "components/cbor/cbor_binary.h" + +namespace cbor { + +CBORWriter::~CBORWriter() {} + +// static +base::Optional<std::vector<uint8_t>> CBORWriter::Write( + const CBORValue& node, + size_t max_nesting_level) { + std::vector<uint8_t> cbor; + CBORWriter writer(&cbor); + if (writer.EncodeCBOR(node, base::checked_cast<int>(max_nesting_level))) + return cbor; + return base::nullopt; +} + +CBORWriter::CBORWriter(std::vector<uint8_t>* cbor) : encoded_cbor_(cbor) {} + +bool CBORWriter::EncodeCBOR(const CBORValue& node, int max_nesting_level) { + if (max_nesting_level < 0) + return false; + + switch (node.type()) { + case CBORValue::Type::NONE: { + StartItem(CBORValue::Type::BYTE_STRING, 0); + return true; + } + + // Represents unsigned integers. + case CBORValue::Type::UNSIGNED: { + int64_t value = node.GetUnsigned(); + StartItem(CBORValue::Type::UNSIGNED, static_cast<uint64_t>(value)); + return true; + } + + // Represents negative integers. + case CBORValue::Type::NEGATIVE: { + int64_t value = node.GetNegative(); + StartItem(CBORValue::Type::NEGATIVE, static_cast<uint64_t>(-(value + 1))); + return true; + } + + // Represents a byte string. + case CBORValue::Type::BYTE_STRING: { + const CBORValue::BinaryValue& bytes = node.GetBytestring(); + StartItem(CBORValue::Type::BYTE_STRING, + base::strict_cast<uint64_t>(bytes.size())); + // Add the bytes. + encoded_cbor_->insert(encoded_cbor_->end(), bytes.begin(), bytes.end()); + return true; + } + + case CBORValue::Type::STRING: { + base::StringPiece string = node.GetString(); + StartItem(CBORValue::Type::STRING, + base::strict_cast<uint64_t>(string.size())); + + // Add the characters. + encoded_cbor_->insert(encoded_cbor_->end(), string.begin(), string.end()); + return true; + } + + // Represents an array. + case CBORValue::Type::ARRAY: { + const CBORValue::ArrayValue& array = node.GetArray(); + StartItem(CBORValue::Type::ARRAY, array.size()); + for (const auto& value : array) { + if (!EncodeCBOR(value, max_nesting_level - 1)) + return false; + } + return true; + } + + // Represents a map. + case CBORValue::Type::MAP: { + const CBORValue::MapValue& map = node.GetMap(); + StartItem(CBORValue::Type::MAP, map.size()); + + for (const auto& value : map) { + if (!EncodeCBOR(value.first, max_nesting_level - 1)) + return false; + if (!EncodeCBOR(value.second, max_nesting_level - 1)) + return false; + } + return true; + } + + // Represents a simple value. + case CBORValue::Type::SIMPLE_VALUE: { + const CBORValue::SimpleValue simple_value = node.GetSimpleValue(); + StartItem(CBORValue::Type::SIMPLE_VALUE, + base::checked_cast<uint64_t>(simple_value)); + return true; + } + + default: + break; + } + NOTREACHED(); + return false; +} + +void CBORWriter::StartItem(CBORValue::Type type, uint64_t size) { + encoded_cbor_->push_back(base::checked_cast<uint8_t>( + static_cast<unsigned>(type) << constants::kMajorTypeBitShift)); + SetUint(size); +} + +void CBORWriter::SetAdditionalInformation(uint8_t additional_information) { + DCHECK(!encoded_cbor_->empty()); + DCHECK_EQ(additional_information & constants::kAdditionalInformationMask, + additional_information); + encoded_cbor_->back() |= + (additional_information & constants::kAdditionalInformationMask); +} + +void CBORWriter::SetUint(uint64_t value) { + size_t count = GetNumUintBytes(value); + int shift = -1; + // Values under 24 are encoded directly in the initial byte. + // Otherwise, the last 5 bits of the initial byte contains the length + // of unsigned integer, which is encoded in following bytes. + switch (count) { + case 0: + SetAdditionalInformation(base::checked_cast<uint8_t>(value)); + break; + case 1: + SetAdditionalInformation(constants::kAdditionalInformation1Byte); + shift = 0; + break; + case 2: + SetAdditionalInformation(constants::kAdditionalInformation2Bytes); + shift = 1; + break; + case 4: + SetAdditionalInformation(constants::kAdditionalInformation4Bytes); + shift = 3; + break; + case 8: + SetAdditionalInformation(constants::kAdditionalInformation8Bytes); + shift = 7; + break; + default: + NOTREACHED(); + break; + } + for (; shift >= 0; shift--) { + encoded_cbor_->push_back(0xFF & (value >> (shift * 8))); + } +} + +size_t CBORWriter::GetNumUintBytes(uint64_t value) { + if (value < 24) { + return 0; + } else if (value <= 0xFF) { + return 1; + } else if (value <= 0xFFFF) { + return 2; + } else if (value <= 0xFFFFFFFF) { + return 4; + } + return 8; +} + +} // namespace cbor diff --git a/chromium/components/cbor/cbor_writer.h b/chromium/components/cbor/cbor_writer.h new file mode 100644 index 00000000000..89e24da742e --- /dev/null +++ b/chromium/components/cbor/cbor_writer.h @@ -0,0 +1,100 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CBOR_CBOR_WRITER_H_ +#define COMPONENTS_CBOR_CBOR_WRITER_H_ + +#include <stddef.h> +#include <stdint.h> +#include <vector> + +#include "base/optional.h" +#include "components/cbor/cbor_export.h" +#include "components/cbor/cbor_values.h" + +// A basic Concise Binary Object Representation (CBOR) encoder as defined by +// https://tools.ietf.org/html/rfc7049. This is a generic encoder that supplies +// canonical, well-formed CBOR values but does not guarantee their validity +// (see https://tools.ietf.org/html/rfc7049#section-3.2). +// Supported: +// * Major types: +// * 0: Unsigned integers, up to INT64_MAX. +// * 1: Negative integers, to INT64_MIN. +// * 2: Byte strings. +// * 3: UTF-8 strings. +// * 4: Arrays, with the number of elements known at the start. +// * 5: Maps, with the number of elements known at the start +// of the container. +// * 7: Simple values. +// +// Unsupported: +// * Floating-point numbers. +// * Indefinite-length encodings. +// * Parsing. +// +// Requirements for canonical CBOR as suggested by RFC 7049 are: +// 1) All major data types for the CBOR values must be as short as possible. +// * Unsigned integer between 0 to 23 must be expressed in same byte as +// the major type. +// * 24 to 255 must be expressed only with an additional uint8_t. +// * 256 to 65535 must be expressed only with an additional uint16_t. +// * 65536 to 4294967295 must be expressed only with an additional +// uint32_t. * The rules for expression of length in major types +// 2 to 5 follow the above rule for integers. +// 2) Keys in every map must be sorted (first by major type, then by key +// length, then by value in byte-wise lexical order). +// 3) Indefinite length items must be converted to definite length items. +// 4) All maps must not have duplicate keys. +// +// Current implementation of CBORWriter encoder meets all the requirements of +// canonical CBOR. + +namespace cbor { + +class CBOR_EXPORT CBORWriter { + public: + // Default that should be sufficiently large for most use cases. + static constexpr size_t kDefaultMaxNestingDepth = 16; + + ~CBORWriter(); + + // Returns the CBOR byte string representation of |node|, unless its nesting + // depth is greater than |max_nesting_depth|, in which case an empty optional + // value is returned. The nesting depth of |node| is defined as the number of + // arrays/maps that has to be traversed to reach the most nested CBORValue + // contained in |node|. Primitive values and empty containers have nesting + // depths of 0. + static base::Optional<std::vector<uint8_t>> Write( + const CBORValue& node, + size_t max_nesting_level = kDefaultMaxNestingDepth); + + private: + explicit CBORWriter(std::vector<uint8_t>* cbor); + + // Called recursively to build the CBOR bytestring. When completed, + // |encoded_cbor_| will contain the CBOR. + bool EncodeCBOR(const CBORValue& node, int max_nesting_level); + + // Encodes the type and size of the data being added. + void StartItem(CBORValue::Type type, uint64_t size); + + // Encodes the additional information for the data. + void SetAdditionalInformation(uint8_t additional_information); + + // Encodes an unsigned integer value. This is used to both write + // unsigned integers and to encode the lengths of other major types. + void SetUint(uint64_t value); + + // Get the number of bytes needed to store the unsigned integer. + size_t GetNumUintBytes(uint64_t value); + + // Holds the encoded CBOR data. + std::vector<uint8_t>* encoded_cbor_; + + DISALLOW_COPY_AND_ASSIGN(CBORWriter); +}; + +} // namespace cbor + +#endif // COMPONENTS_CBOR_CBOR_WRITER_H_ diff --git a/chromium/components/cbor/cbor_writer_unittest.cc b/chromium/components/cbor/cbor_writer_unittest.cc new file mode 100644 index 00000000000..5b047b6d2f4 --- /dev/null +++ b/chromium/components/cbor/cbor_writer_unittest.cc @@ -0,0 +1,431 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cbor/cbor_writer.h" + +#include <limits> +#include <string> + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +/* Leveraging RFC 7049 examples from + https://github.com/cbor/test-vectors/blob/master/appendix_a.json. */ +namespace cbor { + +TEST(CBORWriterTest, TestWriteUint) { + typedef struct { + const int64_t value; + const base::StringPiece cbor; + } UintTestCase; + + static const UintTestCase kUintTestCases[] = { + // Reminder: must specify length when creating string pieces + // with null bytes, else the string will truncate prematurely. + {0, base::StringPiece("\x00", 1)}, + {1, base::StringPiece("\x01")}, + {10, base::StringPiece("\x0a")}, + {23, base::StringPiece("\x17")}, + {24, base::StringPiece("\x18\x18")}, + {25, base::StringPiece("\x18\x19")}, + {100, base::StringPiece("\x18\x64")}, + {1000, base::StringPiece("\x19\x03\xe8")}, + {1000000, base::StringPiece("\x1a\x00\x0f\x42\x40", 5)}, + {0xFFFFFFFF, base::StringPiece("\x1a\xff\xff\xff\xff")}, + {0x100000000, + base::StringPiece("\x1b\x00\x00\x00\x01\x00\x00\x00\x00", 9)}, + {std::numeric_limits<int64_t>::max(), + base::StringPiece("\x1b\x7f\xff\xff\xff\xff\xff\xff\xff")}}; + + for (const UintTestCase& test_case : kUintTestCases) { + auto cbor = CBORWriter::Write(CBORValue(test_case.value)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor)); + } +} + +TEST(CBORWriterTest, TestWriteNegativeInteger) { + static const struct { + const int64_t negative_int; + const base::StringPiece cbor; + } kNegativeIntTestCases[] = { + {-1LL, base::StringPiece("\x20")}, + {-10LL, base::StringPiece("\x29")}, + {-23LL, base::StringPiece("\x36")}, + {-24LL, base::StringPiece("\x37")}, + {-25LL, base::StringPiece("\x38\x18")}, + {-100LL, base::StringPiece("\x38\x63")}, + {-1000LL, base::StringPiece("\x39\x03\xe7")}, + {-4294967296LL, base::StringPiece("\x3a\xff\xff\xff\xff")}, + {-4294967297LL, + base::StringPiece("\x3b\x00\x00\x00\x01\x00\x00\x00\x00", 9)}, + {std::numeric_limits<int64_t>::min(), + base::StringPiece("\x3b\x7f\xff\xff\xff\xff\xff\xff\xff")}, + }; + + for (const auto& test_case : kNegativeIntTestCases) { + SCOPED_TRACE(testing::Message() << "testing negative int at index: " + << test_case.negative_int); + + auto cbor = CBORWriter::Write(CBORValue(test_case.negative_int)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor)); + } +} + +TEST(CBORWriterTest, TestWriteBytes) { + typedef struct { + const std::vector<uint8_t> bytes; + const base::StringPiece cbor; + } BytesTestCase; + + static const BytesTestCase kBytesTestCases[] = { + {{}, base::StringPiece("\x40")}, + {{0x01, 0x02, 0x03, 0x04}, base::StringPiece("\x44\x01\x02\x03\x04")}, + }; + + for (const BytesTestCase& test_case : kBytesTestCases) { + auto cbor = CBORWriter::Write(CBORValue(test_case.bytes)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor)); + } +} + +TEST(CBORWriterTest, TestWriteString) { + typedef struct { + const std::string string; + const base::StringPiece cbor; + } StringTestCase; + + static const StringTestCase kStringTestCases[] = { + {"", base::StringPiece("\x60")}, + {"a", base::StringPiece("\x61\x61")}, + {"IETF", base::StringPiece("\x64\x49\x45\x54\x46")}, + {"\"\\", base::StringPiece("\x62\x22\x5c")}, + {"\xc3\xbc", base::StringPiece("\x62\xc3\xbc")}, + {"\xe6\xb0\xb4", base::StringPiece("\x63\xe6\xb0\xb4")}, + {"\xf0\x90\x85\x91", base::StringPiece("\x64\xf0\x90\x85\x91")}}; + + for (const StringTestCase& test_case : kStringTestCases) { + SCOPED_TRACE(testing::Message() + << "testing encoding string : " << test_case.string); + + auto cbor = CBORWriter::Write(CBORValue(test_case.string)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor)); + } +} + +TEST(CBORWriterTest, TestWriteArray) { + static const uint8_t kArrayTestCaseCbor[] = { + // clang-format off + 0x98, 0x19, // array of 25 elements + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x18, 0x18, 0x19, + // clang-format on + }; + std::vector<CBORValue> array; + for (int64_t i = 1; i <= 25; i++) { + array.push_back(CBORValue(i)); + } + auto cbor = CBORWriter::Write(CBORValue(array)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), + testing::ElementsAreArray(kArrayTestCaseCbor, + arraysize(kArrayTestCaseCbor))); +} + +TEST(CBORWriterTest, TestWriteMapWithMapValue) { + static const uint8_t kMapTestCaseCbor[] = { + // clang-format off + 0xb6, // map of 8 pairs: + 0x00, // key 0 + 0x61, 0x61, // value "a" + + 0x17, // key 23 + 0x61, 0x62, // value "b" + + 0x18, 0x18, // key 24 + 0x61, 0x63, // value "c" + + 0x18, 0xFF, // key 255 + 0x61, 0x64, // value "d" + + 0x19, 0x01, 0x00, // key 256 + 0x61, 0x65, // value "e" + + 0x19, 0xFF, 0xFF, // key 65535 + 0x61, 0x66, // value "f" + + 0x1A, 0x00, 0x01, 0x00, 0x00, // key 65536 + 0x61, 0x67, // value "g" + + 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, // key 4294967295 + 0x61, 0x68, // value "h" + + // key 4294967296 + 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x69, // value "i" + + // key INT64_MAX + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x61, 0x6a, // value "j" + + 0x20, // key -1 + 0x61, 0x6b, // value "k" + + 0x37, // key -24 + 0x61, 0x6c, // value "l" + + 0x38, 0x18, // key -25 + 0x61, 0x6d, // value "m" + + 0x38, 0xFF, // key -256 + 0x61, 0x6e, // value "n" + + 0x39, 0x01, 0x00, // key -257 + 0x61, 0x6f, // value "o" + + 0x3A, 0x00, 0x01, 0x00, 0x00, // key -65537 + 0x61, 0x70, // value "p" + + 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, // key -4294967296 + 0x61, 0x71, // value "q" + + // key -4294967297 + 0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x72, // value "r" + + // key INT64_MIN + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x61, 0x73, // value "s" + + 0x60, // key "" + 0x61, 0x2e, // value "." + + 0x61, 0x65, // key "e" + 0x61, 0x45, // value "E" + + 0x62, 0x61, 0x61, // key "aa" + 0x62, 0x41, 0x41, // value "AA" + // clang-format on + }; + CBORValue::MapValue map; + // Shorter strings sort first in CTAP, thus the “aa” value should be + // serialised last in the map. + map[CBORValue("aa")] = CBORValue("AA"); + map[CBORValue("e")] = CBORValue("E"); + // The empty string is shorter than all others, so should appear first among + // the strings. + map[CBORValue("")] = CBORValue("."); + // Map keys are sorted by major type, by byte length, and then by + // byte-wise lexical order. So all integer type keys should appear before + // key "" and all positive integer keys should appear before negative integer + // keys. + map[CBORValue(-1)] = CBORValue("k"); + map[CBORValue(-24)] = CBORValue("l"); + map[CBORValue(-25)] = CBORValue("m"); + map[CBORValue(-256)] = CBORValue("n"); + map[CBORValue(-257)] = CBORValue("o"); + map[CBORValue(-65537)] = CBORValue("p"); + map[CBORValue(int64_t(-4294967296))] = CBORValue("q"); + map[CBORValue(int64_t(-4294967297))] = CBORValue("r"); + map[CBORValue(std::numeric_limits<int64_t>::min())] = CBORValue("s"); + map[CBORValue(0)] = CBORValue("a"); + map[CBORValue(23)] = CBORValue("b"); + map[CBORValue(24)] = CBORValue("c"); + map[CBORValue(std::numeric_limits<uint8_t>::max())] = CBORValue("d"); + map[CBORValue(256)] = CBORValue("e"); + map[CBORValue(std::numeric_limits<uint16_t>::max())] = CBORValue("f"); + map[CBORValue(65536)] = CBORValue("g"); + map[CBORValue(int64_t(std::numeric_limits<uint32_t>::max()))] = + CBORValue("h"); + map[CBORValue(int64_t(4294967296))] = CBORValue("i"); + map[CBORValue(std::numeric_limits<int64_t>::max())] = CBORValue("j"); + auto cbor = CBORWriter::Write(CBORValue(map)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray( + kMapTestCaseCbor, arraysize(kMapTestCaseCbor))); +} + +TEST(CBORWriterTest, TestWriteMapWithArray) { + static const uint8_t kMapArrayTestCaseCbor[] = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0x82, // array with 2 elements + 0x02, + 0x03, + // clang-format on + }; + CBORValue::MapValue map; + map[CBORValue("a")] = CBORValue(1); + CBORValue::ArrayValue array; + array.push_back(CBORValue(2)); + array.push_back(CBORValue(3)); + map[CBORValue("b")] = CBORValue(array); + auto cbor = CBORWriter::Write(CBORValue(map)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), + testing::ElementsAreArray(kMapArrayTestCaseCbor, + arraysize(kMapArrayTestCaseCbor))); +} + +TEST(CBORWriterTest, TestWriteNestedMap) { + static const uint8_t kNestedMapTestCase[] = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0xa2, // map of 2 pairs + 0x61, 0x63, // "c" + 0x02, + + 0x61, 0x64, // "d" + 0x03, + // clang-format on + }; + CBORValue::MapValue map; + map[CBORValue("a")] = CBORValue(1); + CBORValue::MapValue nested_map; + nested_map[CBORValue("c")] = CBORValue(2); + nested_map[CBORValue("d")] = CBORValue(3); + map[CBORValue("b")] = CBORValue(nested_map); + auto cbor = CBORWriter::Write(CBORValue(map)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), + testing::ElementsAreArray(kNestedMapTestCase, + arraysize(kNestedMapTestCase))); +} + +TEST(CBORWriterTest, TestWriteSimpleValue) { + static const struct { + CBORValue::SimpleValue simple_value; + const base::StringPiece cbor; + } kSimpleTestCase[] = { + {CBORValue::SimpleValue::FALSE_VALUE, base::StringPiece("\xf4")}, + {CBORValue::SimpleValue::TRUE_VALUE, base::StringPiece("\xf5")}, + {CBORValue::SimpleValue::NULL_VALUE, base::StringPiece("\xf6")}, + {CBORValue::SimpleValue::UNDEFINED, base::StringPiece("\xf7")}}; + + for (const auto& test_case : kSimpleTestCase) { + auto cbor = CBORWriter::Write(CBORValue(test_case.simple_value)); + ASSERT_TRUE(cbor.has_value()); + EXPECT_THAT(cbor.value(), testing::ElementsAreArray(test_case.cbor)); + } +} + +// For major type 0, 2, 3, empty CBOR array, and empty CBOR map, the nesting +// depth is expected to be 0 since the CBOR decoder does not need to parse +// any nested CBOR value elements. +TEST(CBORWriterTest, TestWriteSingleLayer) { + const CBORValue simple_uint = CBORValue(1); + const CBORValue simple_string = CBORValue("a"); + const std::vector<uint8_t> byte_data = {0x01, 0x02, 0x03, 0x04}; + const CBORValue simple_bytestring = CBORValue(byte_data); + CBORValue::ArrayValue empty_cbor_array; + CBORValue::MapValue empty_cbor_map; + const CBORValue empty_array_value = CBORValue(empty_cbor_array); + const CBORValue empty_map_value = CBORValue(empty_cbor_map); + CBORValue::ArrayValue simple_array; + simple_array.push_back(CBORValue(2)); + CBORValue::MapValue simple_map; + simple_map[CBORValue("b")] = CBORValue(3); + const CBORValue single_layer_cbor_map = CBORValue(simple_map); + const CBORValue single_layer_cbor_array = CBORValue(simple_array); + + EXPECT_TRUE(CBORWriter::Write(simple_uint, 0).has_value()); + EXPECT_TRUE(CBORWriter::Write(simple_string, 0).has_value()); + EXPECT_TRUE(CBORWriter::Write(simple_bytestring, 0).has_value()); + + EXPECT_TRUE(CBORWriter::Write(empty_array_value, 0).has_value()); + EXPECT_TRUE(CBORWriter::Write(empty_map_value, 0).has_value()); + + EXPECT_FALSE(CBORWriter::Write(single_layer_cbor_array, 0).has_value()); + EXPECT_TRUE(CBORWriter::Write(single_layer_cbor_array, 1).has_value()); + + EXPECT_FALSE(CBORWriter::Write(single_layer_cbor_map, 0).has_value()); + EXPECT_TRUE(CBORWriter::Write(single_layer_cbor_map, 1).has_value()); +} + +// Major type 5 nested CBOR map value with following structure. +// {"a": 1, +// "b": {"c": 2, +// "d": 3}} +TEST(CBORWriterTest, NestedMaps) { + CBORValue::MapValue cbor_map; + cbor_map[CBORValue("a")] = CBORValue(1); + CBORValue::MapValue nested_map; + nested_map[CBORValue("c")] = CBORValue(2); + nested_map[CBORValue("d")] = CBORValue(3); + cbor_map[CBORValue("b")] = CBORValue(nested_map); + EXPECT_TRUE(CBORWriter::Write(CBORValue(cbor_map), 2).has_value()); + EXPECT_FALSE(CBORWriter::Write(CBORValue(cbor_map), 1).has_value()); +} + +// Testing Write() function for following CBOR structure with depth of 3. +// [1, +// 2, +// 3, +// {"a": 1, +// "b": {"c": 2, +// "d": 3}}] +TEST(CBORWriterTest, UnbalancedNestedContainers) { + CBORValue::ArrayValue cbor_array; + CBORValue::MapValue cbor_map; + CBORValue::MapValue nested_map; + + cbor_map[CBORValue("a")] = CBORValue(1); + nested_map[CBORValue("c")] = CBORValue(2); + nested_map[CBORValue("d")] = CBORValue(3); + cbor_map[CBORValue("b")] = CBORValue(nested_map); + cbor_array.push_back(CBORValue(1)); + cbor_array.push_back(CBORValue(2)); + cbor_array.push_back(CBORValue(3)); + cbor_array.push_back(CBORValue(cbor_map)); + + EXPECT_TRUE(CBORWriter::Write(CBORValue(cbor_array), 3).has_value()); + EXPECT_FALSE(CBORWriter::Write(CBORValue(cbor_array), 2).has_value()); +} + +// Testing Write() function for following CBOR structure. +// {"a": 1, +// "b": {"c": 2, +// "d": 3 +// "h": { "e": 4, +// "f": 5, +// "g": [6, 7, [8]]}}} +// Since above CBOR contains 5 nesting levels. Thus, Write() is expected to +// return empty optional object when maximum nesting layer size is set to 4. +TEST(CBORWriterTest, OverlyNestedCBOR) { + CBORValue::MapValue map; + CBORValue::MapValue nested_map; + CBORValue::MapValue inner_nested_map; + CBORValue::ArrayValue inner_array; + CBORValue::ArrayValue array; + + map[CBORValue("a")] = CBORValue(1); + nested_map[CBORValue("c")] = CBORValue(2); + nested_map[CBORValue("d")] = CBORValue(3); + inner_nested_map[CBORValue("e")] = CBORValue(4); + inner_nested_map[CBORValue("f")] = CBORValue(5); + inner_array.push_back(CBORValue(6)); + array.push_back(CBORValue(6)); + array.push_back(CBORValue(7)); + array.push_back(CBORValue(inner_array)); + inner_nested_map[CBORValue("g")] = CBORValue(array); + nested_map[CBORValue("h")] = CBORValue(inner_nested_map); + map[CBORValue("b")] = CBORValue(nested_map); + + EXPECT_TRUE(CBORWriter::Write(CBORValue(map), 5).has_value()); + EXPECT_FALSE(CBORWriter::Write(CBORValue(map), 4).has_value()); +} + +} // namespace cbor |