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/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