From 55e7cd16202bd5a13e0aa0b1401852d875953162 Mon Sep 17 00:00:00 2001 From: Carlos Medeiros Date: Wed, 2 Nov 2022 16:08:26 +0000 Subject: [PATCH 01/81] update submodules --- deps/ledger-zxlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ledger-zxlib b/deps/ledger-zxlib index 08fa5d82..9a82671e 160000 --- a/deps/ledger-zxlib +++ b/deps/ledger-zxlib @@ -1 +1 @@ -Subproject commit 08fa5d82b4b71ad371961547defda0aa26a01e19 +Subproject commit 9a82671e9c4800425edbb6c55c8867d0b2a92a39 From fd72ffaeefa8e6347e7219866f1913acdf05b5be Mon Sep 17 00:00:00 2001 From: Carlos Medeiros Date: Thu, 10 Nov 2022 10:06:47 +0000 Subject: [PATCH 02/81] add cbor library --- .gitignore | 1 + CMakeLists.txt | 19 +- app/Makefile | 4 + deps/tinycbor-ledger/LICENSE | 21 + deps/tinycbor-ledger/README.md | 4 + deps/tinycbor-ledger/cborparser.c | 1529 +++++++++++++ deps/tinycbor-ledger/cborvalidation.c | 657 ++++++ deps/tinycbor/.appveyor.yml | 28 + deps/tinycbor/.gitattributes | 4 + deps/tinycbor/.gitignore | 81 + deps/tinycbor/.tag | 1 + deps/tinycbor/.travis.yml | 92 + deps/tinycbor/Doxyfile | 49 + deps/tinycbor/LICENSE | 21 + deps/tinycbor/Makefile | 256 +++ deps/tinycbor/README | 13 + deps/tinycbor/TODO | 25 + deps/tinycbor/VERSION | 1 + deps/tinycbor/examples/examples.pro | 2 + deps/tinycbor/examples/simplereader.c | 185 ++ deps/tinycbor/examples/simplereader.pro | 3 + deps/tinycbor/scripts/maketag.pl | 91 + deps/tinycbor/scripts/update-docs.sh | 52 + deps/tinycbor/src/cbor.dox | 123 ++ deps/tinycbor/src/cbor.h | 724 +++++++ deps/tinycbor/src/cborencoder.c | 689 ++++++ .../src/cborencoder_close_container_checked.c | 57 + deps/tinycbor/src/cborencoder_float.c | 42 + deps/tinycbor/src/cborerrorstrings.c | 188 ++ deps/tinycbor/src/cborinternal_p.h | 316 +++ deps/tinycbor/src/cborjson.h | 62 + deps/tinycbor/src/cborparser.c | 1529 +++++++++++++ deps/tinycbor/src/cborparser_dup_string.c | 126 ++ deps/tinycbor/src/cborparser_float.c | 54 + deps/tinycbor/src/cborpretty.c | 579 +++++ deps/tinycbor/src/cborpretty_stdio.c | 87 + deps/tinycbor/src/cbortojson.c | 708 ++++++ deps/tinycbor/src/cborvalidation.c | 657 ++++++ deps/tinycbor/src/compilersupport_p.h | 221 ++ deps/tinycbor/src/open_memstream.c | 114 + deps/tinycbor/src/parsetags.pl | 116 + deps/tinycbor/src/src.pri | 26 + deps/tinycbor/src/tags.txt | 23 + deps/tinycbor/src/tinycbor-version.h | 3 + deps/tinycbor/src/tinycbor.pro | 10 + deps/tinycbor/src/utf8_p.h | 104 + deps/tinycbor/tests/.gitignore | 17 + deps/tinycbor/tests/c90/c90.pro | 7 + deps/tinycbor/tests/c90/tst_c90.c | 30 + deps/tinycbor/tests/cpp/cpp.pro | 5 + deps/tinycbor/tests/cpp/tst_cpp.cpp | 44 + deps/tinycbor/tests/encoder/data.cpp | 346 +++ deps/tinycbor/tests/encoder/encoder.pro | 9 + deps/tinycbor/tests/encoder/tst_encoder.cpp | 609 ++++++ deps/tinycbor/tests/parser/data.cpp | 607 ++++++ deps/tinycbor/tests/parser/parser.pro | 10 + deps/tinycbor/tests/parser/tst_parser.cpp | 1905 +++++++++++++++++ deps/tinycbor/tests/tests.pro | 3 + deps/tinycbor/tests/tojson/tojson.pro | 8 + deps/tinycbor/tests/tojson/tst_tojson.cpp | 721 +++++++ deps/tinycbor/tinycbor.pc.in | 11 + deps/tinycbor/tools/cbordump/cbordump.c | 164 ++ deps/tinycbor/tools/cbordump/cbordump.pro | 10 + deps/tinycbor/tools/json2cbor/json2cbor.c | 492 +++++ deps/tinycbor/tools/json2cbor/json2cbor.pro | 20 + 65 files changed, 14709 insertions(+), 6 deletions(-) create mode 100644 deps/tinycbor-ledger/LICENSE create mode 100644 deps/tinycbor-ledger/README.md create mode 100644 deps/tinycbor-ledger/cborparser.c create mode 100644 deps/tinycbor-ledger/cborvalidation.c create mode 100644 deps/tinycbor/.appveyor.yml create mode 100644 deps/tinycbor/.gitattributes create mode 100644 deps/tinycbor/.gitignore create mode 100644 deps/tinycbor/.tag create mode 100644 deps/tinycbor/.travis.yml create mode 100644 deps/tinycbor/Doxyfile create mode 100644 deps/tinycbor/LICENSE create mode 100644 deps/tinycbor/Makefile create mode 100644 deps/tinycbor/README create mode 100644 deps/tinycbor/TODO create mode 100644 deps/tinycbor/VERSION create mode 100644 deps/tinycbor/examples/examples.pro create mode 100644 deps/tinycbor/examples/simplereader.c create mode 100644 deps/tinycbor/examples/simplereader.pro create mode 100644 deps/tinycbor/scripts/maketag.pl create mode 100755 deps/tinycbor/scripts/update-docs.sh create mode 100644 deps/tinycbor/src/cbor.dox create mode 100644 deps/tinycbor/src/cbor.h create mode 100644 deps/tinycbor/src/cborencoder.c create mode 100644 deps/tinycbor/src/cborencoder_close_container_checked.c create mode 100644 deps/tinycbor/src/cborencoder_float.c create mode 100644 deps/tinycbor/src/cborerrorstrings.c create mode 100644 deps/tinycbor/src/cborinternal_p.h create mode 100644 deps/tinycbor/src/cborjson.h create mode 100644 deps/tinycbor/src/cborparser.c create mode 100644 deps/tinycbor/src/cborparser_dup_string.c create mode 100644 deps/tinycbor/src/cborparser_float.c create mode 100644 deps/tinycbor/src/cborpretty.c create mode 100644 deps/tinycbor/src/cborpretty_stdio.c create mode 100644 deps/tinycbor/src/cbortojson.c create mode 100644 deps/tinycbor/src/cborvalidation.c create mode 100644 deps/tinycbor/src/compilersupport_p.h create mode 100644 deps/tinycbor/src/open_memstream.c create mode 100755 deps/tinycbor/src/parsetags.pl create mode 100644 deps/tinycbor/src/src.pri create mode 100644 deps/tinycbor/src/tags.txt create mode 100644 deps/tinycbor/src/tinycbor-version.h create mode 100644 deps/tinycbor/src/tinycbor.pro create mode 100644 deps/tinycbor/src/utf8_p.h create mode 100644 deps/tinycbor/tests/.gitignore create mode 100644 deps/tinycbor/tests/c90/c90.pro create mode 100644 deps/tinycbor/tests/c90/tst_c90.c create mode 100644 deps/tinycbor/tests/cpp/cpp.pro create mode 100644 deps/tinycbor/tests/cpp/tst_cpp.cpp create mode 100644 deps/tinycbor/tests/encoder/data.cpp create mode 100644 deps/tinycbor/tests/encoder/encoder.pro create mode 100644 deps/tinycbor/tests/encoder/tst_encoder.cpp create mode 100644 deps/tinycbor/tests/parser/data.cpp create mode 100644 deps/tinycbor/tests/parser/parser.pro create mode 100644 deps/tinycbor/tests/parser/tst_parser.cpp create mode 100644 deps/tinycbor/tests/tests.pro create mode 100644 deps/tinycbor/tests/tojson/tojson.pro create mode 100644 deps/tinycbor/tests/tojson/tst_tojson.cpp create mode 100644 deps/tinycbor/tinycbor.pc.in create mode 100644 deps/tinycbor/tools/cbordump/cbordump.c create mode 100644 deps/tinycbor/tools/cbordump/cbordump.pro create mode 100644 deps/tinycbor/tools/json2cbor/json2cbor.c create mode 100644 deps/tinycbor/tools/json2cbor/json2cbor.pro diff --git a/.gitignore b/.gitignore index 21c5a2d2..7e593d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ cmake-build-fuzz/ !\deps/nanox-secure-sdk !\deps/ledger-zxlib !\deps/tinycbor +!\deps/tinycbor-ledger !\deps/BLAKE app/src/glyphs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d6728109..bbf94a63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,7 +81,11 @@ string(APPEND CMAKE_LINKER_FLAGS " -fsanitize=address -fno-omit-frame-pointer") ############################################################## # static libs file(GLOB_RECURSE JSMN_SRC - deps/jsmn/src/jsmn.c + ${CMAKE_CURRENT_SOURCE_DIR}/deps/jsmn/src/jsmn.c + ) +file(GLOB_RECURSE TINYCBOR_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src/cborparser.c + ${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src/cborvalidation.c ) file(GLOB_RECURSE LIB_SRC @@ -104,14 +108,16 @@ file(GLOB_RECURSE LIB_SRC add_library(app_lib STATIC ${LIB_SRC} ${JSMN_SRC} + ${TINYCBOR_SRC} ) target_include_directories(app_lib PUBLIC - deps/ledger-zxlib/include - deps/jsmn/src - app/src - app/src/common - deps/ledger-zxlib/app/common + ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/include + ${CMAKE_CURRENT_SOURCE_DIR}/deps/jsmn/src + ${CMAKE_CURRENT_SOURCE_DIR}/app/src + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/common + ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/app/common + ${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src ) target_link_libraries(app_lib PUBLIC) @@ -129,6 +135,7 @@ target_include_directories(unittests PRIVATE ${CONAN_INCLUDE_DIRS_FMT} ${CONAN_INCLUDE_DIRS_JSONCPP} ${CMAKE_CURRENT_SOURCE_DIR}/deps/jsmn/src + ${CMAKE_CURRENT_SOURCE_DIR}/deps/tinycbor/src ) target_link_libraries(unittests PRIVATE diff --git a/app/Makefile b/app/Makefile index fa402882..cf7e27b9 100755 --- a/app/Makefile +++ b/app/Makefile @@ -195,6 +195,10 @@ APP_SOURCE_PATH += $(MY_DIR)/../deps/jsmn/src SDK_SOURCE_PATH += lib_stusb lib_stusb_impl SDK_SOURCE_PATH += lib_ux +# NanoX SDK does not allow += in APP_SOURCE_PATH or INCLUDES_PATH +CFLAGS += -I$(MY_DIR)/../deps/tinycbor/src +APP_SOURCE_PATH += $(MY_DIR)/../deps/tinycbor-ledger + .PHONY: rust rust: @echo "No rust code" diff --git a/deps/tinycbor-ledger/LICENSE b/deps/tinycbor-ledger/LICENSE new file mode 100644 index 00000000..4aad977c --- /dev/null +++ b/deps/tinycbor-ledger/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/tinycbor-ledger/README.md b/deps/tinycbor-ledger/README.md new file mode 100644 index 00000000..a1fffa85 --- /dev/null +++ b/deps/tinycbor-ledger/README.md @@ -0,0 +1,4 @@ +# Workaround +Ledger NanoX SDK does not allow += in APP_SOURCE_PATH or INCLUDES_PATH +For this reason, in order to compile only a couple of files, we need to copy them over to this directory +Please refer to ../tinycbor for the complete source code diff --git a/deps/tinycbor-ledger/cborparser.c b/deps/tinycbor-ledger/cborparser.c new file mode 100644 index 00000000..45ae23fb --- /dev/null +++ b/deps/tinycbor-ledger/cborparser.c @@ -0,0 +1,1529 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" + +#include + +/** + * \defgroup CborParsing Parsing CBOR streams + * \brief Group of functions used to parse CBOR streams. + * + * TinyCBOR provides functions for pull-based stream parsing of a CBOR-encoded + * payload. The main data type for the parsing is a CborValue, which behaves + * like an iterator and can be used to extract the encoded data. It is first + * initialized with a call to cbor_parser_init() and is usually used to extract + * exactly one item, most often an array or map. + * + * Nested CborValue objects can be parsed using cbor_value_enter_container(). + * Each call to cbor_value_enter_container() must be matched by a call to + * cbor_value_leave_container(), with the exact same parameters. + * + * The example below initializes a CborParser object, begins the parsing with a + * CborValue and decodes a single integer: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * cbor_parser_init(buffer, len, 0, &parser, &value); + * cbor_value_get_int(&value, &result); + * return result; + * } + * \endcode + * + * The code above does no error checking, which means it assumes the data comes + * from a source trusted to send one properly-encoded integer. The following + * example does the exact same operation, but includes error checking and + * returns 0 on parsing failure: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * if (cbor_parser_init(buffer, len, 0, &parser, &value) != CborNoError) + * return 0; + * if (!cbor_value_is_integer(&value) || + * cbor_value_get_int(&value, &result) != CborNoError) + * return 0; + * return result; + * } + * \endcode + * + * Note, in the example above, that one can't distinguish a parsing failure + * from an encoded value of zero. Reporting a parsing error is left as an + * exercise to the reader. + * + * The code above does not execute a range-check either: it is possible that + * the value decoded from the CBOR stream encodes a number larger than what can + * be represented in a variable of type \c{int}. If detecting that case is + * important, the code should call cbor_value_get_int_checked() instead. + * + *

Memory and parsing constraints

+ * + * TinyCBOR is designed to run with little memory and with minimal overhead. + * Except where otherwise noted, the parser functions always run on constant + * time (O(1)), do not recurse and never allocate memory (thus, stack usage is + * bounded and is O(1)). + * + *

Error handling and preconditions

+ * + * All functions operating on a CborValue return a CborError condition, with + * CborNoError standing for the normal situation in which no parsing error + * occurred. All functions may return parsing errors in case the stream cannot + * be decoded properly, be it due to corrupted data or due to reaching the end + * of the input buffer. + * + * Error conditions must not be ignored. All decoder functions have undefined + * behavior if called after an error has been reported, and may crash. + * + * Some functions are also documented to have preconditions, like + * cbor_value_get_int() requiring that the input be an integral value. + * Violation of preconditions also results in undefined behavior and the + * program may crash. + */ + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \struct CborValue + * + * This type contains one value parsed from the CBOR stream. Each CborValue + * behaves as an iterator in a StAX-style parser. + * + * \if privatedocs + * Implementation details: the CborValue contains these fields: + * \list + * \li ptr: pointer to the actual data + * \li flags: flags from the decoder + * \li extra: partially decoded integer value (0, 1 or 2 bytes) + * \li remaining: remaining items in this collection after this item or UINT32_MAX if length is unknown + * \endlist + * \endif + */ + +static uint64_t extract_number_and_advance(CborValue *it) +{ + /* This function is only called after we've verified that the number + * here is valid, so we can just use _cbor_value_extract_int64_helper. */ + uint8_t descriptor; + uint64_t v = _cbor_value_extract_int64_helper(it); + + read_bytes_unchecked(it, &descriptor, 0, 1); + descriptor &= SmallValueMask; + + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); + advance_bytes(it, bytesNeeded + 1); + + return v; +} + +static bool is_fixed_type(uint8_t type) +{ + return type != CborTextStringType && type != CborByteStringType && type != CborArrayType && + type != CborMapType; +} + +static CborError preparse_value(CborValue *it) +{ + enum { + /* flags to keep */ + FlagsToKeep = CborIteratorFlag_ContainerIsMap | CborIteratorFlag_NextIsMapKey + }; + uint8_t descriptor; + + /* are we at the end? */ + it->type = CborInvalidType; + it->flags &= FlagsToKeep; + if (!read_bytes(it, &descriptor, 0, 1)) + return CborErrorUnexpectedEOF; + + uint8_t type = descriptor & MajorTypeMask; + it->type = type; + it->extra = (descriptor &= SmallValueMask); + + if (descriptor > Value64Bit) { + if (unlikely(descriptor != IndefiniteLength)) + return type == CborSimpleType ? CborErrorUnknownType : CborErrorIllegalNumber; + if (likely(!is_fixed_type(type))) { + /* special case */ + it->flags |= CborIteratorFlag_UnknownLength; + it->type = type; + return CborNoError; + } + return type == CborSimpleType ? CborErrorUnexpectedBreak : CborErrorIllegalNumber; + } + + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); + + if (bytesNeeded) { + if (!can_read_bytes(it, bytesNeeded + 1)) + return CborErrorUnexpectedEOF; + + it->extra = 0; + + /* read up to 16 bits into it->extra */ + if (bytesNeeded == 1) { + uint8_t extra; + read_bytes_unchecked(it, &extra, 1, bytesNeeded); + it->extra = extra; + } else if (bytesNeeded == 2) { + read_bytes_unchecked(it, &it->extra, 1, bytesNeeded); + it->extra = cbor_ntohs(it->extra); + } else { + cbor_static_assert(CborIteratorFlag_IntegerValueTooLarge == (Value32Bit & 3)); + cbor_static_assert((CborIteratorFlag_IntegerValueIs64Bit | + CborIteratorFlag_IntegerValueTooLarge) == (Value64Bit & 3)); + it->flags |= (descriptor & 3); + } + } + + uint8_t majortype = type >> MajorTypeShift; + if (majortype == NegativeIntegerType) { + it->flags |= CborIteratorFlag_NegativeInteger; + it->type = CborIntegerType; + } else if (majortype == SimpleTypesType) { + switch (descriptor) { + case FalseValue: + it->extra = false; + it->type = CborBooleanType; + break; + + case SinglePrecisionFloat: + case DoublePrecisionFloat: + it->flags |= CborIteratorFlag_IntegerValueTooLarge; + /* fall through */ + case TrueValue: + case NullValue: + case UndefinedValue: + case HalfPrecisionFloat: + read_bytes_unchecked(it, &it->type, 0, 1); + break; + + case SimpleTypeInNextByte: +#ifndef CBOR_PARSER_NO_STRICT_CHECKS + if (unlikely(it->extra < 32)) { + it->type = CborInvalidType; + return CborErrorIllegalSimpleType; + } +#endif + break; + + case 28: + case 29: + case 30: + case Break: + cbor_assert(false); /* these conditions can't be reached */ + return CborErrorUnexpectedBreak; + } + } + + return CborNoError; +} + +static CborError preparse_next_value_nodecrement(CborValue *it) +{ + uint8_t byte; + if (it->remaining == UINT32_MAX && read_bytes(it, &byte, 0, 1) && byte == (uint8_t)BreakByte) { + /* end of map or array */ + if ((it->flags & CborIteratorFlag_ContainerIsMap && it->flags & CborIteratorFlag_NextIsMapKey) + || it->type == CborTagType) { + /* but we weren't expecting it! */ + return CborErrorUnexpectedBreak; + } + it->type = CborInvalidType; + it->remaining = 0; + it->flags |= CborIteratorFlag_UnknownLength; /* leave_container must consume the Break */ + return CborNoError; + } + + return preparse_value(it); +} + +static CborError preparse_next_value(CborValue *it) +{ + /* tags don't count towards item totals or whether we've successfully + * read a map's key or value */ + bool itemCounts = it->type != CborTagType; + + if (it->remaining != UINT32_MAX) { + if (itemCounts && --it->remaining == 0) { + it->type = CborInvalidType; + it->flags &= ~CborIteratorFlag_UnknownLength; /* no Break to consume */ + return CborNoError; + } + } + if (itemCounts) { + /* toggle the flag indicating whether this was a map key */ + it->flags ^= CborIteratorFlag_NextIsMapKey; + } + return preparse_next_value_nodecrement(it); +} + +static CborError advance_internal(CborValue *it) +{ + uint64_t length = extract_number_and_advance(it); + + if (it->type == CborByteStringType || it->type == CborTextStringType) { + cbor_assert(length == (size_t)length); + cbor_assert((it->flags & CborIteratorFlag_UnknownLength) == 0); + advance_bytes(it, length); + } + + return preparse_next_value(it); +} + +/** \internal + * + * Decodes the CBOR integer value when it is larger than the 16 bits available + * in value->extra. This function requires that value->flags have the + * CborIteratorFlag_IntegerValueTooLarge flag set. + * + * This function is also used to extract single- and double-precision floating + * point values (SinglePrecisionFloat == Value32Bit and DoublePrecisionFloat == + * Value64Bit). + */ +uint64_t _cbor_value_decode_int64_internal(const CborValue *value) +{ + cbor_assert(value->flags & CborIteratorFlag_IntegerValueTooLarge || + value->type == CborFloatType || value->type == CborDoubleType); + if (value->flags & CborIteratorFlag_IntegerValueIs64Bit) + return read_uint64(value, 1); + + return read_uint32(value, 1); +} + +/** + * Initializes the CBOR parser for parsing \a size bytes beginning at \a + * buffer. Parsing will use flags set in \a flags. The iterator to the first + * element is returned in \a it. + * + * The \a parser structure needs to remain valid throughout the decoding + * process. It is not thread-safe to share one CborParser among multiple + * threads iterating at the same time, but the object can be copied so multiple + * threads can iterate. + */ +CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it) +{ + memset(parser, 0, sizeof(*parser)); + parser->source.end = buffer + size; + parser->flags = (enum CborParserGlobalFlags)flags; + it->parser = parser; + it->source.ptr = buffer; + it->remaining = 1; /* there's one type altogether, usually an array or map */ + it->flags = 0; + return preparse_value(it); +} + +CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *token) +{ + memset(parser, 0, sizeof(*parser)); + parser->source.ops = ops; + parser->flags = CborParserFlag_ExternalSource; + it->parser = parser; + it->source.token = token; + it->remaining = 1; + return preparse_value(it); +} + +/** + * \fn bool cbor_value_at_end(const CborValue *it) + * + * Returns true if \a it has reached the end of the iteration, usually when + * advancing after the last item in an array or map. + * + * In the case of the outermost CborValue object, this function returns true + * after decoding a single element. A pointer to the first byte of the + * remaining data (if any) can be obtained with cbor_value_get_next_byte(). + * + * \sa cbor_value_advance(), cbor_value_is_valid(), cbor_value_get_next_byte() + */ + +/** + * \fn const uint8_t *cbor_value_get_next_byte(const CborValue *it) + * + * Returns a pointer to the next byte that would be decoded if this CborValue + * object were advanced. + * + * This function is useful if cbor_value_at_end() returns true for the + * outermost CborValue: the pointer returned is the first byte of the data + * remaining in the buffer, if any. Code can decide whether to begin decoding a + * new CBOR data stream from this point, or parse some other data appended to + * the same buffer. + * + * This function may be used even after a parsing error. If that occurred, + * then this function returns a pointer to where the parsing error occurred. + * Note that the error recovery is not precise and the pointer may not indicate + * the exact byte containing bad data. + * + * This function makes sense only when using a linear buffer (that is, when the + * parser is initialize by cbor_parser_init()). If using an external source, + * this function may return garbage; instead, consult the external source itself + * to find out more details about the presence of more data. + * + * \sa cbor_value_at_end() + */ + +CborError cbor_value_reparse(CborValue *it) +{ + if (it->flags & CborIteratorFlag_IteratingStringChunks) + return CborNoError; + return preparse_next_value_nodecrement(it); +} + +/** + * \fn bool cbor_value_is_valid(const CborValue *it) + * + * Returns true if the iterator \a it contains a valid value. Invalid iterators + * happen when iteration reaches the end of a container (see \ref + * cbor_value_at_end()) or when a search function resulted in no matches. + * + * \sa cbor_value_advance(), cbor_value_at_end(), cbor_value_get_type() + */ + +/** + * Performs a basic validation of the CBOR stream pointed by \a it and returns + * the error it found. If no error was found, it returns CborNoError and the + * application can iterate over the items with certainty that no other errors + * will appear during parsing. + * + * A basic validation checks for: + * \list + * \li absence of undefined additional information bytes; + * \li well-formedness of all numbers, lengths, and simple values; + * \li string contents match reported sizes; + * \li arrays and maps contain the number of elements they are reported to have; + * \endlist + * + * For further checks, see cbor_value_validate(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance(). + * + * \sa cbor_value_validate(), cbor_value_advance() + */ +CborError cbor_value_validate_basic(const CborValue *it) +{ + CborValue value = *it; + return cbor_value_advance(&value); +} + +/** + * Advances the CBOR value \a it by one fixed-size position. Fixed-size types + * are: integers, tags, simple types (including boolean, null and undefined + * values) and floating point types. + * + * If the type is not of fixed size, this function has undefined behavior. Code + * must be sure that the current type is one of the fixed-size types before + * calling this function. This function is provided because it can guarantee + * that it runs in constant time (O(1)). + * + * If the caller is not able to determine whether the type is fixed or not, code + * can use the cbor_value_advance() function instead. + * + * \sa cbor_value_at_end(), cbor_value_advance(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance_fixed(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + cbor_assert(is_fixed_type(it->type)); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_internal(it); +} + +static CborError advance_recursive(CborValue *it, int nestingLevel) +{ + CborError err; + CborValue recursed; + + if (is_fixed_type(it->type)) + return advance_internal(it); + + if (!cbor_value_is_container(it)) { + size_t len = SIZE_MAX; + return _cbor_value_copy_string(it, NULL, &len, it); + } + + /* map or array */ + if (nestingLevel == 0) + return CborErrorNestingTooDeep; + + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; + while (!cbor_value_at_end(&recursed)) { + err = advance_recursive(&recursed, nestingLevel - 1); + if (err) + return err; + } + return cbor_value_leave_container(it, &recursed); +} + + +/** + * Advances the CBOR value \a it by one element, skipping over containers. + * Unlike cbor_value_advance_fixed(), this function can be called on a CBOR + * value of any type. However, if the type is a container (map or array) or a + * string with a chunked payload, this function will not run in constant time + * and will recurse into itself (it will run on O(n) time for the number of + * elements or chunks and will use O(n) memory for the number of nested + * containers). + * + * The number of recursions can be limited at compile time to avoid stack + * exhaustion in constrained systems. + * + * \sa cbor_value_at_end(), cbor_value_advance_fixed(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_recursive(it, CBOR_PARSER_MAX_RECURSIONS); +} + +/** + * \fn bool cbor_value_is_tag(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR tag. + * + * \sa cbor_value_get_tag(), cbor_value_skip_tag() + */ + +/** + * \fn CborError cbor_value_get_tag(const CborValue *value, CborTag *result) + * + * Retrieves the CBOR tag value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a CBOR tag value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_tag is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_tag() + */ + +/** + * Advances the CBOR value \a it until it no longer points to a tag. If \a it is + * already not pointing to a tag, then this function returns it unchanged. + * + * This function does not run in constant time: it will run on O(n) for n being + * the number of tags. It does use constant memory (O(1) memory requirements). + * + * \sa cbor_value_advance_fixed(), cbor_value_advance() + */ +CborError cbor_value_skip_tag(CborValue *it) +{ + while (cbor_value_is_tag(it)) { + CborError err = cbor_value_advance_fixed(it); + if (err) + return err; + } + return CborNoError; +} + +/** + * \fn bool cbor_value_is_container(const CborValue *it) + * + * Returns true if the \a it value is a container and requires recursion in + * order to decode (maps and arrays), false otherwise. + */ + +/** + * Creates a CborValue iterator pointing to the first element of the container + * represented by \a it and saves it in \a recursed. The \a it container object + * needs to be kept and passed again to cbor_value_leave_container() in order + * to continue iterating past this container. + * + * The \a it CborValue iterator must point to a container. + * + * \sa cbor_value_is_container(), cbor_value_leave_container(), cbor_value_advance() + */ +CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) +{ + cbor_static_assert(CborIteratorFlag_ContainerIsMap == (CborMapType & ~CborArrayType)); + cbor_assert(cbor_value_is_container(it)); + *recursed = *it; + + if (it->flags & CborIteratorFlag_UnknownLength) { + recursed->remaining = UINT32_MAX; + advance_bytes(recursed, 1); + } else { + uint64_t len = extract_number_and_advance(recursed); + + recursed->remaining = (uint32_t)len; + if (recursed->remaining != len || len == UINT32_MAX) { + /* back track the pointer to indicate where the error occurred */ + copy_current_position(recursed, it); + return CborErrorDataTooLarge; + } + if (recursed->type == CborMapType) { + /* maps have keys and values, so we need to multiply by 2 */ + if (recursed->remaining > UINT32_MAX / 2) { + /* back track the pointer to indicate where the error occurred */ + copy_current_position(recursed, it); + return CborErrorDataTooLarge; + } + recursed->remaining *= 2; + } + if (len == 0) { + /* the case of the empty container */ + recursed->type = CborInvalidType; + return CborNoError; + } + } + recursed->flags = (recursed->type & CborIteratorFlag_ContainerIsMap); + return preparse_next_value_nodecrement(recursed); +} + +/** + * Updates \a it to point to the next element after the container. The \a + * recursed object needs to point to the element obtained either by advancing + * the last element of the container (via cbor_value_advance(), + * cbor_value_advance_fixed(), a nested cbor_value_leave_container(), or the \c + * next pointer from cbor_value_copy_string() or cbor_value_dup_string()). + * + * The \a it and \a recursed parameters must be the exact same as passed to + * cbor_value_enter_container(). + * + * \sa cbor_value_enter_container(), cbor_value_at_end() + */ +CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed) +{ + cbor_assert(cbor_value_is_container(it)); + cbor_assert(recursed->type == CborInvalidType); + + copy_current_position(it, recursed); + if (recursed->flags & CborIteratorFlag_UnknownLength) + advance_bytes(it, 1); + return preparse_next_value(it); +} + + +/** + * \fn CborType cbor_value_get_type(const CborValue *value) + * + * Returns the type of the CBOR value that the iterator \a value points to. If + * \a value does not point to a valid value, this function returns \ref + * CborInvalidType. + * + * TinyCBOR also provides functions to test directly if a given CborValue object + * is of a given type, like cbor_value_is_text_string() and cbor_value_is_null(). + * + * \sa cbor_value_is_valid() + */ + +/** + * \fn bool cbor_value_is_null(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR null type. + * + * \sa cbor_value_is_valid(), cbor_value_is_undefined() + */ + +/** + * \fn bool cbor_value_is_undefined(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR undefined type. + * + * \sa cbor_value_is_valid(), cbor_value_is_null() + */ + +/** + * \fn bool cbor_value_is_boolean(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR boolean + * type (true or false). + * + * \sa cbor_value_is_valid(), cbor_value_get_boolean() + */ + +/** + * \fn CborError cbor_value_get_boolean(const CborValue *value, bool *result) + * + * Retrieves the boolean value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a boolean value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_boolean is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_boolean() + */ + +/** + * \fn bool cbor_value_is_simple_type(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR Simple Type + * type (other than true, false, null and undefined). + * + * \sa cbor_value_is_valid(), cbor_value_get_simple_type() + */ + +/** + * \fn CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) + * + * Retrieves the CBOR Simple Type value that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a simple_type + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_simple_type is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_simple_type() + */ + +/** + * \fn bool cbor_value_is_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR integer + * type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_uint64, cbor_value_get_raw_integer + */ + +/** + * \fn bool cbor_value_is_unsigned_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR unsigned + * integer type (positive values or zero). + * + * \sa cbor_value_is_valid(), cbor_value_get_uint64() + */ + +/** + * \fn bool cbor_value_is_negative_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR negative + * integer type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_raw_integer + */ + +/** + * \fn CborError cbor_value_get_int(const CborValue *value, int *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int} are silently truncated to fit. Use + * cbor_value_get_int_checked() if that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_int64(const CborValue *value, int64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int64_t} are silently truncated to fit. Use + * cbor_value_get_int64_checked() that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an unsigned integer + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_unsigned_integer is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_unsigned_integer() + */ + +/** + * \fn CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * This function is provided because CBOR negative integers can assume values + * that cannot be represented with normal 64-bit integer variables. + * + * If the integer is unsigned (that is, if cbor_value_is_unsigned_integer() + * returns true), then \a result will contain the actual value. If the integer + * is negative, then \a result will contain the absolute value of that integer, + * minus one. That is, \c {actual = -result - 1}. On architectures using two's + * complement for representation of negative integers, it is equivalent to say + * that \a result will contain the bitwise negation of the actual value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike \ref cbor_value_get_int64(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use either + * cbor_value_get_uint64() (if the number is positive) or + * cbor_value_get_raw_integer(). + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64() + */ +CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result) +{ + uint64_t v; + cbor_assert(cbor_value_is_integer(value)); + v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * The range for int64_t is -2^63 to 2^63-1 (int64_t is required to be + * two's complement, C11 7.20.1.1 paragraph 3), which in CBOR is + * represented the same way, differing only on the "sign bit" (the major + * type). + */ + + if (unlikely(v > (uint64_t)INT64_MAX)) + return CborErrorDataTooLarge; + + *result = v; + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike \ref cbor_value_get_int(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use one of the other integer + * functions to obtain the value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64(), + * cbor_value_get_uint64(), cbor_value_get_int64_checked(), cbor_value_get_raw_integer() + */ +CborError cbor_value_get_int_checked(const CborValue *value, int *result) +{ + uint64_t v; + cbor_assert(cbor_value_is_integer(value)); + v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * But we can convert from signed to unsigned without fault (paragraph 2). + * + * The range for int is implementation-defined and int is not guaranteed to use + * two's complement representation (although int32_t is). + */ + + if (value->flags & CborIteratorFlag_NegativeInteger) { + if (unlikely(v > (unsigned) -(INT_MIN + 1))) + return CborErrorDataTooLarge; + + *result = (int)v; + *result = -*result - 1; + } else { + if (unlikely(v > (uint64_t)INT_MAX)) + return CborErrorDataTooLarge; + + *result = (int)v; + } + return CborNoError; + +} + +/** + * \fn bool cbor_value_is_length_known(const CborValue *value) + * + * Returns true if the length of this type is known without calculation. That + * is, if the length of this CBOR string, map or array is encoded in the data + * stream, this function returns true. If the length is not encoded, it returns + * false. + * + * If the length is known, code can call cbor_value_get_string_length(), + * cbor_value_get_array_length() or cbor_value_get_map_length() to obtain the + * length. If the length is not known but is necessary, code can use the + * cbor_value_calculate_string_length() function (no equivalent function is + * provided for maps and arrays). + */ + +/** + * \fn bool cbor_value_is_text_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR text + * string. CBOR text strings are UTF-8 encoded and usually contain + * human-readable text. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_text_string(), cbor_value_dup_text_string() + */ + +/** + * \fn bool cbor_value_is_byte_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR byte + * string. CBOR byte strings are binary data with no specified encoding or + * format. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_byte_string(), cbor_value_dup_byte_string() + */ + +/** + * \fn CborError cbor_value_get_string_length(const CborValue *value, size_t *length) + * + * Extracts the length of the byte or text string that \a value points to and + * stores it in \a result. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * If the length of this string is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * If the length of the string is required but the length was not encoded, use + * cbor_value_calculate_string_length(), but note that that function does not + * run in constant time. + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known(), cbor_value_calculate_string_length() + */ + +/** + * Calculates the length of the byte or text string that \a value points to and + * stores it in \a len. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * This function is different from cbor_value_get_string_length() in that it + * calculates the length even for strings sent in chunks. For that reason, this + * function may not run in constant time (it will run in O(n) time on the + * number of chunks). It does use constant memory (O(1)). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_get_string_length(), cbor_value_copy_text_string(), cbor_value_copy_byte_string(), cbor_value_is_length_known() + */ +CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len) +{ + *len = SIZE_MAX; + return _cbor_value_copy_string(value, NULL, len, NULL); +} + +CborError _cbor_value_begin_string_iteration(CborValue *it) +{ + it->flags |= CborIteratorFlag_IteratingStringChunks | + CborIteratorFlag_BeforeFirstStringChunk; + if (!cbor_value_is_length_known(it)) { + /* chunked string: we're before the first chunk; + * advance to the first chunk */ + advance_bytes(it, 1); + } + + return CborNoError; +} + +CborError _cbor_value_finish_string_iteration(CborValue *it) +{ + if (!cbor_value_is_length_known(it)) + advance_bytes(it, 1); /* skip the Break */ + + return preparse_next_value(it); +} + +static CborError get_string_chunk_size(const CborValue *it, size_t *offset, size_t *len) +{ + uint8_t descriptor; + size_t bytesNeeded = 1; + + if (cbor_value_is_length_known(it) && (it->flags & CborIteratorFlag_BeforeFirstStringChunk) == 0) + return CborErrorNoMoreStringChunks; + + /* are we at the end? */ + if (!read_bytes(it, &descriptor, 0, 1)) + return CborErrorUnexpectedEOF; + + if (descriptor == BreakByte) + return CborErrorNoMoreStringChunks; + if ((descriptor & MajorTypeMask) != it->type) + return CborErrorIllegalType; + + /* find the string length */ + descriptor &= SmallValueMask; + if (descriptor < Value8Bit) { + *len = descriptor; + } else if (unlikely(descriptor > Value64Bit)) { + return CborErrorIllegalNumber; + } else { + uint64_t val; + bytesNeeded = (size_t)(1 << (descriptor - Value8Bit)); + if (!can_read_bytes(it, 1 + bytesNeeded)) + return CborErrorUnexpectedEOF; + + if (descriptor <= Value16Bit) { + if (descriptor == Value16Bit) + val = read_uint16(it, 1); + else + val = read_uint8(it, 1); + } else { + if (descriptor == Value32Bit) + val = read_uint32(it, 1); + else + val = read_uint64(it, 1); + } + + *len = val; + if (*len != val) + return CborErrorDataTooLarge; + + ++bytesNeeded; + } + + *offset = bytesNeeded; + return CborNoError; +} + +CborError _cbor_value_get_string_chunk_size(const CborValue *value, size_t *len) +{ + size_t offset; + return get_string_chunk_size(value, &offset, len); +} + +static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) +{ + size_t offset; + CborError err = get_string_chunk_size(it, &offset, len); + if (err) + return err; + + /* we're good, transfer the string now */ + err = transfer_string(it, bufferptr, offset, *len); + if (err) + return err; + + /* we've iterated at least once */ + it->flags &= ~CborIteratorFlag_BeforeFirstStringChunk; + return CborNoError; +} + +/** + * \fn CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one text string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_text_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_text_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_text_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_caculate_string_length(), cbor_value_get_byte_string_chunk() + */ + +/** + * \fn CborError cbor_value_get_byte_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one byte string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_byte_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_byte_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_byte_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \sa cbor_value_dup_byte_string(), cbor_value_copy_byte_string(), cbor_value_caculate_string_length(), cbor_value_get_text_string_chunk() + */ + +CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next) +{ + CborValue tmp; + if (!next) + next = &tmp; + *next = *value; + return get_string_chunk(next, bufferptr, len); +} + +/* We return uintptr_t so that we can pass memcpy directly as the iteration + * function. The choice is to optimize for memcpy, which is used in the base + * parser API (cbor_value_copy_string), while memcmp is used in convenience API + * only. */ +typedef uintptr_t (*IterateFunction)(char *, const uint8_t *, size_t); + +static uintptr_t iterate_noop(char *dest, const uint8_t *src, size_t len) +{ + (void)dest; + (void)src; + (void)len; + return true; +} + +static uintptr_t iterate_memcmp(char *s1, const uint8_t *s2, size_t len) +{ + return memcmp(s1, (const char *)s2, len) == 0; +} + +static uintptr_t iterate_memcpy(char *dest, const uint8_t *src, size_t len) +{ + return (uintptr_t)memcpy(dest, src, len); +} + +static CborError iterate_string_chunks(const CborValue *value, char *buffer, size_t *buflen, + bool *result, CborValue *next, IterateFunction func) +{ + CborError err; + CborValue tmp; + size_t total = 0; + const void *ptr; + + cbor_assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); + if (!next) + next = &tmp; + *next = *value; + *result = true; + + err = _cbor_value_begin_string_iteration(next); + if (err) + return err; + + while (1) { + size_t newTotal; + size_t chunkLen; + err = get_string_chunk(next, &ptr, &chunkLen); + if (err == CborErrorNoMoreStringChunks) + break; + if (err) + return err; + + if (unlikely(add_check_overflow(total, chunkLen, &newTotal))) + return CborErrorDataTooLarge; + + if (*result && *buflen >= newTotal) + *result = !!func(buffer == NULL ? buffer : buffer + total, (const uint8_t *)ptr, chunkLen); + else + *result = false; + + total = newTotal; + } + + /* is there enough room for the ending NUL byte? */ + if (*result && *buflen > total) { + uint8_t nul[] = { 0 }; + *result = !!func(buffer == NULL ? buffer : buffer + total, nul, 1); + } + *buflen = total; + return _cbor_value_finish_string_iteration(next); +} + +/** + * \fn CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed to by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of text strings. That byte is + * not included in the returned value of \c{*buflen}. If there was no space for + * the terminating null, no error is returned, so callers must check the value + * of *buflen after the call, before relying on the '\0'; if it has not been + * changed by the call, there is no '\0'-termination on the buffer's contents. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_get_text_string_chunk() cbor_value_dup_text_string(), cbor_value_copy_byte_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +/** + * \fn CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of null-terminated strings. + * That byte is not included in the returned value of \c{*buflen}. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_get_byte_string_chunk(), cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +CborError _cbor_value_copy_string(const CborValue *value, void *buffer, + size_t *buflen, CborValue *next) +{ + bool copied_all; + CborError err = iterate_string_chunks(value, (char*)buffer, buflen, &copied_all, next, + buffer ? iterate_memcpy : iterate_noop); + return err ? err : + copied_all ? CborNoError : CborErrorOutOfMemory; +} + +/** + * Compares the entry \a value with the string \a string and stores the result + * in \a result. If the value is different from \a string \a result will + * contain \c false. + * + * The entry at \a value may be a tagged string. If \a value is not a string or + * a tagged string, the comparison result will be false. + * + * CBOR requires text strings to be encoded in UTF-8, but this function does + * not validate either the strings in the stream or the string \a string to be + * matched. Moreover, comparison is done on strict codepoint comparison, + * without any Unicode normalization. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_skip_tag(), cbor_value_copy_text_string() + */ +CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result) +{ + size_t len; + CborValue copy = *value; + CborError err = cbor_value_skip_tag(©); + if (err) + return err; + if (!cbor_value_is_text_string(©)) { + *result = false; + return CborNoError; + } + + len = strlen(string); + return iterate_string_chunks(©, CONST_CAST(char *, string), &len, result, NULL, iterate_memcmp); +} + +/** + * \fn bool cbor_value_is_array(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR array. + * + * \sa cbor_value_is_valid(), cbor_value_is_map() + */ + +/** + * \fn CborError cbor_value_get_array_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR array that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a CBOR array, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_array is recommended. + * + * If the length of this array is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * \fn bool cbor_value_is_map(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR map. + * + * \sa cbor_value_is_valid(), cbor_value_is_array() + */ + +/** + * \fn CborError cbor_value_get_map_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR map that \a value points to and stores it in + * \a result. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the length of this map is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * Attempts to find the value in map \a map that corresponds to the text string + * entry \a string. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the item is found, it is stored in \a result. If no item is found + * matching the key, then \a result will contain an element of type \ref + * CborInvalidType. Matching is performed using + * cbor_value_text_string_equals(), so tagged strings will also match. + * + * This function has a time complexity of O(n) where n is the number of + * elements in the map to be searched. In addition, this function is has O(n) + * memory requirement based on the number of nested containers (maps or arrays) + * found as elements of this map. + * + * \sa cbor_value_is_valid(), cbor_value_text_string_equals(), cbor_value_advance() + */ +CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element) +{ + CborError err; + size_t len = strlen(string); + cbor_assert(cbor_value_is_map(map)); + err = cbor_value_enter_container(map, element); + if (err) + goto error; + + while (!cbor_value_at_end(element)) { + /* find the non-tag so we can compare */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + if (cbor_value_is_text_string(element)) { + bool equals; + size_t dummyLen = len; + err = iterate_string_chunks(element, CONST_CAST(char *, string), &dummyLen, + &equals, element, iterate_memcmp); + if (err) + goto error; + if (equals) + return preparse_value(element); + } else { + /* skip this key */ + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* skip this value */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* not found */ + element->type = CborInvalidType; + return CborNoError; + +error: + element->type = CborInvalidType; + return err; +} + +/** + * \fn bool cbor_value_is_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (32-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_float(const CborValue *value, float *result) + * + * Retrieves the CBOR single-precision floating point (32-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a single-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_float is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_float(), cbor_value_get_double() + */ + +/** + * \fn bool cbor_value_is_double(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * double-precision floating point (64-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_float(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_double(const CborValue *value, float *result) + * + * Retrieves the CBOR double-precision floating point (64-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a double-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_double is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_double(), cbor_value_get_float() + */ + +/** + * \fn bool cbor_value_is_half_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (16-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_float() + */ + +/** + * \fn CborError cbor_value_get_half_float(const CborValue *value, void *result) + * + * Retrieves the CBOR half-precision floating point (16-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a half-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_half_float is recommended. + * + * Note: since the C language does not have a standard type for half-precision + * floating point, this function takes a \c{void *} as a parameter for the + * storage area, which must be at least 16 bits wide. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() + */ + +/** @} */ diff --git a/deps/tinycbor-ledger/cborvalidation.c b/deps/tinycbor-ledger/cborvalidation.c new file mode 100644 index 00000000..f76d25c6 --- /dev/null +++ b/deps/tinycbor-ledger/cborvalidation.c @@ -0,0 +1,657 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" +#include "utf8_p.h" + +#include + +#ifndef CBOR_NO_FLOATING_POINT +# include +# include +#endif + + +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \enum CborValidationFlags + * The CborValidationFlags enum contains flags that control the validation of a + * CBOR stream. + * + * \value CborValidateBasic Validates only the syntactic correctedness of the stream. + * \value CborValidateCanonical Validates that the stream is in canonical format, according to + * RFC 7049 section 3.9. + * \value CborValidateStrictMode Performs strict validation, according to RFC 7049 section 3.10. + * \value CborValidateStrictest Attempt to perform the strictest validation we know of. + * + * \value CborValidateShortestIntegrals (Canonical) Validate that integral numbers and lengths are + * enconded in their shortest form possible. + * \value CborValidateShortestFloatingPoint (Canonical) Validate that floating-point numbers are encoded + * in their shortest form possible. + * \value CborValidateShortestNumbers (Canonical) Validate both integral and floating-point numbers + * are in their shortest form possible. + * \value CborValidateNoIndeterminateLength (Canonical) Validate that no string, array or map uses + * indeterminate length encoding. + * \value CborValidateMapIsSorted (Canonical & Strict mode) Validate that map keys appear in + * sorted order. + * \value CborValidateMapKeysAreUnique (Strict mode) Validate that map keys are unique. + * \value CborValidateTagUse (Strict mode) Validate that known tags are used with the + * correct types. This does not validate that the content of + * those types is syntactically correct. For example, this + * option validates that tag 1 (DateTimeString) is used with + * a Text String, but it does not validate that the string is + * a valid date/time representation. + * \value CborValidateUtf8 (Strict mode) Validate that text strings are appropriately + * encoded in UTF-8. + * \value CborValidateMapKeysAreString Validate that all map keys are text strings. + * \value CborValidateNoUndefined Validate that no elements of type "undefined" are present. + * \value CborValidateNoTags Validate that no tags are used. + * \value CborValidateFiniteFloatingPoint Validate that all floating point numbers are finite (no NaN or + * infinities are allowed). + * \value CborValidateCompleteData Validate that the stream is complete and there is no more data + * in the buffer. + * \value CborValidateNoUnknownSimpleTypesSA Validate that all Standards Action simple types are registered + * with IANA. + * \value CborValidateNoUnknownSimpleTypes Validate that all simple types used are registered with IANA. + * \value CborValidateNoUnknownTagsSA Validate that all Standard Actions tags are registered with IANA. + * \value CborValidateNoUnknownTagsSR Validate that all Standard Actions and Specification Required tags + * are registered with IANA (see below for limitations). + * \value CborValidateNoUnkonwnTags Validate that all tags are registered with IANA + * (see below for limitations). + * + * \par Simple type registry + * The CBOR specification requires that registration for use of the first 19 + * simple types must be done by way of Standards Action. The rest of the simple + * types only require a specification. The official list can be obtained from + * https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml. + * + * \par + * There are no registered simple types recognized by this release of TinyCBOR + * (beyond those defined by RFC 7049). + * + * \par Tag registry + * The CBOR specification requires that registration for use of the first 23 + * tags must be done by way of Standards Action. The next up to tag 255 only + * require a specification. Finally, all other tags can be registered on a + * first-come-first-serve basis. The official list can be ontained from + * https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml. + * + * \par + * Given the variability of this list, TinyCBOR cannot recognize all tags + * registered with IANA. Instead, the implementation only recognizes tags + * that are backed by an RFC. + * + * \par + * These are the tags known to the current TinyCBOR release: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TagData ItemSemantics
0UTF-8 text stringStandard date/time string
1integerEpoch-based date/time
2byte stringPositive bignum
3byte stringNegative bignum
4arrayDecimal fraction
5arrayBigfloat
16arrayCOSE Single Recipient Encrypted Data Object (RFC 8152)
17arrayCOSE Mac w/o Recipients Object (RFC 8152)
18arrayCOSE Single Signer Data Object (RFC 8162)
21byte string, array, mapExpected conversion to base64url encoding
22byte string, array, mapExpected conversion to base64 encoding
23byte string, array, mapExpected conversion to base16 encoding
24byte stringEncoded CBOR data item
32UTF-8 text stringURI
33UTF-8 text stringbase64url
34UTF-8 text stringbase64
35UTF-8 text stringRegular expression
36UTF-8 text stringMIME message
96arrayCOSE Encrypted Data Object (RFC 8152)
97arrayCOSE MACed Data Object (RFC 8152)
98arrayCOSE Signed Data Object (RFC 8152)
55799anySelf-describe CBOR
+ */ + +struct KnownTagData { uint32_t tag; uint32_t types; }; +static const struct KnownTagData knownTagData[] = { + { 0, (uint32_t)CborTextStringType }, + { 1, (uint32_t)(CborIntegerType+1) }, + { 2, (uint32_t)CborByteStringType }, + { 3, (uint32_t)CborByteStringType }, + { 4, (uint32_t)CborArrayType }, + { 5, (uint32_t)CborArrayType }, + { 16, (uint32_t)CborArrayType }, + { 17, (uint32_t)CborArrayType }, + { 18, (uint32_t)CborArrayType }, + { 21, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 22, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 23, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 24, (uint32_t)CborByteStringType }, + { 32, (uint32_t)CborTextStringType }, + { 33, (uint32_t)CborTextStringType }, + { 34, (uint32_t)CborTextStringType }, + { 35, (uint32_t)CborTextStringType }, + { 36, (uint32_t)CborTextStringType }, + { 96, (uint32_t)CborArrayType }, + { 97, (uint32_t)CborArrayType }, + { 98, (uint32_t)CborArrayType }, + { 55799, 0U } +}; + +static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft); + +static inline CborError validate_utf8_string(const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + const uint8_t * const end = buffer + n; + while (buffer < end) { + uint32_t uc = get_utf8(&buffer, end); + if (uc == ~0U) + return CborErrorInvalidUtf8TextString; + } + return CborNoError; +} + +static inline CborError validate_simple_type(uint8_t simple_type, uint32_t flags) +{ + /* At current time, all known simple types are those from RFC 7049, + * which are parsed by the parser into different CBOR types. + * That means that if we've got here, the type is unknown */ + if (simple_type < 32) + return (flags & CborValidateNoUnknownSimpleTypesSA) ? CborErrorUnknownSimpleType : CborNoError; + return (flags & CborValidateNoUnknownSimpleTypes) == CborValidateNoUnknownSimpleTypes ? + CborErrorUnknownSimpleType : CborNoError; +} + +static inline CborError validate_number(const CborValue *it, CborType type, uint32_t flags) +{ + CborError err = CborNoError; + size_t bytesUsed, bytesNeeded; + uint64_t value; + + if ((flags & CborValidateShortestIntegrals) == 0) + return err; + if (type >= CborHalfFloatType && type <= CborDoubleType) + return err; /* checked elsewhere */ + + err = extract_number_checked(it, &value, &bytesUsed); + if (err) + return err; + + bytesNeeded = 0; + if (value >= Value8Bit) + ++bytesNeeded; + if (value > 0xffU) + ++bytesNeeded; + if (value > 0xffffU) + bytesNeeded += 2; + if (value > 0xffffffffU) + bytesNeeded += 4; + if (bytesNeeded < bytesUsed) + return CborErrorOverlongEncoding; + return CborNoError; +} + +static inline CborError validate_tag(CborValue *it, CborTag tag, uint32_t flags, int recursionLeft) +{ + CborType type = cbor_value_get_type(it); + const size_t knownTagCount = sizeof(knownTagData) / sizeof(knownTagData[0]); + const struct KnownTagData *tagData = knownTagData; + const struct KnownTagData * const knownTagDataEnd = knownTagData + knownTagCount; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + if (flags & CborValidateNoTags) + return CborErrorExcludedType; + + /* find the tag data, if any */ + for ( ; tagData != knownTagDataEnd; ++tagData) { + if (tagData->tag < tag) + continue; + if (tagData->tag > tag) + tagData = NULL; + break; + } + if (tagData == knownTagDataEnd) + tagData = NULL; + + if (flags & CborValidateNoUnknownTags && !tagData) { + /* tag not found */ + if (flags & CborValidateNoUnknownTagsSA && tag < 24) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTagsSR) == CborValidateNoUnknownTagsSR && tag < 256) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTags) == CborValidateNoUnknownTags) + return CborErrorUnknownTag; + } + + if (flags & CborValidateTagUse && tagData && tagData->types) { + uint32_t allowedTypes = tagData->types; + + /* correct Integer so it's not zero */ + if (type == CborIntegerType) + type = (CborType)(type + 1); + + while (allowedTypes) { + if ((uint8_t)(allowedTypes & 0xff) == type) + break; + allowedTypes >>= 8; + } + if (!allowedTypes) + return CborErrorInappropriateTagForType; + } + + return validate_value(it, flags, recursionLeft); +} + +#ifndef CBOR_NO_FLOATING_POINT +static inline CborError validate_floating_point(CborValue *it, CborType type, uint32_t flags) +{ + CborError err; + int r; + double val; + float valf; + uint16_t valf16 = 0x7c01; /* dummy value, an infinity */ + + if (type != CborDoubleType) { + if (type == CborFloatType) { + err = cbor_value_get_float(it, &valf); + val = valf; + } else { +# ifdef CBOR_NO_HALF_FLOAT_TYPE + (void)valf16; + return CborErrorUnsupportedType; +# else + err = cbor_value_get_half_float(it, &valf16); + val = decode_half(valf16); +# endif + } + } else { + err = cbor_value_get_double(it, &val); + } + cbor_assert(err == CborNoError); /* can't fail */ + + r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) { + if (flags & CborValidateFiniteFloatingPoint) + return CborErrorExcludedValue; + if (flags & CborValidateShortestFloatingPoint) { + if (type == CborDoubleType) + return CborErrorOverlongEncoding; +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) + return CborErrorOverlongEncoding; + if (r == FP_NAN && valf16 != 0x7e00) + return CborErrorImproperValue; + if (r == FP_INFINITE && valf16 != 0x7c00 && valf16 != 0xfc00) + return CborErrorImproperValue; +# endif + } + } + + if (flags & CborValidateShortestFloatingPoint && type > CborHalfFloatType) { + if (type == CborDoubleType) { + valf = (float)val; + if ((double)valf == val) + return CborErrorOverlongEncoding; + } +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) { + valf16 = encode_half(valf); + if (valf == decode_half(valf16)) + return CborErrorOverlongEncoding; + } +# endif + } + + return CborNoError; +} +#endif + +static CborError validate_container(CborValue *it, int containerType, uint32_t flags, int recursionLeft) +{ + CborError err; + const uint8_t *previous = NULL; + const uint8_t *previous_end = NULL; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + + while (!cbor_value_at_end(it)) { + const uint8_t *current = cbor_value_get_next_byte(it); + + if (containerType == CborMapType) { + if (flags & CborValidateMapKeysAreString) { + CborType type = cbor_value_get_type(it); + if (type == CborTagType) { + /* skip the tags */ + CborValue copy = *it; + err = cbor_value_skip_tag(©); + if (err) + return err; + type = cbor_value_get_type(©); + } + if (type != CborTextStringType) + return CborErrorMapKeyNotString; + } + } + + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + + if (containerType != CborMapType) + continue; + + if (flags & CborValidateMapIsSorted) { + if (it->parser->flags & CborParserFlag_ExternalSource) + return CborErrorUnimplementedValidation; + if (previous) { + size_t bytelen1 = (size_t)(previous_end - previous); + size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current); + int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); + + if (r == 0 && bytelen1 != bytelen2) + r = bytelen1 < bytelen2 ? -1 : +1; + if (r > 0) + return CborErrorMapNotSorted; + if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique) + return CborErrorMapKeysNotUnique; + } + + previous = current; + previous_end = cbor_value_get_next_byte(it); + } + + /* map: that was the key, so get the value */ + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + } + return CborNoError; +} + +static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft) +{ + CborError err; + CborType type = cbor_value_get_type(it); + + if (cbor_value_is_length_known(it)) { + err = validate_number(it, type, flags); + if (err) + return err; + } else { + if (flags & CborValidateNoIndeterminateLength) + return CborErrorUnknownLength; + } + + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (!err) + err = validate_container(&recursed, type, flags, recursionLeft - 1); + if (err) { + copy_current_position(it, &recursed); + return err; + } + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; + return CborNoError; + } + + case CborIntegerType: { + uint64_t val; + err = cbor_value_get_raw_integer(it, &val); + cbor_assert(err == CborNoError); /* can't fail */ + + break; + } + + case CborByteStringType: + case CborTextStringType: { + size_t n = 0; + const void *ptr; + + err = cbor_value_begin_string_iteration(it); + if (err) + return err; + + while (1) { + CborValue next; + err = _cbor_value_get_string_chunk(it, &ptr, &n, &next); + if (!err) { + err = validate_number(it, type, flags); + if (err) + return err; + } + + *it = next; + if (err == CborErrorNoMoreStringChunks) + return cbor_value_finish_string_iteration(it); + if (err) + return err; + + if (type == CborTextStringType && flags & CborValidateUtf8) { + err = validate_utf8_string(ptr, n); + if (err) + return err; + } + } + + return CborNoError; + } + + case CborTagType: { + CborTag tag; + err = cbor_value_get_tag(it, &tag); + cbor_assert(err == CborNoError); /* can't fail */ + + err = cbor_value_advance_fixed(it); + if (err) + return err; + err = validate_tag(it, tag, flags, recursionLeft - 1); + if (err) + return err; + + return CborNoError; + } + + case CborSimpleType: { + uint8_t simple_type; + err = cbor_value_get_simple_type(it, &simple_type); + cbor_assert(err == CborNoError); /* can't fail */ + err = validate_simple_type(simple_type, flags); + if (err) + return err; + break; + } + + case CborNullType: + case CborBooleanType: + break; + + case CborUndefinedType: + if (flags & CborValidateNoUndefined) + return CborErrorExcludedType; + break; + + case CborHalfFloatType: + case CborFloatType: + case CborDoubleType: { +#ifdef CBOR_NO_FLOATING_POINT + return CborErrorUnsupportedType; +#else + err = validate_floating_point(it, type, flags); + if (err) + return err; + break; +#endif /* !CBOR_NO_FLOATING_POINT */ + } + + case CborInvalidType: + return CborErrorUnknownType; + } + + err = cbor_value_advance_fixed(it); + return err; +} + +/** + * Performs a full validation, controlled by the \a flags options, of the CBOR + * stream pointed by \a it and returns the error it found. If no error was + * found, it returns CborNoError and the application can iterate over the items + * with certainty that no errors will appear during parsing. + * + * If \a flags is CborValidateBasic, the result should be the same as + * cbor_value_validate_basic(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance() and cbor_value_validate_basic(). + * + * \sa CborValidationFlags, cbor_value_validate_basic(), cbor_value_advance() + */ +CborError cbor_value_validate(const CborValue *it, uint32_t flags) +{ + CborValue value = *it; + CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS); + if (err) + return err; + if (flags & CborValidateCompleteData && can_read_bytes(&value, 1)) + return CborErrorGarbageAtEnd; + return CborNoError; +} + +/** + * @} + */ diff --git a/deps/tinycbor/.appveyor.yml b/deps/tinycbor/.appveyor.yml new file mode 100644 index 00000000..ef47797e --- /dev/null +++ b/deps/tinycbor/.appveyor.yml @@ -0,0 +1,28 @@ +version: 0.6-build-{build} +pull_requests: + do_not_increment_build_number: true +image: +- Visual Studio 2017 +- Visual Studio 2019 +install: +- cmd: >- + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86) & (set QTDIR=C:\Qt\5.13\msvc2017) + + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\6.1\msvc2019_64) + + set path=%PATH%;%QTDIR%\bin +build_script: +- cmd: >- + nmake -f Makefile.nmake -nologo CFLAGS="-W3 -Os -MDd" + + cd tests + + qmake CONFIG-=release CONFIG+=debug + + nmake -nologo -s +test_script: +- cmd: >- + nmake -s -nologo TESTARGS=-silent check +artifacts: +- path: lib\tinycbor.lib +deploy: off diff --git a/deps/tinycbor/.gitattributes b/deps/tinycbor/.gitattributes new file mode 100644 index 00000000..76ed2567 --- /dev/null +++ b/deps/tinycbor/.gitattributes @@ -0,0 +1,4 @@ +.tag export-subst +.gitignore export-ignore +.gitattributes export-ignore +.appveyor.yml text diff --git a/deps/tinycbor/.gitignore b/deps/tinycbor/.gitignore new file mode 100644 index 00000000..3272de33 --- /dev/null +++ b/deps/tinycbor/.gitignore @@ -0,0 +1,81 @@ +# Frequent generated files +callgrind.out.* +pcviewer.cfg +*~ +*.a +*.la +*.core +*.d +*.dylib +*.moc +*.o +*.obj +*.orig +*.swp +*.rej +*.so +*.so.* +*.pbxuser +*.mode1 +*.mode1v3 +*_pch.h.cpp +*_resource.rc +.#* +*.*# +core +.qmake.cache +.qmake.stash +.qmake.vars +.device.vars +tags +.DS_Store +*.debug +Makefile* +*.prl +*.app +*.pro.user* +*.qmlproject.user* +*.gcov +*.gcda +*.gcno +*.flc +.*.swp +tinycbor.pc + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.exe.embed.manifest +*.exe_manifest.rc +*.exe_manifest.res + +# MinGW generated files +*.Debug +*.Release + +# INTEGRITY generated files +*.gpj +*.int +*.ael +*.dla +*.dnm +*.dep +*.map + +bin +doc +lib +src/cjson +src/doxygen.log +!/Makefile +.config diff --git a/deps/tinycbor/.tag b/deps/tinycbor/.tag new file mode 100644 index 00000000..6828f88d --- /dev/null +++ b/deps/tinycbor/.tag @@ -0,0 +1 @@ +$Format:%H$ diff --git a/deps/tinycbor/.travis.yml b/deps/tinycbor/.travis.yml new file mode 100644 index 00000000..4df21cb7 --- /dev/null +++ b/deps/tinycbor/.travis.yml @@ -0,0 +1,92 @@ +env: + - BUILD_DOCS=false +jobs: + include: + - # only build docs on main + if: branch = main + env: BUILD_DOCS=true + +language: cpp +matrix: + include: + - os: linux + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:beineri/opt-qt-5.12.1-xenial' + packages: + - qt512base valgrind + - doxygen + env: + - QMAKESPEC=linux-g++ + - EVAL="CC=gcc && CXX=g++" + - CFLAGS="-Os" + - LDFLAGS="-Wl,--no-undefined -lm" + - QMAKEFLAGS="-config release" + - QT_NO_CPU_FEATURE=rdrnd + - os: linux + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:beineri/opt-qt-5.12.1-xenial' + packages: + - qt512base + env: + - QMAKESPEC=linux-clang + - EVAL="CC=clang && CXX=clang++" + - CFLAGS="-Oz" + - LDFLAGS="-Wl,--no-undefined -lm" + - QMAKEFLAGS="-config release" + - MAKEFLAGS=-s + - TESTARGS=-silent + - os: linux + dist: xenial + env: + - QMAKESPEC=linux-gcc-freestanding + - EVAL="CXX=false" + - CFLAGS="-ffreestanding -Os" + - LDFLAGS="-Wl,--no-undefined -lm" + - os: linux + dist: xenial + env: + - QMAKESPEC=linux-gcc-no-math + - EVAL="CXX=false && touch src/math.h src/float.h" + - CFLAGS="-ffreestanding -DCBOR_NO_FLOATING_POINT -Os" + - LDFLAGS="-Wl,--no-undefined" + - LDLIBS="" + - os: osx + env: + - QMAKESPEC=macx-clang + - CFLAGS="-Oz" + - QMAKEFLAGS="-config debug" + - MAKEFLAGS=-s + - TESTARGS=-silent + - PATH=/usr/local/opt/qt5/bin:$PATH +install: + - if [ "${TRAVIS_OS_NAME}" != "linux" ]; then + brew update; + brew install qt5; + fi +script: + - PATH=`echo /opt/qt*/bin`:$PATH + - eval "$EVAL" + - make -s -f Makefile.configure configure | tee .config + - make -k + CFLAGS="$CFLAGS -march=native -g1 -Wall -Wextra -Werror" + CPPFLAGS="-DNDEBUG -DCBOR_ENCODER_WRITER_CONTROL=-1 -DCBOR_PARSER_READER_CONTROL=-1" + lib/libtinycbor.a + - size lib/libtinycbor.a | tee sizes + - make -s clean + - make -k + CFLAGS="$CFLAGS -O0 -g" + LDFLAGS="$LDFLAGS" ${LDLIBS+LDLIBS="$LDLIBS"} + - grep -q freestanding-pass .config || make + QMAKEFLAGS="$QMAKEFLAGS QMAKE_CXX=$CXX" + tests/Makefile + - grep -q freestanding-pass .config || + (cd tests && make TESTARGS=-silent check -k + TESTRUNNER=`which valgrind 2>/dev/null`) + - make -s clean + - ! [ $BUILD_DOCS ] || ./scripts/update-docs.sh diff --git a/deps/tinycbor/Doxyfile b/deps/tinycbor/Doxyfile new file mode 100644 index 00000000..a7263c2f --- /dev/null +++ b/deps/tinycbor/Doxyfile @@ -0,0 +1,49 @@ +PROJECT_NAME = "TinyCBOR $(VERSION) API" +OUTPUT_DIRECTORY = ../doc +ABBREVIATE_BRIEF = +SHORT_NAMES = YES +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = YES +TAB_SIZE = 8 +ALIASES = "value=\arg \c" +OPTIMIZE_OUTPUT_FOR_C = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +GENERATE_TODOLIST = NO +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = NO +GENERATE_DEPRECATEDLIST= NO +SHOW_USED_FILES = NO +WARN_IF_UNDOCUMENTED = NO +WARN_LOGFILE = doxygen.log +INPUT = . +FILE_PATTERNS = *.h \ + *.c \ + *.dox +EXCLUDE_PATTERNS = *_p.h +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +IGNORE_PREFIX = cbor_ \ + Cbor +HTML_TIMESTAMP = NO +GENERATE_HTMLHELP = YES +GENERATE_CHI = YES +BINARY_TOC = YES +TOC_EXPAND = YES +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +SEARCHENGINE = NO +GENERATE_LATEX = NO +COMPACT_LATEX = YES +MACRO_EXPANSION = YES +PREDEFINED = DOXYGEN \ + CBOR_INLINE_API= +CLASS_DIAGRAMS = NO +CLASS_GRAPH = NO +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +GRAPHICAL_HIERARCHY = NO +DIRECTORY_GRAPH = NO diff --git a/deps/tinycbor/LICENSE b/deps/tinycbor/LICENSE new file mode 100644 index 00000000..4aad977c --- /dev/null +++ b/deps/tinycbor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/tinycbor/Makefile b/deps/tinycbor/Makefile new file mode 100644 index 00000000..3135d70f --- /dev/null +++ b/deps/tinycbor/Makefile @@ -0,0 +1,256 @@ +# Variables: +prefix = /usr/local +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +includedir = $(prefix)/include +pkgconfigdir = $(libdir)/pkgconfig + +CFLAGS = -Wall -Wextra +LDFLAGS_GCSECTIONS = -Wl,--gc-sections +LDFLAGS += $(if $(gc_sections-pass),$(LDFLAGS_GCSECTIONS)) +LDLIBS = -lm + +GIT_ARCHIVE = git archive --prefix="$(PACKAGE)/" -9 +INSTALL = install +INSTALL_DATA = $(INSTALL) -m 644 +INSTALL_PROGRAM = $(INSTALL) -m 755 +QMAKE = qmake +MKDIR = mkdir -p +RMDIR = rmdir +SED = sed + +# Our sources +TINYCBOR_HEADERS = src/cbor.h src/cborjson.h src/tinycbor-version.h +TINYCBOR_FREESTANDING_SOURCES = \ + src/cborerrorstrings.c \ + src/cborencoder.c \ + src/cborencoder_close_container_checked.c \ + src/cborencoder_float.c \ + src/cborparser.c \ + src/cborparser_float.c \ + src/cborpretty.c \ +# +CBORDUMP_SOURCES = tools/cbordump/cbordump.c + +BUILD_SHARED = $(shell file -L /bin/sh 2>/dev/null | grep -q ELF && echo 1) +BUILD_STATIC = 1 + +ifneq ($(BUILD_STATIC),1) +ifneq ($(BUILD_SHARED),1) + $(error error: BUILD_STATIC and BUILD_SHARED can not be both disabled) +endif +endif + +INSTALL_TARGETS += $(bindir)/cbordump +ifeq ($(BUILD_SHARED),1) +BINLIBRARY=lib/libtinycbor.so +INSTALL_TARGETS += $(libdir)/libtinycbor.so.$(VERSION) +endif +ifeq ($(BUILD_STATIC),1) +BINLIBRARY=lib/libtinycbor.a +INSTALL_TARGETS += $(libdir)/libtinycbor.a +endif +INSTALL_TARGETS += $(pkgconfigdir)/tinycbor.pc +INSTALL_TARGETS += $(TINYCBOR_HEADERS:src/%=$(includedir)/tinycbor/%) + +# setup VPATH +MAKEFILE := $(lastword $(MAKEFILE_LIST)) +SRCDIR := $(dir $(MAKEFILE)) +VPATH = $(SRCDIR):$(SRCDIR)/src + +# Our version +GIT_DIR := $(strip $(shell git -C $(SRCDIR). rev-parse --git-dir 2> /dev/null)) +VERSION = $(shell cat $(SRCDIR)VERSION) +SOVERSION = $(shell cut -f1-2 -d. $(SRCDIR)VERSION) +PACKAGE = tinycbor-$(VERSION) + +# Check that QMAKE is Qt 5 +ifeq ($(origin QMAKE),file) + check_qmake = $(strip $(shell $(1) -query QT_VERSION 2>/dev/null | cut -b1)) + ifneq ($(call check_qmake,$(QMAKE)),5) + QMAKE := qmake -qt5 + ifneq ($(call check_qmake,$(QMAKE)),5) + QMAKE := qmake-qt5 + ifneq ($(call check_qmake,$(QMAKE)),5) + QMAKE := @echo >&2 $(MAKEFILE): Cannot find a Qt 5 qmake; false + endif + endif + endif +endif + +-include .config + +ifeq ($(wildcard .config),) + $(info .config file not yet created) +endif + +ifeq ($(freestanding-pass),1) +TINYCBOR_SOURCES = $(TINYCBOR_FREESTANDING_SOURCES) +else +TINYCBOR_SOURCES = \ + $(TINYCBOR_FREESTANDING_SOURCES) \ + src/cborparser_dup_string.c \ + src/cborpretty_stdio.c \ + src/cbortojson.c \ + src/cborvalidation.c \ +# +# if open_memstream is unavailable on the system, try to implement our own +# version using funopen or fopencookie +ifeq ($(open_memstream-pass),) + ifeq ($(funopen-pass)$(fopencookie-pass),) + CFLAGS += -DWITHOUT_OPEN_MEMSTREAM + ifeq ($(wildcard .config),.config) + $(warning warning: funopen and fopencookie unavailable, open_memstream can not be implemented and conversion to JSON will not work properly!) + endif + else + TINYCBOR_SOURCES += src/open_memstream.c + endif +endif +endif + +# json2cbor depends on an external library (cjson) +ifneq ($(cjson-pass)$(system-cjson-pass),) + JSON2CBOR_SOURCES = tools/json2cbor/json2cbor.c + INSTALL_TARGETS += $(bindir)/json2cbor + ifeq ($(system-cjson-pass),1) + LDFLAGS_CJSON = -lcjson + else + JSON2CBOR_SOURCES += src/cjson/cJSON.c + json2cbor_CCFLAGS = -I$(SRCDIR)src/cjson + endif +endif + +# Rules +all: .config \ + $(if $(subst 0,,$(BUILD_STATIC)),lib/libtinycbor.a) \ + $(if $(subst 0,,$(BUILD_SHARED)),lib/libtinycbor.so) \ + $(if $(freestanding-pass),,bin/cbordump) \ + tinycbor.pc +all: $(if $(JSON2CBOR_SOURCES),bin/json2cbor) +check: tests/Makefile | $(BINLIBRARY) + $(MAKE) -C tests check +silentcheck: | $(BINLIBRARY) + TESTARGS=-silent $(MAKE) -f $(MAKEFILE) -s check +configure: .config +.config: Makefile.configure + $(MAKE) -f $(SRCDIR)Makefile.configure OUT='$@' configure + +lib/libtinycbor-freestanding.a: $(TINYCBOR_FREESTANDING_SOURCES:.c=.o) + @$(MKDIR) -p lib + $(AR) cqs $@ $^ + +lib/libtinycbor.a: $(TINYCBOR_SOURCES:.c=.o) + @$(MKDIR) -p lib + $(AR) cqs $@ $^ + +lib/libtinycbor.so: $(TINYCBOR_SOURCES:.c=.pic.o) + @$(MKDIR) -p lib + $(CC) -shared -Wl,-soname,libtinycbor.so.$(SOVERSION) -o lib/libtinycbor.so.$(VERSION) $(LDFLAGS) $^ $(LDLIBS) + cd lib ; ln -sf libtinycbor.so.$(VERSION) libtinycbor.so ; ln -sf libtinycbor.so.$(VERSION) libtinycbor.so.$(SOVERSION) + +bin/cbordump: $(CBORDUMP_SOURCES:.c=.o) $(BINLIBRARY) + @$(MKDIR) -p bin + $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS) + +bin/json2cbor: $(JSON2CBOR_SOURCES:.c=.o) $(BINLIBRARY) + @$(MKDIR) -p bin + $(CC) -o $@ $(LDFLAGS) $^ $(LDFLAGS_CJSON) $(LDLIBS) + +tinycbor.pc: tinycbor.pc.in + $(SED) > $@ < $< \ + -e 's,@prefix@,$(prefix),' \ + -e 's,@exec_prefix@,$(exec_prefix),' \ + -e 's,@libdir@,$(libdir),' \ + -e 's,@includedir@,$(includedir),' \ + -e 's,@version@,$(VERSION),' + +tests/Makefile: tests/tests.pro + $(QMAKE) $(QMAKEFLAGS) -o $@ $< + +$(PACKAGE).tar.gz: | .git + GIT_DIR=$(SRCDIR).git $(GIT_ARCHIVE) --format=tar.gz -o "$(PACKAGE).tar.gz" HEAD +$(PACKAGE).zip: | .git + GIT_DIR=$(SRCDIR).git $(GIT_ARCHIVE) --format=zip -o "$(PACKAGE).zip" HEAD + +$(DESTDIR)$(libdir)/%: lib/% + $(INSTALL) -d $(@D) + $(INSTALL_DATA) $< $@ +$(DESTDIR)$(bindir)/%: bin/% + $(INSTALL) -d $(@D) + $(INSTALL_PROGRAM) $< $@ +$(DESTDIR)$(pkgconfigdir)/%: % + $(INSTALL) -d $(@D) + $(INSTALL_DATA) $< $@ +$(DESTDIR)$(includedir)/tinycbor/%: src/% + $(INSTALL) -d $(@D) + $(INSTALL_DATA) $< $@ + +install-strip: + $(MAKE) -f $(MAKEFILE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install + +install: $(INSTALL_TARGETS:%=$(DESTDIR)%) +ifeq ($(BUILD_SHARED),1) + ln -sf libtinycbor.so.$(VERSION) $(DESTDIR)$(libdir)/libtinycbor.so + ln -sf libtinycbor.so.$(VERSION) $(DESTDIR)$(libdir)/libtinycbor.so.$(SOVERSION) +endif + +uninstall: + $(RM) $(INSTALL_TARGETS:%=$(DESTDIR)%) + $(RM) $(DESTDIR)$(libdir)/libtinycbor.so + $(RM) $(DESTDIR)$(libdir)/libtinycbor.so.$(SOVERSION) + +mostlyclean: + $(RM) $(TINYCBOR_SOURCES:.c=.o) + $(RM) $(TINYCBOR_SOURCES:.c=.pic.o) + $(RM) $(CBORDUMP_SOURCES:.c=.o) + +clean: mostlyclean + $(RM) bin/cbordump + $(RM) bin/json2cbor + $(RM) lib/libtinycbor.a + $(RM) lib/libtinycbor-freestanding.a + $(RM) tinycbor.pc + $(RM) lib/libtinycbor.so* + test -e tests/Makefile && $(MAKE) -C tests clean || : + +distclean: clean + test -e tests/Makefile && $(MAKE) -C tests distclean || : + +docs: + cd $(SRCDIR)src && VERSION=$(VERSION) doxygen $(SRCDIR)/../Doxyfile + +dist: $(PACKAGE).tar.gz $(PACKAGE).zip +distcheck: .git + -$(RM) -r $${TMPDIR-/tmp}/tinycbor-distcheck + GIT_DIR=$(SRCDIR).git git archive --prefix=tinycbor-distcheck/ --format=tar HEAD | tar -xf - -C $${TMPDIR-/tmp} + cd $${TMPDIR-/tmp}/tinycbor-distcheck && $(MAKE) silentcheck + $(RM) -r $${TMPDIR-/tmp}/tinycbor-distcheck + +tag: distcheck + @cd $(SRCDIR). && perl scripts/maketag.pl + +.PHONY: all check silentcheck configure install uninstall +.PHONY: mostlyclean clean distclean +.PHONY: docs dist distcheck release +.SECONDARY: + +cflags := $(CPPFLAGS) -I$(SRCDIR)src +cflags += -std=gnu99 $(CFLAGS) + +ifneq ($(DISABLE_WERROR),1) +cflags += \ + -Werror=discarded-qualifiers \ + -Werror=incompatible-pointer-types \ + -Werror=implicit-function-declaration \ + -Werror=int-conversion +endif + +%.o: %.c + @test -d $(@D) || $(MKDIR) $(@D) + $(CC) $(cflags) $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $< +%.pic.o: %.c + @test -d $(@D) || $(MKDIR) $(@D) + $(CC) $(cflags) -fPIC $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $< + +-include src/*.d diff --git a/deps/tinycbor/README b/deps/tinycbor/README new file mode 100644 index 00000000..167efa06 --- /dev/null +++ b/deps/tinycbor/README @@ -0,0 +1,13 @@ +Concise Binary Object Representation (CBOR) Library +--------------------------------------------------- + +To build TinyCBOR: + + make + +If you want to change the compiler or pass extra compiler flags: + + make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32" + +Documentation: https://intel.github.io/tinycbor/current/ + diff --git a/deps/tinycbor/TODO b/deps/tinycbor/TODO new file mode 100644 index 00000000..e9103ee6 --- /dev/null +++ b/deps/tinycbor/TODO @@ -0,0 +1,25 @@ +==== To Do list for libcbor ==== +=== General === +* API review +* Benchmark +* Write examples +** Simple decoder +** Decoder to JSON +** Windowed encoding/decoding (limited memory) + +=== Encoder === +* Write API docs +* Add API for creating indeterminate-length arrays and maps +* Add API for creating indeterminate-length strings +* Add API for relaxing doubles to floats and to integers +* Add length-checking of the sub-containers (#ifndef CBOR_ENCODER_NO_USER_CHECK) +* Decide how to indicate number of bytes needed +** Suggestion: return negative number from the functions + +=== Decoder === +* Write functions not yet implemented +* Add API for stream-decoding strings +* Add API for checking known tags and simple types +* (unlikely) Add API for checking the pairing of a tag and the tagged type +* Write tests for error conditions +* Fuzzy-test the decoder diff --git a/deps/tinycbor/VERSION b/deps/tinycbor/VERSION new file mode 100644 index 00000000..a918a2aa --- /dev/null +++ b/deps/tinycbor/VERSION @@ -0,0 +1 @@ +0.6.0 diff --git a/deps/tinycbor/examples/examples.pro b/deps/tinycbor/examples/examples.pro new file mode 100644 index 00000000..22071ac3 --- /dev/null +++ b/deps/tinycbor/examples/examples.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = simplereader.pro diff --git a/deps/tinycbor/examples/simplereader.c b/deps/tinycbor/examples/simplereader.c new file mode 100644 index 00000000..0612ba44 --- /dev/null +++ b/deps/tinycbor/examples/simplereader.c @@ -0,0 +1,185 @@ +#include "../src/cbor.h" + +#include +#include +#include +#include +#include + +static uint8_t *readfile(const char *fname, size_t *size) +{ + struct stat st; + FILE *f = fopen(fname, "rb"); + if (!f) + return NULL; + if (fstat(fileno(f), &st) == -1) + return NULL; + uint8_t *buf = malloc(st.st_size); + if (buf == NULL) + return NULL; + *size = fread(buf, st.st_size, 1, f) == 1 ? st.st_size : 0; + fclose(f); + return buf; +} + +static void indent(int nestingLevel) +{ + while (nestingLevel--) + printf(" "); +} + +static void dumpbytes(const uint8_t *buf, size_t len) +{ + printf("\""); + while (len--) + printf("\\x%02X", *buf++); + printf("\""); +} + +static CborError dumprecursive(CborValue *it, int nestingLevel) +{ + while (!cbor_value_at_end(it)) { + CborError err; + CborType type = cbor_value_get_type(it); + + indent(nestingLevel); + switch (type) { + case CborArrayType: + case CborMapType: { + // recursive type + CborValue recursed; + assert(cbor_value_is_container(it)); + puts(type == CborArrayType ? "Array[" : "Map["); + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; // parse error + err = dumprecursive(&recursed, nestingLevel + 1); + if (err) + return err; // parse error + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; // parse error + indent(nestingLevel); + puts("]"); + continue; + } + + case CborIntegerType: { + int64_t val; + cbor_value_get_int64(it, &val); // can't fail + printf("%lld\n", (long long)val); + break; + } + + case CborByteStringType: { + uint8_t *buf; + size_t n; + err = cbor_value_dup_byte_string(it, &buf, &n, it); + if (err) + return err; // parse error + dumpbytes(buf, n); + puts(""); + free(buf); + continue; + } + + case CborTextStringType: { + char *buf; + size_t n; + err = cbor_value_dup_text_string(it, &buf, &n, it); + if (err) + return err; // parse error + printf("\"%s\"\n", buf); + free(buf); + continue; + } + + case CborTagType: { + CborTag tag; + cbor_value_get_tag(it, &tag); // can't fail + printf("Tag(%lld)\n", (long long)tag); + break; + } + + case CborSimpleType: { + uint8_t type; + cbor_value_get_simple_type(it, &type); // can't fail + printf("simple(%u)\n", type); + break; + } + + case CborNullType: + puts("null"); + break; + + case CborUndefinedType: + puts("undefined"); + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); // can't fail + puts(val ? "true" : "false"); + break; + } + + case CborDoubleType: { + double val; + if (false) { + float f; + case CborFloatType: + cbor_value_get_float(it, &f); + val = f; + } else { + cbor_value_get_double(it, &val); + } + printf("%g\n", val); + break; + } + case CborHalfFloatType: { + uint16_t val; + cbor_value_get_half_float(it, &val); + printf("__f16(%04x)\n", val); + break; + } + + case CborInvalidType: + assert(false); // can't happen + break; + } + + err = cbor_value_advance_fixed(it); + if (err) + return err; + } + return CborNoError; +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + puts("simplereader "); + return 1; + } + + size_t length; + uint8_t *buf = readfile(argv[1], &length); + if (!buf) { + perror("readfile"); + return 1; + } + + CborParser parser; + CborValue it; + CborError err = cbor_parser_init(buf, length, 0, &parser, &it); + if (!err) + err = dumprecursive(&it, 0); + free(buf); + + if (err) { + fprintf(stderr, "CBOR parsing failure at offset %ld: %s\n", + it.ptr - buf, cbor_error_string(err)); + return 1; + } + return 0; +} diff --git a/deps/tinycbor/examples/simplereader.pro b/deps/tinycbor/examples/simplereader.pro new file mode 100644 index 00000000..07fdc6ac --- /dev/null +++ b/deps/tinycbor/examples/simplereader.pro @@ -0,0 +1,3 @@ +CONFIG -= qt +SOURCES = simplereader.c +include(../src/src.pri) diff --git a/deps/tinycbor/scripts/maketag.pl b/deps/tinycbor/scripts/maketag.pl new file mode 100644 index 00000000..5b1a8b79 --- /dev/null +++ b/deps/tinycbor/scripts/maketag.pl @@ -0,0 +1,91 @@ +#!perl +use strict; +sub run(@) { + open PROC, "-|", @_ or die("Cannot run $_[0]: $!"); + my @out; + while () { + chomp; + push @out, $_; + } + close PROC; + return @out; +} + +my @tags = run("git", "tag"); +my @v = run("git", "show", "HEAD:VERSION"); +my $v = $v[0]; + +my $tagfile = ".git/TAG_EDITMSG"; +open TAGFILE, ">", $tagfile + or die("Cannot create file for editing tag message: $!"); +select TAGFILE; +print "TinyCBOR release $v\n"; +print "\n"; +print "# Write something nice about this release here\n"; + +# Do we have a commit template? +my @result = run("git", "config", "--get", "commit.template"); +if (scalar @result) { + open TEMPLATE, "<", $result[0]; + map { print $_; }