// 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/reader.h" #include #include #include "base/check_op.h" #include "base/notreached.h" #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/constants.h" namespace cbor { namespace constants { const char kUnsupportedMajorType[] = "Unsupported major type."; } namespace { Value::Type GetMajorType(uint8_t initial_data_byte) { return static_cast( (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. There is 1 // exception: we declare |kUnsupportedMajorType| in constants.h in the // `constants` namespace, because we use it in several files. const char kNoError[] = "Successfully deserialized to a CBOR value."; const char kUnknownAdditionalInfo[] = "Unknown additional info format in the first byte."; const char kIncompleteCBORData[] = "Prematurely terminated CBOR data byte array."; const char kIncorrectMapKeyType[] = "Specified map key type is not supported by the current implementation."; const char kTooMuchNesting[] = "Too much nesting."; const char kInvalidUTF8[] = "String encodings other than UTF-8 are not allowed."; const char kExtraneousData[] = "Trailing data bytes are not allowed."; const char kMapKeyOutOfOrder[] = "Map keys must be strictly monotonically increasing based on 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."; const char kUnknownError[] = "An unknown error occured."; } // namespace Reader::Config::Config() = default; Reader::Config::~Config() = default; Reader::Reader(base::span data) : rest_(data), error_code_(DecoderError::CBOR_NO_ERROR) {} Reader::~Reader() {} // static base::Optional Reader::Read(base::span data, DecoderError* error_code_out, int max_nesting_level) { Config config; config.error_code_out = error_code_out; config.max_nesting_level = max_nesting_level; return Read(data, config); } // static base::Optional Reader::Read(base::span data, size_t* num_bytes_consumed, DecoderError* error_code_out, int max_nesting_level) { DCHECK(num_bytes_consumed); Config config; config.num_bytes_consumed = num_bytes_consumed; config.error_code_out = error_code_out; config.max_nesting_level = max_nesting_level; return Read(data, config); } // static base::Optional Reader::Read(base::span data, const Config& config) { Reader reader(data); base::Optional value = reader.DecodeCompleteDataItem(config, config.max_nesting_level); auto error = reader.GetErrorCode(); const bool success = value.has_value(); DCHECK_EQ(success, error == DecoderError::CBOR_NO_ERROR); if (config.num_bytes_consumed) { *config.num_bytes_consumed = success ? data.size() - reader.num_bytes_remaining() : 0; } else if (success && reader.num_bytes_remaining() > 0) { error = DecoderError::EXTRANEOUS_DATA; value.reset(); } if (config.error_code_out) { *config.error_code_out = error; } return value; } base::Optional Reader::DecodeCompleteDataItem(const Config& config, int max_nesting_level) { if (max_nesting_level < 0 || max_nesting_level > kCBORMaxDepth) { error_code_ = DecoderError::TOO_MUCH_NESTING; return base::nullopt; } base::Optional header = DecodeDataItemHeader(); if (!header.has_value()) { return base::nullopt; } switch (header->type) { case Value::Type::UNSIGNED: return DecodeValueToUnsigned(header->value); case Value::Type::NEGATIVE: return DecodeValueToNegative(header->value); case Value::Type::BYTE_STRING: return ReadByteStringContent(*header); case Value::Type::STRING: return ReadStringContent(*header, config); case Value::Type::ARRAY: return ReadArrayContent(*header, config, max_nesting_level); case Value::Type::MAP: return ReadMapContent(*header, config, max_nesting_level); case Value::Type::SIMPLE_VALUE: return DecodeToSimpleValue(*header); case Value::Type::TAG: // We explicitly don't support TAG. case Value::Type::NONE: case Value::Type::INVALID_UTF8: break; } error_code_ = DecoderError::UNSUPPORTED_MAJOR_TYPE; return base::nullopt; } base::Optional Reader::DecodeDataItemHeader() { const base::Optional initial_byte = ReadByte(); if (!initial_byte) { return base::nullopt; } const auto major_type = GetMajorType(initial_byte.value()); const uint8_t additional_info = GetAdditionalInfo(initial_byte.value()); base::Optional value = ReadVariadicLengthInteger(additional_info); return value ? base::make_optional( DataItemHeader{major_type, additional_info, value.value()}) : base::nullopt; } base::Optional Reader::ReadVariadicLengthInteger( uint8_t additional_info) { uint8_t additional_bytes = 0; if (additional_info < 24) { return base::make_optional(additional_info); } 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 base::nullopt; } const base::Optional> bytes = ReadBytes(additional_bytes); if (!bytes) { return base::nullopt; } uint64_t int_data = 0; for (const uint8_t b : bytes.value()) { int_data <<= 8; int_data |= b; } return IsEncodingMinimal(additional_bytes, int_data) ? base::make_optional(int_data) : base::nullopt; } base::Optional Reader::DecodeValueToNegative(uint64_t value) { auto negative_value = -base::CheckedNumeric(value) - 1; if (!negative_value.IsValid()) { error_code_ = DecoderError::OUT_OF_RANGE_INTEGER_VALUE; return base::nullopt; } return Value(negative_value.ValueOrDie()); } base::Optional Reader::DecodeValueToUnsigned(uint64_t value) { auto unsigned_value = base::CheckedNumeric(value); if (!unsigned_value.IsValid()) { error_code_ = DecoderError::OUT_OF_RANGE_INTEGER_VALUE; return base::nullopt; } return Value(unsigned_value.ValueOrDie()); } base::Optional Reader::DecodeToSimpleValue( const DataItemHeader& header) { // ReadVariadicLengthInteger provides this bound. CHECK_LE(header.additional_info, 27); // Floating point numbers are not supported. if (header.additional_info > 24) { error_code_ = DecoderError::UNSUPPORTED_FLOATING_POINT_VALUE; return base::nullopt; } // Since |header.additional_info| <= 24, ReadVariadicLengthInteger also // provides this bound for |header.value|. CHECK_LE(header.value, 255u); // |SimpleValue| is an enum class and so the underlying type is specified to // be |int|. So this cast is safe. Value::SimpleValue possibly_unsupported_simple_value = static_cast(static_cast(header.value)); switch (possibly_unsupported_simple_value) { case Value::SimpleValue::FALSE_VALUE: case Value::SimpleValue::TRUE_VALUE: case Value::SimpleValue::NULL_VALUE: case Value::SimpleValue::UNDEFINED: return Value(possibly_unsupported_simple_value); } error_code_ = DecoderError::UNSUPPORTED_SIMPLE_VALUE; return base::nullopt; } base::Optional Reader::ReadStringContent( const Reader::DataItemHeader& header, const Config& config) { uint64_t num_bytes = header.value; const base::Optional> bytes = ReadBytes(num_bytes); if (!bytes) { return base::nullopt; } std::string cbor_string(bytes->begin(), bytes->end()); if (base::IsStringUTF8(cbor_string)) { return Value(std::move(cbor_string)); } if (config.allow_invalid_utf8) { return Value(*bytes, Value::Type::INVALID_UTF8); } error_code_ = DecoderError::INVALID_UTF8; return base::nullopt; } base::Optional Reader::ReadByteStringContent( const Reader::DataItemHeader& header) { uint64_t num_bytes = header.value; const base::Optional> bytes = ReadBytes(num_bytes); if (!bytes) { return base::nullopt; } std::vector cbor_byte_string(bytes->begin(), bytes->end()); return Value(std::move(cbor_byte_string)); } base::Optional Reader::ReadArrayContent( const Reader::DataItemHeader& header, const Config& config, int max_nesting_level) { const uint64_t length = header.value; Value::ArrayValue cbor_array; for (uint64_t i = 0; i < length; ++i) { base::Optional cbor_element = DecodeCompleteDataItem(config, max_nesting_level - 1); if (!cbor_element.has_value()) { return base::nullopt; } cbor_array.push_back(std::move(cbor_element.value())); } return Value(std::move(cbor_array)); } base::Optional Reader::ReadMapContent( const Reader::DataItemHeader& header, const Config& config, int max_nesting_level) { const uint64_t length = header.value; Value::MapValue cbor_map; for (uint64_t i = 0; i < length; ++i) { base::Optional key = DecodeCompleteDataItem(config, max_nesting_level - 1); base::Optional value = DecodeCompleteDataItem(config, max_nesting_level - 1); if (!key.has_value() || !value.has_value()) { return base::nullopt; } switch (key.value().type()) { case Value::Type::UNSIGNED: case Value::Type::NEGATIVE: case Value::Type::STRING: case Value::Type::BYTE_STRING: break; case Value::Type::INVALID_UTF8: error_code_ = DecoderError::INVALID_UTF8; return base::nullopt; default: error_code_ = DecoderError::INCORRECT_MAP_KEY_TYPE; return base::nullopt; } if (!IsKeyInOrder(key.value(), &cbor_map)) { return base::nullopt; } cbor_map.insert_or_assign(std::move(key.value()), std::move(value.value())); } return Value(std::move(cbor_map)); } base::Optional Reader::ReadByte() { const base::Optional> bytes = ReadBytes(1); return bytes ? base::make_optional(bytes.value()[0]) : base::nullopt; } base::Optional> Reader::ReadBytes( uint64_t num_bytes) { if (base::strict_cast(rest_.size()) < num_bytes) { error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; return base::nullopt; } const base::span ret = rest_.first(num_bytes); rest_ = rest_.subspan(num_bytes); return ret; } bool Reader::IsEncodingMinimal(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; } bool Reader::IsKeyInOrder(const Value& new_key, Value::MapValue* map) { if (map->empty()) { return true; } const auto& max_current_key = map->rbegin()->first; const auto less = map->key_comp(); if (!less(max_current_key, new_key)) { error_code_ = DecoderError::OUT_OF_ORDER_KEY; return false; } return true; } // static const char* Reader::ErrorCodeToString(DecoderError error) { switch (error) { case DecoderError::CBOR_NO_ERROR: return kNoError; case DecoderError::UNSUPPORTED_MAJOR_TYPE: return constants::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::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; case DecoderError::UNKNOWN_ERROR: return kUnknownError; default: NOTREACHED(); return "Unknown error code."; } } } // namespace cbor