Add the API to do more in-depth validation
This implements barely the canonical validation. Implemented features:
- CborValidateShortestFloatingPoint
- CborValidateNoIndeterminateLength
- CborValidateNoUndefined
- CborValidateNoTags
- CborValidateCompleteData
- CborValidateNoUnknownSimpleTypesSA
- CborValidateNoUnknownSimpleTypes
Signed-off-by: Thiago Macieira <thiago.macieira@intel.com>
diff --git a/Makefile b/Makefile
index 1e5bff1..11c1a2c 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,7 @@
src/cborparser_dup_string.c \
src/cborpretty.c \
src/cbortojson.c \
+ src/cborvalidation.c \
#
CBORDUMP_SOURCES = tools/cbordump/cbordump.c
diff --git a/Makefile.nmake b/Makefile.nmake
index 1e675f6..4945d07 100644
--- a/Makefile.nmake
+++ b/Makefile.nmake
@@ -7,7 +7,8 @@
src\cborencoder_close_container_checked.c \
src\cborparser.c \
src\cborparser_dup_string.c \
- src\cborpretty.c
+ src\cborpretty.c \
+ src/cborvalidation.c
TINYCBOR_OBJS = \
src\cborerrorstrings.obj \
src\cborencoder.obj \
diff --git a/src/cbor.h b/src/cbor.h
index 98281b9..46e72e9 100644
--- a/src/cbor.h
+++ b/src/cbor.h
@@ -140,6 +140,10 @@
CborErrorInappropriateTagForType,
CborErrorDuplicateObjectKeys,
CborErrorInvalidUtf8TextString,
+ CborErrorExcludedType,
+ CborErrorExcludedValue,
+ CborErrorImproperValue,
+ CborErrorOverlongEncoding,
/* encoder errors */
CborErrorTooManyItems = 768,
@@ -483,6 +487,51 @@
return CborNoError;
}
+/* Validation API */
+
+enum CborValidationFlags {
+ /* Bit mapping:
+ * bits 0-7 (8 bits): canonical format
+ * bits 8-11 (4 bits): canonical format & strict mode
+ * bits 12-20 (8 bits): strict mode
+ * bits 21-31 (10 bits): other
+ */
+
+ CborValidateShortestIntegrals = 0x0001,
+ CborValidateShortestFloatingPoint = 0x0002,
+ CborValidateShortestNumbers = CborValidateShortestIntegrals | CborValidateShortestFloatingPoint,
+ CborValidateNoIndeterminateLength = 0x0100,
+ CborValidateMapIsSorted = 0x0200 | CborValidateNoIndeterminateLength,
+
+ CborValidateCanonicalFormat = 0x0fff,
+
+ CborValidateMapKeysAreUnique = 0x1000 | CborValidateMapIsSorted,
+ CborValidateTagUse = 0x2000,
+ CborValidateUtf8 = 0x4000,
+
+ CborValidateStrictMode = 0xfff00,
+
+ CborValidateMapKeysAreString = 0x100000,
+ CborValidateNoUndefined = 0x200000,
+ CborValidateNoTags = 0x400000,
+ CborValidateFiniteFloatingPoint = 0x800000,
+ /* unused = 0x1000000, */
+ /* unused = 0x2000000, */
+
+ CborValidateNoUnknownSimpleTypesSA = 0x4000000,
+ CborValidateNoUnknownSimpleTypes = 0x8000000 | CborValidateNoUnknownSimpleTypesSA,
+ CborValidateNoUnknownTagsSA = 0x10000000,
+ CborValidateNoUnknownTagsSR = 0x20000000 | CborValidateNoUnknownTagsSA,
+ CborValidateNoUnknownTags = 0x40000000 | CborValidateNoUnknownTagsSR,
+
+ CborValidateCompleteData = (int)0x80000000,
+
+ CborValidateStrictest = (int)~0U,
+ CborValidateBasic = 0
+};
+
+CBOR_API CborError cbor_value_validate(const CborValue *it, int flags);
+
/* Human-readable (dump) API */
enum CborPrettyFlags {
diff --git a/src/cborerrorstrings.c b/src/cborerrorstrings.c
index d2fe42f..0fd556b 100644
--- a/src/cborerrorstrings.c
+++ b/src/cborerrorstrings.c
@@ -134,6 +134,16 @@
case CborErrorInvalidUtf8TextString:
return _("invalid UTF-8 content in string");
+ case CborErrorExcludedType:
+ return _("excluded type found");
+
+ case CborErrorExcludedValue:
+ return _("excluded value found");
+
+ case CborErrorImproperValue:
+ case CborErrorOverlongEncoding:
+ return _("value encoded in non-canonical form");
+
case CborErrorTooManyItems:
return _("too many items added to encoder");
diff --git a/src/cborvalidation.c b/src/cborvalidation.c
new file mode 100644
index 0000000..6b19c58
--- /dev/null
+++ b/src/cborvalidation.c
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#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 <string.h>
+
+#ifndef CBOR_NO_FLOATING_POINT
+# include <float.h>
+# include <math.h>
+#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 syntax 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 integrals 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.
+ * \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:
+[will be added in the next commit]
+ */
+
+static CborError validate_value(CborValue *it, int flags, int recursionLeft);
+
+static inline CborError validate_simple_type(uint8_t simple_type, int 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_tag(CborValue *it, CborTag tag, int flags, int recursionLeft)
+{
+ CborType type = cbor_value_get_type(it);
+ if (!recursionLeft)
+ return CborErrorNestingTooDeep;
+
+ if (flags & CborValidateNoTags)
+ return CborErrorExcludedType;
+ if (flags & CborValidateNoUnknownTags) {
+ if (tag > 255 && (flags & CborValidateNoUnknownTagsSR) == 0)
+ return CborErrorUnknownTag;
+ if (flags & CborValidateNoUnknownTagsSR) {
+ if (tag > 23 && (flags & CborValidateNoUnknownTagsSA) == 0)
+ return CborErrorUnknownTag;
+ if (flags & CborValidateNoUnknownTagsSA)
+ return CborErrorUnknownTag;
+ }
+ }
+
+ return validate_value(it, flags, recursionLeft);
+}
+
+#ifndef CBOR_NO_FLOATING_POINT
+static inline CborError validate_floating_point(CborValue *it, CborType type, int flags)
+{
+ CborError err;
+ double val;
+ float valf;
+ uint16_t valf16;
+
+ if (type != CborDoubleType) {
+ if (type == CborFloatType) {
+ err = cbor_value_get_float(it, &valf);
+ val = valf;
+ } else {
+# ifdef CBOR_NO_HALF_FLOAT_TYPE
+ (void)val16;
+ 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 */
+
+ int 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, int flags, int recursionLeft)
+{
+ CborError err;
+ if (!recursionLeft)
+ return CborErrorNestingTooDeep;
+
+ while (!cbor_value_at_end(it)) {
+ err = validate_value(it, flags, recursionLeft);
+ if (err)
+ return err;
+
+ if (containerType == CborArrayType)
+ continue;
+
+ /* map: that was the key, so get he value */
+ err = validate_value(it, flags, recursionLeft);
+ if (err)
+ return err;
+ }
+ return CborNoError;
+}
+
+static CborError validate_value(CborValue *it, int flags, int recursionLeft)
+{
+ CborError err;
+ if (flags & CborValidateNoIndeterminateLength) {
+ if (!cbor_value_is_length_known(it))
+ return CborErrorUnknownLength;
+ }
+
+ CborType type = cbor_value_get_type(it);
+ 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) {
+ it->ptr = recursed.ptr;
+ 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;
+
+ while (1) {
+ err = _cbor_value_get_string_chunk(it, &ptr, &n, it);
+ if (err)
+ return err;
+ if (!ptr)
+ break;
+ }
+
+ 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 other 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, int flags)
+{
+ CborValue value = *it;
+ CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS);
+ if (err)
+ return err;
+ if (flags & CborValidateCompleteData && it->ptr != it->parser->end)
+ return CborErrorGarbageAtEnd;
+ return CborNoError;
+}
+
+/**
+ * @}
+ */
diff --git a/src/src.pri b/src/src.pri
index 5e25480..01887aa 100644
--- a/src/src.pri
+++ b/src/src.pri
@@ -6,6 +6,7 @@
$$PWD/cborparser_dup_string.c \
$$PWD/cborpretty.c \
$$PWD/cbortojson.c \
+ $$PWD/cborvalidation.c \
HEADERS += $$PWD/cbor.h $$PWD/tinycbor-version.h
diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp
index 0bf0fed..484d1b1 100644
--- a/tests/parser/tst_parser.cpp
+++ b/tests/parser/tst_parser.cpp
@@ -87,6 +87,8 @@
void checkedIntegers();
void validation_data();
void validation();
+ void strictValidation_data();
+ void strictValidation();
void resumeParsing_data();
void resumeParsing();
void endPointer_data();
@@ -1303,6 +1305,10 @@
QTest::newRow("illegal-number-in-map-length-1") << raw("\x81\xbc") << 0 << CborErrorIllegalNumber;
QTest::newRow("illegal-number-in-map-length-2") << raw("\x81\xbd") << 0 << CborErrorIllegalNumber;
QTest::newRow("illegal-number-in-map-length-3") << raw("\x81\xbe") << 0 << CborErrorIllegalNumber;
+ QTest::newRow("illegal-number-in-tag-1") << raw("\x81\xdc") << 0 << CborErrorIllegalNumber;
+ QTest::newRow("illegal-number-in-tag-2") << raw("\x81\xdd") << 0 << CborErrorIllegalNumber;
+ QTest::newRow("illegal-number-in-tag-3") << raw("\x81\xde") << 0 << CborErrorIllegalNumber;
+ QTest::newRow("illegal-number-in-tag-4") << raw("\x81\xdf") << 0 << CborErrorIllegalNumber;
QTest::newRow("unsigned-too-short-1-0") << raw("\x81\x18") << 0 << CborErrorUnexpectedEOF; // requires 1 byte, 0 given
QTest::newRow("unsigned-too-short-2-0") << raw("\x81\x19") << 0 << CborErrorUnexpectedEOF; // requires 2 bytes, 0 given
@@ -1526,10 +1532,160 @@
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
CborError err2 = cbor_value_validate_basic(&first);
+ CborError err3 = cbor_value_validate(&first, CborValidateBasic);
err = parseOne(&first, &decoded);
QCOMPARE(int(err), int(expectedError));
- if (!QByteArray(QTest::currentDataTag()).contains("utf8"))
+ if (!QByteArray(QTest::currentDataTag()).contains("utf8")) {
QCOMPARE(int(err2), int(expectedError));
+ QCOMPARE(int(err3), int(expectedError));
+ }
+}
+
+void tst_Parser::strictValidation_data()
+{
+ addValidationColumns();
+
+ // Canonical validation - fixed types
+ QTest::newRow("unsigned-0") << raw("\x00") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("unsigned-24") << raw("\x18\x18") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("unsigned-256") << raw("\x19\1\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("unsigned-65526") << raw("\x1a\0\1\0\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("unsigned-4294967296") << raw("\x1b\0\0\0\1\0\0\0\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("negative-1") << raw("\x20") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("negative-25") << raw("\x38\x38") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("negative-257") << raw("\x39\1\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("negative-65527") << raw("\x3a\0\1\0\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("negative-4294967297") << raw("\x3b\0\0\0\1\0\0\0\0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("simple-0") << raw("\xe0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("false") << raw("\xf4") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("true") << raw("\xf5") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("null") << raw("\xf6") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("undefined") << raw("\xf7") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("simple-32") << raw("\xf8\x20") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("fp-nan") << raw("\xf9\x7e\00") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("fp--inf") << raw("\xf9\xfc\00") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("fp-+inf") << raw("\xf9\x7c\00") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("overlong-fp-nan_f") << raw("\xfa\x7f\xc0\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+ QTest::newRow("overlong-fp--inf_f") << raw("\xfa\xff\x80\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+ QTest::newRow("overlong-fp-+inf_f") << raw("\xfa\x7f\x80\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+ QTest::newRow("overlong-fp-nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+ QTest::newRow("overlong-fp--inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+ QTest::newRow("overlong-fp-+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << int(CborValidateCanonicalFormat) << CborErrorOverlongEncoding;
+
+ // canonical - lengths
+ QByteArray data24(24, 0x20); // also decodes as -1
+ QByteArray data256(256, 0x40); // also decodes as h''
+ QByteArray data65536(65536, 0x60);// also decodes as ""
+ QTest::newRow("bytearray-0") << raw("\x40") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("bytearray-24") << (raw("\x58\x18") + data24) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("bytearray-256") << (raw("\x59\1\0") + data256) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("bytearray-65536") << (raw("\x5a\0\1\0\0") + data65536) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("_bytearray-0") << raw("\x5f\xff") << int(CborValidateCanonicalFormat) << CborErrorUnknownLength;
+ QTest::newRow("string-0") << raw("\x60") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("string-24") << (raw("\x78\x18") + data24) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("string-256") << (raw("\x79\1\0") + data256) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("string-65536") << (raw("\x7a\0\1\0\0") + data65536) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("_string-0") << raw("\x7f\xff") << int(CborValidateCanonicalFormat) << CborErrorUnknownLength;
+ QTest::newRow("array-0") << raw("\x80") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("array-24") << (raw("\x98\x18") + data24) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("array-256") << (raw("\x99\1\0") + data256) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("array-65536") << (raw("\x9a\0\1\0\0") + data65536) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("_array-0") << raw("\x9f\xff") << int(CborValidateCanonicalFormat) << CborErrorUnknownLength;
+
+ // we need unique, sorted, string keys for map
+ // we'll make all key-value pairs a total of 4 bytes
+ char mapentry[] = { 0x62, 0, 0, 0x20 };
+ QByteArray mapdata24(24 * sizeof(mapentry), Qt::Uninitialized);
+ QByteArray mapdata256(256 * sizeof(mapentry), Qt::Uninitialized);
+ char *mapdata24ptr = mapdata24.data();
+ char *mapdata256ptr = mapdata256.data();
+ for (int i = 0; i < 256; ++i) {
+ mapentry[1] = 'A' + (i >> 4);
+ mapentry[2] = 'a' + (i & 0xf);
+ memcpy(mapdata256ptr + i * sizeof(mapentry), mapentry, sizeof(mapentry));
+ if (i < 24)
+ memcpy(mapdata24ptr + i * sizeof(mapentry), mapentry, sizeof(mapentry));
+ }
+ QTest::newRow("map-0") << raw("\xa0") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("map-24") << (raw("\xb8\x18") + mapdata24) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("map-256") << (raw("\xb9\1\0") + mapdata256) << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("_map-0") << raw("\xbf\xff") << int(CborValidateCanonicalFormat) << CborErrorUnknownLength;
+
+ QTest::newRow("tag-0") << raw("\xc0\x60") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("tag-24") << raw("\xd8\x18\x40") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("tag-55799") << raw("\xd9\xd9\xf7\x60") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("tag-65536") << raw("\xda\0\1\0\0\x60") << int(CborValidateCanonicalFormat) << CborNoError;
+ QTest::newRow("tag-4294967296") << raw("\xdb\0\0\0\1\0\0\0\0\x60") << int(CborValidateCanonicalFormat) << CborNoError;
+
+ // excluded non-finite
+ QTest::newRow("excluded-fp-nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-nan_f") << raw("\xfa\x7f\xc0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp--inf_f") << raw("\xfa\xff\x80\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp--inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-+inf_f") << raw("\xfa\x7f\x80\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+
+ // excluded undefined
+ QTest::newRow("no-undefined") << raw("\xf7") << int(CborValidateNoUndefined) << CborErrorExcludedType;
+
+ // exclude non-finite
+ QTest::newRow("excluded-fp-nan_f16") << raw("\xf9\x7e\00") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp--inf_f16") << raw("\xf9\xfc\00") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-+inf_f16") << raw("\xf9\x7c\00") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-nan_f") << raw("\xfa\x7f\xc0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp--inf_f") << raw("\xfa\xff\x80\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-+inf_f") << raw("\xfa\x7f\x80\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp--inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+ QTest::newRow("excluded-fp-+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << int(CborValidateFiniteFloatingPoint) << CborErrorExcludedValue;
+
+ // unknown simple types
+ QTest::newRow("unknown-simple-type-0") << raw("\xe0") << int(CborValidateNoUnknownSimpleTypes) << CborErrorUnknownSimpleType;
+ QTest::newRow("unknown-simple-type-32") << raw("\xf8\x20") << int(CborValidateNoUnknownSimpleTypes) << CborErrorUnknownSimpleType;
+ QTest::newRow("allowed-simple-type-32") << raw("\xf8\x20") << int(CborValidateNoUnknownSimpleTypesSA) << CborNoError;
+
+ // unknown tags
+ QTest::newRow("unknown-tag-6") << raw("\xc6\x60") << int(CborValidateNoUnknownTags) << CborErrorUnknownTag;
+ QTest::newRow("unknown-tag-31") << raw("\xd8\x1f\x60") << int(CborValidateNoUnknownTags) << CborErrorUnknownTag;
+ QTest::newRow("unknown-tag-256") << raw("\xd9\1\0\x60") << int(CborValidateNoUnknownTags) << CborErrorUnknownTag;
+ QTest::newRow("unknown-tag-65536") << raw("\xda\0\1\0\0\x60") << int(CborValidateNoUnknownTags) << CborErrorUnknownTag;
+ QTest::newRow("unknown-tag-4294967296") << raw("\xdb\0\0\0\1\0\0\0\0\x60") << int(CborValidateNoUnknownTags) << CborErrorUnknownTag;
+// QTest::newRow("allowed-tag-31") << raw("\xd8\x1f\x60") << int(CborValidateNoUnknownTagsSA) << CborNoError;
+// QTest::newRow("allowed-tag-256") << raw("\xd8\x1f\x60") << int(CborValidateNoUnknownTagsSR) << CborNoError;
+
+ // excluded tags
+ QTest::newRow("excluded-tag-0") << raw("\xc0\x60") << int(CborValidateNoTags) << CborErrorExcludedType;
+ QTest::newRow("excluded-tag-24") << raw("\xd8\x18\x40") << int(CborValidateNoTags) << CborErrorExcludedType;
+ QTest::newRow("excluded-tag-55799") << raw("\xd9\xd9\xf7\x60") << int(CborValidateNoTags) << CborErrorExcludedType;
+
+ // complete data
+ QTest::newRow("garbage-data-0") << raw("\0\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-1") << raw("\x20\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-2") << raw("\x40\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-3") << raw("\x60\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-4") << raw("\x80\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-5") << raw("\xa0\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-6") << raw("\xc0\x60\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-7") << raw("\xf4\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-f16") << raw("\xf9\0\0\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-f32") << raw("\xfa\0\0\0\0\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+ QTest::newRow("garbage-data-f64") << raw("\xfb\0\0\0\0\0\0\0\0\1") << int(CborValidateCompleteData) << CborErrorGarbageAtEnd;
+}
+
+void tst_Parser::strictValidation()
+{
+ QFETCH(QByteArray, data);
+ QFETCH(int, flags);
+ QFETCH(CborError, expectedError);
+
+ QString decoded;
+ CborParser parser;
+ CborValue first;
+ CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
+ QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+
+ err = cbor_value_validate(&first, flags);
+ QCOMPARE(int(err), int(expectedError));
}
void tst_Parser::resumeParsing_data()