Decode conformance for preferred serialization, CDE and dCBOR (#216)
Three conformance modes are added for decoding
- preferred serialization
- CDE
- dCBOR
This checks for sort ordering and duplicate labels when a map is decoded in CDE and dCBOR modes.
It does not support arrays and maps as map labels. They will error out, unless you use maps-as-arrays mode.
Conformance includes checking for shortest form of integers and floats and for dCBOR unification of floats and integers.
* start work on dCBOR decoding enforcement
* checkpoint
* Floating point conformane; ifdef for disabling
* Add more tests
* More dCBOR tests and conformance checks
* More test cases
* Bug fixes and more tests
* Check point stuff
* Map dup and sort order checking kind of working
* more work...
* Finish off UsefulInputBuf_Compare()
* Fix #ifdef fanout
* Fix warnings and #ifdef fan out
* sort & dup checking working and tested
* Fix test ifdef fan out
* Minor fix of one test case
* Another fan out fix
* backout map label checking; doc; test
* Stragglers
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 218d269..d67dd85 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -43,6 +43,7 @@
when who what, where, why
-------- ---- --------------------------------------------------
+ 1/7/2024 llundblade Add UsefulInputBuf_Compare().
10/05/2024 llundblade Add Xxx_OffsetToPointer.
28/02/2024 llundblade Rearrange UsefulOutBuf_Compare().
19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
@@ -1425,7 +1426,7 @@
* and @c start2. It compares bytes at those two starting points until
* they are not equal or @c uLen1 or @c uLen2 is reached. If the
* length of the string given is off the end of the output data, the
- * string will be effectively concated to the data in the output
+ * string will be effectively truncated to the data in the output
* buffer for the comparison.
*
* This returns positive when @c uStart1 lexographically sorts ahead
@@ -1822,6 +1823,32 @@
static void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pUInBuf, size_t uNewLen);
+/**
+ * @brief Compare two ranges of bytes somewhere in the input buffer.
+ *
+ * @param[in] pUInBuf The input buffer.
+ * @param[in] uOffset1 Offset of first range of bytes.
+ * @param[in] uLen1 Length of first range of bytes.
+ * @param[in] uOffset2 Offset of second range of bytes.
+ * @param[in] uLen2 Length of second range of bytes.
+ *
+ * This returns the same as UsefulBuf_Compare().
+ *
+ * If the offset or the length plus offset or a range extends outside
+ * the input buffer, that range of bytes will be considered greater
+ * than the other string. If both are outside this is considered a
+ * degenerate condition and the first string is considered larger.
+ *
+ * This is a somewhat odd function of UsefulInputBuf as it is not used
+ * for consuming data. QCBOR uses it for map order and duplicate
+ * checking.
+ */
+int
+UsefulInputBuf_Compare(UsefulInputBuf *pUInBuf,
+ const size_t uOffset1,
+ const size_t uLen1,
+ const size_t uOffset2,
+ const size_t uLen2);
/*----------------------------------------------------------
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index d64e973..1441ade 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -43,8 +43,6 @@
#endif
#endif
-
-
/**
* @file qcbor_common.h
*
@@ -531,15 +529,33 @@
*/
QCBOR_ERR_NOT_PREFERRED = 79,
- /* Trying to encode something that is discouraged (e.g., 65-bit
+ /** Trying to encode something that is discouraged (e.g., 65-bit
* negative integer) without allowing it by calling
* QCBOREncode_Allow() */
QCBOR_ERR_NOT_ALLOWED = 80,
+
/** QCBORDecode_EnterBstrWrapped() cannot be used on
- * indefinite-length strings because they exist in memory pool for
+ * indefinite-length strings because they exist in the memory pool for
* a @ref QCBORStringAllocate. */
QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING = 81,
+ /** Decoded CBOR is does not conform to preferred serialization. The CBOR head's argument is not encoded in shortest form, or indefinite lengths are used.*/
+ QCBOR_ERR_PREFERRED_CONFORMANCE = 82,
+
+ /** Decoded CBOR does not conform to CDE. This occurs when a map is not sorted. Other
+ * CDE issues are reported as QCBOR_ERR_PREFERRED_CONFORMANCE. */
+ QCBOR_ERR_CDE_CONFORMANCE = 83,
+
+ /** Decoded CBOR does not conform to dCBOR. Floating point numbers are not reduced to integers.
+ * Other issues are reported as either QCBOR_ERR_CDE_CONFORMANCE or QCBOR_ERR_PREFERRED_CONFORMANCE. */
+ QCBOR_ERR_DCBOR_CONFORMANCE = 84,
+
+ /** A map is unsorted and should be for CDE or dCBOR. */
+ QCBOR_ERR_UNSORTED = 85,
+
+ /** Conformance checking requested, preferred serialization disabled, float in the input. */
+ QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 86,
+
/** A range of error codes that can be made use of by the
* caller. QCBOR internally does nothing with these except notice
* that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index dc79912..c2da5a2 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -193,7 +193,34 @@
/** See QCBORDecode_Init() */
QCBOR_DECODE_MODE_MAP_STRINGS_ONLY = 1,
/** See QCBORDecode_Init() */
- QCBOR_DECODE_MODE_MAP_AS_ARRAY = 2
+ QCBOR_DECODE_MODE_MAP_AS_ARRAY = 2,
+ /**
+ * This checks that the input is encoded with preferred
+ * serialization. The checking is performed as each item is
+ * decoded. If no QCBORDecode_GetXxx() is called for an item,
+ * there's no check on that item. Preferred serialization was first
+ * defined in section 4.1 of RFC 8949, but is more sharply in
+ * draft-ietf-cbor-cde. Summarizing, the requirements are: the use
+ * of definite-length encoding only, integers, including string
+ * lengths and tags, must be in shortest form, and floating-point
+ * numbers must be reduced to shortest form all the way to
+ * half-precision. */
+ QCBOR_DECODE_MODE_PREFERRED = 3,
+
+ /** This checks that maps in the input are sorted by label as
+ * described in RFC 8949 section 4.2.1. This also performs
+ * duplicate label checking. This mode adds considerable CPU-time
+ * expense to decoding, though it is probably only of consequence
+ * for large inputs on slow CPUs.
+ *
+ * This also performs all the checks that
+ * QCBOR_DECODE_MODE_PREFERRED does. */
+ QCBOR_DECODE_MODE_CDE = 4,
+
+ /** This requires integer-float unification. It performs all the checks that
+ * QCBOR_DECODE_MODE_CDE does. */
+ QCBOR_DECODE_MODE_DCBOR = 5
+
/* This is stored in uint8_t in places; never add values > 255 */
} QCBORDecodeMode;
@@ -554,7 +581,6 @@
*/
uint16_t uTags[QCBOR_MAX_TAGS_PER_ITEM];
#endif
-
} QCBORItem;
/**
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 95e0a95..a9aebff 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -358,6 +358,7 @@
* - Max items in an array or map when encoding or decoding is
* @ref QCBOR_MAX_ITEMS_IN_ARRAY (typically 65,536).
* - Does not directly support labels in maps other than text strings & integers.
+ * - Traversal, duplicate and sort order checking errors out for labels that are arrays or maps.
* - Does not directly support integer labels beyond whats fits in @c int64_t
* or @c uint64_t.
* - Epoch dates limited to @c INT64_MAX (+/- 292 billion years).
@@ -880,7 +881,7 @@
* @param[in] dNum The double-precision number to add.
*
* Output a double-precision float straight-through with no checking or
- * processing for preferred serializtion, dCBOR or other.
+ * processing for preferred serialization, dCBOR or other.
*
* Error handling is the same as QCBOREncode_AddInt64().
*
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 04d0d11..0a3fb9c 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -383,6 +383,7 @@
uint8_t uDecodeMode;
uint8_t bStringAllocateAll;
uint8_t uLastError; /* QCBORError stuffed into a uint8_t */
+ uint8_t bAllowAllLabels; /* Used internally only, not an external feature yet */
/* See MapTagNumber() for description of how tags are mapped. */
uint64_t auMappedTags[QCBOR_NUM_MAPPED_TAGS];
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index 91ffda1..4177010 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -44,6 +44,7 @@
when who what, where, why
-------- ---- ---------------------------------------------------
+ 1/7/2024 llundblade Add UsefulInputBuf_Compare().
21/05/2024 llundblade Comment formatting and some code tidiness.
28/02/2022 llundblade Rearrange UsefulOutBuf_Compare().
19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
@@ -452,6 +453,41 @@
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
+int
+UsefulInputBuf_Compare(UsefulInputBuf *pUInBuf,
+ const size_t uOffset1,
+ const size_t uLen1,
+ const size_t uOffset2,
+ const size_t uLen2)
+{
+ UsefulBufC UB1;
+ UsefulBufC UB2;
+
+ const size_t uInputSize = UsefulInputBuf_GetBufferLength(pUInBuf);
+
+ /* Careful length check that works even if uLen1 + uOffset1 > SIZE_MAX */
+ if(uOffset1 > uInputSize || uLen1 > uInputSize - uOffset1) {
+ return 1;
+ }
+ UB1.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset1;
+ UB1.len = uLen1;
+
+ /* Careful length check that works even if uLen2 + uOffset2 > SIZE_MAX */
+ if(uOffset2 > uInputSize || uLen2 > uInputSize - uOffset2) {
+ return -1;
+ }
+ UB2.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset2;
+ UB2.len = uLen2;
+
+ return UsefulBuf_Compare(UB1, UB2);
+}
+
+
+/*
+ * Public function -- see UsefulBuf.h
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
int UsefulOutBuf_Compare(UsefulOutBuf *pMe,
const size_t uStart1, const size_t uLen1,
const size_t uStart2, const size_t uLen2)
diff --git a/src/ieee754.c b/src/ieee754.c
index 69bf113..f9b7a3f 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -875,7 +875,7 @@
/* Public function; see ieee754.h */
int
-IEEE754_IsNotStandardDoubleNaN(const double d)
+IEEE754_DoubleHasNaNPayload(const double d)
{
const uint64_t uDouble = CopyDoubleToUint64(d);
const uint64_t uDoubleBiasedExponent = (uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT;
@@ -895,19 +895,20 @@
/* Public function; see ieee754.h */
int
-IEEE754_IsNotStandardSingleNaN(const float f)
+IEEE754_SingleHasNaNPayload(const float f)
{
const uint32_t uSingle = CopyFloatToUint32(f);
const uint32_t uSingleBiasedExponent = (uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT;
/* Cast safe because of mask above; exponents < SINGLE_EXPONENT_MAX */
const int32_t nSingleUnbiasedExponent = (int32_t)uSingleBiasedExponent - SINGLE_EXPONENT_BIAS;
- const uint32_t uSingleleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
+ const uint32_t uSingleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN &&
- uSingleleSignificand != 0 &&
- uSingleleSignificand != SINGLE_QUIET_NAN_BIT) {
+ uSingleSignificand != 0 &&
+ uSingleSignificand != SINGLE_QUIET_NAN_BIT) {
return 1;
} else {
return 0;
}
}
+
diff --git a/src/ieee754.h b/src/ieee754.h
index c893e6f..c82b9b4 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -223,7 +223,7 @@
* represent a NaN.
*/
int
-IEEE754_IsNotStandardDoubleNaN(double dNum);
+IEEE754_DoubleHasNaNPayload(double dNum);
@@ -234,11 +234,11 @@
*
* @returns 0 if a quiet NaN, 1 if it has a payload.
*
- * See IEEE754_IsNotStandardDoubleNaN(). A single precision quiet NaN
+ * See IEEE754_DoubleHasNaNPayload(). A single precision quiet NaN
* is 0x7fc00000.
*/
int
-IEEE754_IsNotStandardSingleNaN(float fNum);
+IEEE754_SingleHasNaNPayload(float fNum);
#endif /* ieee754_h */
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index eabd3e4..05363f0 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -773,6 +773,7 @@
* @brief Decode the CBOR head, the type and argument.
*
* @param[in] pUInBuf The input buffer to read from.
+ * @param[in] bRequirePreferred Require preferred serialization for argument.
* @param[out] pnMajorType The decoded major type.
* @param[out] puArgument The decoded argument.
* @param[out] pnAdditionalInfo The decoded Lower 5 bits of initial byte.
@@ -781,11 +782,10 @@
* @retval QCBOR_ERR_HIT_END Unexpected end of input
*
* This decodes the CBOR "head" that every CBOR data item has. See
- * longer explaination of the head in documentation for
- * QCBOREncode_EncodeHead().
+ * longer description in QCBOREncode_EncodeHead().
*
- * This does the network->host byte order conversion. The conversion
- * here also results in the conversion for floats in addition to that
+ * This does the network to host byte order conversion. The conversion
+ * here also provides the conversion for floats in addition to that
* for lengths, tags and integer values.
*
* The int type is preferred to uint8_t for some variables as this
@@ -794,21 +794,21 @@
*/
static QCBORError
QCBOR_Private_DecodeHead(UsefulInputBuf *pUInBuf,
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ bool bRequirePreferred,
+#endif
int *pnMajorType,
uint64_t *puArgument,
int *pnAdditionalInfo)
{
QCBORError uReturn;
+ uint64_t uArgument;
- /* Get the initial byte that every CBOR data item has and break it
- * down. */
+ /* Get and break down initial byte that every CBOR data item has */
const int nInitialByte = (int)UsefulInputBuf_GetByte(pUInBuf);
const int nTmpMajorType = nInitialByte >> 5;
const int nAdditionalInfo = nInitialByte & 0x1f;
- /* Where the argument accumulates */
- uint64_t uArgument;
-
if(nAdditionalInfo >= LEN_IS_ONE_BYTE && nAdditionalInfo <= LEN_IS_EIGHT_BYTES) {
/* Need to get 1,2,4 or 8 additional argument bytes. Map
* LEN_IS_ONE_BYTE..LEN_IS_EIGHT_BYTES to actual length.
@@ -818,14 +818,46 @@
/* Loop getting all the bytes in the argument */
uArgument = 0;
for(int i = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE]; i; i--) {
- /* This shift and add gives the endian conversion. */
+ /* This shift-and-add gives the endian conversion. */
uArgument = (uArgument << 8) + UsefulInputBuf_GetByte(pUInBuf);
}
+
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ /* If requested, check that argument is in preferred form */
+ if(bRequirePreferred) {
+ uint64_t uMinArgument;
+
+ if(nAdditionalInfo == LEN_IS_ONE_BYTE) {
+ if(uArgument < 24) {
+ uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+ goto Done;
+ }
+ } else {
+ if(nTmpMajorType != CBOR_MAJOR_TYPE_SIMPLE) {
+ /* Check only if not a floating-point number */
+ int nArgLen = aIterate[nAdditionalInfo - LEN_IS_ONE_BYTE - 1];
+ uMinArgument = UINT64_MAX >> ((int)sizeof(uint64_t) - nArgLen) * 8;
+ if(uArgument <= uMinArgument) {
+ uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+ goto Done;
+ }
+ }
+ }
+ }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+
} else if(nAdditionalInfo >= ADDINFO_RESERVED1 && nAdditionalInfo <= ADDINFO_RESERVED3) {
/* The reserved and thus-far unused additional info values */
uReturn = QCBOR_ERR_UNSUPPORTED;
goto Done;
} else {
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ if(bRequirePreferred && nAdditionalInfo == LEN_IS_INDEFINITE) {
+ uReturn = QCBOR_ERR_PREFERRED_CONFORMANCE;
+ goto Done;
+ }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+
/* Less than 24, additional info is argument or 31, an
* indefinite-length. No more bytes to get.
*/
@@ -1137,6 +1169,208 @@
}
+#if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+
+static QCBORError
+QCBORDecode_Private_HalfConformance(const double d, const uint8_t uDecodeMode)
+{
+ struct IEEE754_ToInt ToInt;
+
+ /* Only need to check for conversion to integer because
+ * half-precision is always preferred serialization. Don't
+ * need special checker for half-precision because whole
+ * numbers always convert perfectly from half to double.
+ *
+ * This catches half-precision with NaN payload too.
+ *
+ * The only thing allowed here is a double/half-precision that
+ * can't be converted to anything but a double.
+ */
+ if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+ ToInt = IEEE754_DoubleToInt(d);
+ if(ToInt.type != QCBOR_TYPE_DOUBLE) {
+ return QCBOR_ERR_DCBOR_CONFORMANCE;
+ }
+ }
+
+ return QCBOR_SUCCESS;
+}
+
+
+static QCBORError
+QCBORDecode_Private_SingleConformance(const float f, const uint8_t uDecodeMode)
+{
+ struct IEEE754_ToInt ToInt;
+ IEEE754_union ToSmaller;
+
+ if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+ /* See if it could have been encoded as an integer */
+ ToInt = IEEE754_SingleToInt(f);
+ if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
+ return QCBOR_ERR_DCBOR_CONFORMANCE;
+ }
+
+ /* Make sure there is no NaN payload */
+ if(IEEE754_SingleHasNaNPayload(f)) {
+ return QCBOR_ERR_DCBOR_CONFORMANCE;
+ }
+ }
+
+ /* See if it could have been encoded shorter */
+ if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+ ToSmaller = IEEE754_SingleToHalf(f, true);
+ if(ToSmaller.uSize != sizeof(float)) {
+ return QCBOR_ERR_PREFERRED_CONFORMANCE;
+ }
+ }
+
+ return QCBOR_SUCCESS;
+}
+
+
+static QCBORError
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+{
+ struct IEEE754_ToInt ToInt;
+ IEEE754_union ToSmaller;
+
+ if(uDecodeMode >= QCBOR_DECODE_MODE_DCBOR) {
+ /* See if it could have been encoded as an integer */
+ ToInt = IEEE754_DoubleToInt(d);
+ if(ToInt.type == IEEE754_ToInt_IS_INT || ToInt.type == IEEE754_ToInt_IS_UINT) {
+ return QCBOR_ERR_DCBOR_CONFORMANCE;
+ }
+ /* Make sure there is no NaN payload */
+ if(IEEE754_DoubleHasNaNPayload(d)) {
+ return QCBOR_ERR_DCBOR_CONFORMANCE;
+ }
+ }
+
+ /* See if it could have been encoded shorter */
+ if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+ ToSmaller = IEEE754_DoubleToSmaller(d, true, true);
+ if(ToSmaller.uSize != sizeof(double)) {
+ return QCBOR_ERR_PREFERRED_CONFORMANCE;
+ }
+ }
+
+ return QCBOR_SUCCESS;
+}
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+
+static QCBORError
+QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode)
+{
+ (void)f;
+ if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+ return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
+ } else {
+ return QCBOR_SUCCESS;
+ }
+}
+
+static QCBORError
+QCBORDecode_Private_DoubleConformance(const double d, uint8_t uDecodeMode)
+{
+ (void)d;
+ if(uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED) {
+ return QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE;
+ } else {
+ return QCBOR_SUCCESS;
+ }
+}
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+
+/*
+ * Decode a float
+ */
+static QCBORError
+QCBOR_Private_DecodeFloat(const uint8_t uDecodeMode,
+ const int nAdditionalInfo,
+ const uint64_t uArgument,
+ QCBORItem *pDecodedItem)
+{
+ QCBORError uReturn = QCBOR_SUCCESS;
+ float single;
+
+ (void)single; /* Avoid unused error from various #ifndefs */
+
+ switch(nAdditionalInfo) {
+ case HALF_PREC_FLOAT: /* 25 */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ /* Half-precision is returned as a double. The cast to
+ * uint16_t is safe because the encoded value was 16 bits. It
+ * was widened to 64 bits to be passed in here.
+ */
+ pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument);
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+
+ uReturn = QCBORDecode_Private_HalfConformance(pDecodedItem->val.dfnum, uDecodeMode);
+ if(uReturn != QCBOR_SUCCESS) {
+ break;
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS);
+ break;
+
+ case SINGLE_PREC_FLOAT: /* 26 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+ /* Single precision is normally returned as a double since
+ * double is widely supported, there is no loss of precision,
+ * it makes it easy for the caller in most cases and it can
+ * be converted back to single with no loss of precision
+ *
+ * The cast to uint32_t is safe because the encoded value was
+ * 32 bits. It was widened to 64 bits to be passed in here.
+ */
+ single = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument);
+ uReturn = QCBORDecode_Private_SingleConformance(single, uDecodeMode);
+ if(uReturn != QCBOR_SUCCESS) {
+ break;
+ }
+
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ /* In the normal case, use HW to convert float to
+ * double. */
+ pDecodedItem->val.dfnum = (double)single;
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+ /* Use of float HW is disabled, return as a float. */
+ pDecodedItem->val.fnum = single;
+ pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+
+ /* IEEE754_FloatToDouble() could be used here to return as
+ * a double, but it adds object code and most likely
+ * anyone disabling FLOAT HW use doesn't care about floats
+ * and wants to save object code.
+ */
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+ uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+ break;
+
+ case DOUBLE_PREC_FLOAT: /* 27 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+ pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
+ pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+
+ uReturn = QCBORDecode_Private_DoubleConformance(pDecodedItem->val.dfnum, uDecodeMode);
+ if(uReturn != QCBOR_SUCCESS) {
+ break;
+ }
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+ uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+ break;
+ }
+
+ return uReturn;
+}
+
+
/* Make sure #define value line up as DecodeSimple counts on this. */
#if QCBOR_TYPE_FALSE != CBOR_SIMPLEV_FALSE
#error QCBOR_TYPE_FALSE macro value wrong
@@ -1166,7 +1400,6 @@
#error QCBOR_TYPE_FLOAT macro value wrong
#endif
-
/**
* @brief Decode major type 7 -- true, false, floating-point, break...
*
@@ -1182,7 +1415,8 @@
* type in input.
*/
static QCBORError
-QCBOR_Private_DecodeType7(const int nAdditionalInfo,
+QCBOR_Private_DecodeType7(const uint8_t uDecodeMode,
+ const int nAdditionalInfo,
const uint64_t uArgument,
QCBORItem *pDecodedItem)
{
@@ -1201,55 +1435,9 @@
*/
case HALF_PREC_FLOAT: /* 25 */
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
- /* Half-precision is returned as a double. The cast to
- * uint16_t is safe because the encoded value was 16 bits. It
- * was widened to 64 bits to be passed in here.
- */
- pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uArgument);
- pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
- uReturn = FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS);
- break;
case SINGLE_PREC_FLOAT: /* 26 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
- /* Single precision is normally returned as a double since
- * double is widely supported, there is no loss of precision,
- * it makes it easy for the caller in most cases and it can
- * be converted back to single with no loss of precision
- *
- * The cast to uint32_t is safe because the encoded value was
- * 32 bits. It was widened to 64 bits to be passed in here.
- */
- {
- const float f = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uArgument);
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
- /* In the normal case, use HW to convert float to
- * double. */
- pDecodedItem->val.dfnum = (double)f;
- pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
- /* Use of float HW is disabled, return as a float. */
- pDecodedItem->val.fnum = f;
- pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
-
- /* IEEE754_FloatToDouble() could be used here to return as
- * a double, but it adds object code and most likely
- * anyone disabling FLOAT HW use doesn't care about floats
- * and wants to save object code.
- */
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
- }
-#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
- uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
- break;
-
case DOUBLE_PREC_FLOAT: /* 27 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
- pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
- pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
-#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
- uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
+ uReturn = QCBOR_Private_DecodeFloat(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
break;
case CBOR_SIMPLEV_FALSE: /* 20 */
@@ -1257,12 +1445,19 @@
case CBOR_SIMPLEV_NULL: /* 22 */
case CBOR_SIMPLEV_UNDEF: /* 23 */
case CBOR_SIMPLE_BREAK: /* 31 */
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+ nAdditionalInfo == CBOR_SIMPLEV_UNDEF) {
+ uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
+ goto Done;
+ }
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
break; /* nothing to do */
case CBOR_SIMPLEV_ONEBYTE: /* 24 */
if(uArgument <= CBOR_SIMPLE_BREAK) {
/* This takes out f8 00 ... f8 1f which should be encoded
- * as e0 … f7
+ * as e0 … f7 -- preferred serialization check for simple values.
*/
uReturn = QCBOR_ERR_BAD_TYPE_7;
goto Done;
@@ -1270,8 +1465,16 @@
/* FALLTHROUGH */
default: /* 0-19 */
- pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE;
- /* DecodeHead() will make uArgument equal to
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ if(uDecodeMode >= QCBOR_ENCODE_MODE_DCBOR &&
+ (uArgument < CBOR_SIMPLEV_FALSE || uArgument > CBOR_SIMPLEV_NULL)) {
+ uReturn = QCBOR_ERR_DCBOR_CONFORMANCE;
+ goto Done;
+ }
+#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */
+
+ pDecodedItem->uDataType = QCBOR_TYPE_UKNOWN_SIMPLE;
+ /* QCBOR_Private_DecodeHead() will make uArgument equal to
* nAdditionalInfo when nAdditionalInfo is < 24. This cast is
* safe because the 2, 4 and 8 byte lengths of uNumber are in
* the double/float cases above
@@ -1328,7 +1531,14 @@
/* Decode the "head" that every CBOR item has into the major type,
* argument and the additional info.
*/
- uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf), &nMajorType, &uArgument, &nAdditionalInfo);
+ uReturn = QCBOR_Private_DecodeHead(&(pMe->InBuf),
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ // TODO: make this prettier; will optimizer take out stuff without ifdef?
+ pMe->uDecodeMode >= QCBOR_DECODE_MODE_PREFERRED,
+#endif /* !QCBOR_DISABLE_DECODE_CONFORMANCE */
+ &nMajorType,
+ &uArgument,
+ &nAdditionalInfo);
if(uReturn != QCBOR_SUCCESS) {
return uReturn;
}
@@ -1361,7 +1571,7 @@
case CBOR_MAJOR_TYPE_SIMPLE:
/* Major type 7: float, double, true, false, null... */
- return QCBOR_Private_DecodeType7(nAdditionalInfo, uArgument, pDecodedItem);
+ return QCBOR_Private_DecodeType7(pMe->uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
break;
default:
@@ -1442,7 +1652,6 @@
goto Done;
}
-
/* This is where out-of-place break is detected for the whole
* decoding stack. Break is an error for everything that calls
* QCBORDecode_Private_GetNextFullString(), so the check is
@@ -1762,13 +1971,13 @@
* This also implements maps-as-array mode where a map is treated like
* an array to allow caller to do their own label processing.
*/
-
static QCBORError
QCBORDecode_Private_GetNextMapEntry(QCBORDecodeContext *pMe,
- QCBORItem *pDecodedItem)
+ QCBORItem *pDecodedItem,
+ uint32_t *puLabelEndOffset)
{
QCBORItem LabelItem;
- QCBORError uErr;
+ QCBORError uErr, uErr2;
uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
if(QCBORDecode_IsUnrecoverableError(uErr)) {
@@ -1786,12 +1995,22 @@
/* Decoding a map entry, so the item decoded above was the label */
LabelItem = *pDecodedItem;
+
+ if(puLabelEndOffset != NULL) {
+ /* Cast is OK because lengths are all 32-bit in QCBOR */
+ *puLabelEndOffset = (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+ }
/* Get the value of the map item */
- uErr = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
- if(QCBORDecode_IsUnrecoverableError(uErr)) {
+ uErr2 = QCBORDecode_Private_GetNextTagNumber(pMe, pDecodedItem);
+ if(QCBORDecode_IsUnrecoverableError(uErr2)) {
+ uErr = uErr2;
goto Done;
}
+ if(uErr2 != QCBOR_SUCCESS) {
+ /* The recoverable error for the value overrides the recoverable error for the label, if there was an error for the label */
+ uErr = uErr2;
+ }
/* Combine the label item and value item into one */
pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
@@ -1825,8 +2044,15 @@
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
default:
- uErr = QCBOR_ERR_MAP_LABEL_TYPE;
- goto Done;
+ /* It is possible to skip over labels that are non-aggregate
+ * types like floats, but not to skip over labels that are
+ * arrays or maps. We might eventually handle more label
+ * types like floats as they are not too hard and we now
+ * have QCBOR_DISABLE_NON_INTEGER_LABELS */
+ if(!pMe->bAllowAllLabels || QCBORItem_IsMapOrArray(LabelItem)) {
+ uErr = QCBOR_ERR_MAP_LABEL_TYPE;
+ goto Done;
+ }
}
Done:
@@ -2024,7 +2250,8 @@
static QCBORError
QCBORDecode_Private_GetNextMapOrArray(QCBORDecodeContext *pMe,
bool *pbBreak,
- QCBORItem *pDecodedItem)
+ QCBORItem *pDecodedItem,
+ uint32_t *puLabelEndOffset)
{
QCBORError uReturn;
/* ==== First: figure out if at the end of a traversal ==== */
@@ -2052,7 +2279,7 @@
}
/* ==== Next: not at the end, so get another item ==== */
- uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem);
+ uReturn = QCBORDecode_Private_GetNextMapEntry(pMe, pDecodedItem, puLabelEndOffset);
if(QCBORDecode_IsUnrecoverableError(uReturn)) {
/* Error is so bad that traversal is not possible. */
goto Done;
@@ -2624,7 +2851,7 @@
{
QCBORError uReturn;
- uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem);
+ uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pDecodedItem, NULL);
if(uReturn != QCBOR_SUCCESS) {
goto Done;
}
@@ -2700,6 +2927,223 @@
}
+/**
+ * @brief Consume an entire map or array including its contents.
+ *
+ * @param[in] pMe The decoder context.
+ * @param[in] pItemToConsume The array/map whose contents are to be
+ * consumed.
+ * @param[out] puNextNestLevel The next nesting level after the item was
+ * fully consumed.
+ *
+ * This may be called when @c pItemToConsume is not an array or
+ * map. In that case, this is just a pass through for @c puNextNestLevel
+ * since there is nothing to do.
+ */
+static QCBORError
+QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe,
+ const QCBORItem *pItemToConsume,
+ bool *pbBreak,
+ uint8_t *puNextNestLevel)
+{
+ QCBORError uReturn;
+ QCBORItem Item;
+
+ /* If it is a map or array, this will tell if it is empty. */
+ const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
+
+ if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) {
+ /* There is only real work to do for non-empty maps and arrays */
+
+ /* This works for definite- and indefinite-length maps and
+ * arrays by using the nesting level
+ */
+ do {
+ uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item, NULL);
+ if(QCBORDecode_IsUnrecoverableError(uReturn) ||
+ uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
+ goto Done;
+ }
+ } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
+
+ *puNextNestLevel = Item.uNextNestLevel;
+
+ uReturn = QCBOR_SUCCESS;
+
+ } else {
+ /* pItemToConsume is not a map or array. Just pass the nesting
+ * level through. */
+ *puNextNestLevel = pItemToConsume->uNextNestLevel;
+
+ uReturn = QCBOR_SUCCESS;
+ }
+
+Done:
+ return uReturn;
+}
+
+
+/*
+ *
+ * This consumes the next item. It returns the starting position of
+ * the label and the length of the label. It also returns the nest
+ * level of the item consumed.
+ */
+static QCBORError
+QCBORDecode_Private_GetLabelAndConsume(QCBORDecodeContext *pMe,
+ uint8_t *puNestLevel,
+ size_t *puLabelStart,
+ size_t *puLabelLen)
+{
+ QCBORError uErr;
+ QCBORItem Item;
+ uint8_t uLevel;
+ uint32_t uLabelOffset;
+
+ /* Get the label and consume it should it be complex */
+ *puLabelStart = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+ uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, &Item, &uLabelOffset);
+ if(uErr != QCBOR_SUCCESS) {
+ goto Done;
+ }
+ *puLabelLen = uLabelOffset - *puLabelStart;
+ *puNestLevel = Item.uNestingLevel;
+ uErr = QCBORDecode_Private_ConsumeItem(pMe, &Item, NULL, &uLevel);
+
+Done:
+ return uErr;
+}
+
+
+/* Loop over items in a map until the end of the map looking for
+ * duplicates. This starts at the current position in the map, not at
+ * the beginning of the map.
+ *
+ * This saves and restores the traversal cursor and nest tracking so
+ * they are the same on exit as they were on entry.
+ */
+static QCBORError
+QCBORDecode_Private_CheckDups(QCBORDecodeContext *pMe,
+ const uint8_t uNestLevel,
+ const size_t uCompareLabelStart,
+ const size_t uCompareLabelLen)
+{
+ QCBORError uErr;
+ size_t uLabelStart;
+ size_t uLabelLen;
+ uint8_t uLevel;
+ int nCompare;
+
+ const QCBORDecodeNesting SaveNesting = pMe->nesting;
+ const UsefulInputBuf Save = pMe->InBuf;
+
+ do {
+ uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uLevel, &uLabelStart, &uLabelLen);
+ if(uErr != QCBOR_SUCCESS) {
+ if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
+ uErr = QCBOR_SUCCESS; /* Successful end */
+ }
+ break;
+ }
+
+ if(uLevel != uNestLevel) {
+ break; /* Successful end of loop */
+ }
+
+ /* This check for dups works for labels that are preferred
+ * serialization and are not maps. If the labels are not in
+ * preferred serialization, then the check has to be more
+ * complicated and is type-specific because it uses the decoded
+ * value, not the encoded CBOR. It is further complicated for
+ * maps because the order of items in a map that is a label
+ * doesn't matter when checking that is is the duplicate of
+ * another map that is a label. QCBOR so far only turns on this
+ * dup checking as part of CDE checking which requires preferred
+ * serialization. See 5.6 in RFC 8949.
+ */
+ nCompare = UsefulInputBuf_Compare(&(pMe->InBuf),
+ uCompareLabelStart, uCompareLabelLen,
+ uLabelStart, uLabelLen);
+ if(nCompare == 0) {
+ uErr = QCBOR_ERR_DUPLICATE_LABEL;
+ break;
+ }
+ } while (1);
+
+ pMe->nesting = SaveNesting;
+ pMe->InBuf = Save;
+
+ return uErr;
+}
+
+
+/* This does sort order and duplicate detection on a map. The and all
+ * it's members must be in preferred serialization so the comparisons
+ * work correctly.
+ */
+static QCBORError
+QCBORDecode_Private_CheckMap(QCBORDecodeContext *pMe, const QCBORItem *pMapToCheck)
+{
+ QCBORError uErr;
+ uint8_t uNestLevel;
+ size_t offset2, offset1, length2, length1;
+
+ const QCBORDecodeNesting SaveNesting = pMe->nesting;
+ const UsefulInputBuf Save = pMe->InBuf;
+ pMe->bAllowAllLabels = 1;
+
+ /* This loop runs over all the items in the map once, comparing
+ * each adjacent pair for correct ordering. It also calls CheckDup
+ * on each one which also runs over the remaining items in the map
+ * checking for duplicates. So duplicate checking runs in n^2.
+ */
+
+ offset2 = SIZE_MAX;
+ length2 = SIZE_MAX; // To avoid uninitialized warning
+ while(1) {
+ uErr = QCBORDecode_Private_GetLabelAndConsume(pMe, &uNestLevel, &offset1, &length1);
+ if(uErr != QCBOR_SUCCESS) {
+ if(uErr == QCBOR_ERR_NO_MORE_ITEMS) {
+ uErr = QCBOR_SUCCESS; /* Successful exit from loop */
+ }
+ break;
+ }
+
+ if(uNestLevel < pMapToCheck->uNextNestLevel) {
+ break; /* Successful exit from loop */
+ }
+
+ if(offset2 != SIZE_MAX) {
+ /* Check that the labels are ordered. Check is not done the
+ * first time through the loop when offset2 is unset. Since
+ * this does comparison of the items in encoded form they
+ * must be preferred serialization encoded. See RFC 8949
+ * 4.2.1.
+ */
+ if(UsefulInputBuf_Compare(&(pMe->InBuf), offset2, length2, offset1, length1) > 0) {
+ uErr = QCBOR_ERR_UNSORTED;
+ break;
+ }
+ }
+
+ uErr = QCBORDecode_Private_CheckDups(pMe, pMapToCheck->uNextNestLevel, offset1, length1);
+ if(uErr != QCBOR_SUCCESS) {
+ break;
+ }
+
+ offset2 = offset1;
+ length2 = length1;
+ }
+
+ pMe->bAllowAllLabels = 0;
+ pMe->nesting = SaveNesting;
+ pMe->InBuf = Save;
+
+ return uErr;
+}
+
+
/*
* Public function, see header qcbor/qcbor_decode.h file
*/
@@ -2707,7 +3151,13 @@
QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
{
QCBORError uErr;
- uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem);
+ uErr = QCBORDecode_Private_GetNextTagContent(pMe, pDecodedItem);
+#ifndef QCBOR_DISABLE_CONFORMANCE
+ if(uErr == QCBOR_SUCCESS && pMe->uDecodeMode >= QCBOR_ENCODE_MODE_CDE && pDecodedItem->uDataType == QCBOR_TYPE_MAP) {
+ /* Traverse map checking sort order and for duplicates */
+ uErr = QCBORDecode_Private_CheckMap(pMe, pDecodedItem);
+ }
+#endif /* ! QCBOR_DISABLE_CONFORMANCE */
if(uErr != QCBOR_SUCCESS) {
pDecodedItem->uDataType = QCBOR_TYPE_NONE;
pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
@@ -3143,63 +3593,6 @@
-
-/**
- * @brief Consume an entire map or array including its contents.
- *
- * @param[in] pMe The decoder context.
- * @param[in] pItemToConsume The array/map whose contents are to be
- * consumed.
- * @param[out] puNextNestLevel The next nesting level after the item was
- * fully consumed.
- *
- * This may be called when @c pItemToConsume is not an array or
- * map. In that case, this is just a pass through for @c puNextNestLevel
- * since there is nothing to do.
- */
-static QCBORError
-QCBORDecode_Private_ConsumeItem(QCBORDecodeContext *pMe,
- const QCBORItem *pItemToConsume,
- bool *pbBreak,
- uint8_t *puNextNestLevel)
-{
- QCBORError uReturn;
- QCBORItem Item;
-
- /* If it is a map or array, this will tell if it is empty. */
- const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
-
- if(QCBORItem_IsMapOrArray(*pItemToConsume) && !bIsEmpty) {
- /* There is only real work to do for non-empty maps and arrays */
-
- /* This works for definite- and indefinite-length maps and
- * arrays by using the nesting level
- */
- do {
- uReturn = QCBORDecode_Private_GetNextMapOrArray(pMe, pbBreak, &Item);
- if(QCBORDecode_IsUnrecoverableError(uReturn) ||
- uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
- goto Done;
- }
- } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
-
- *puNextNestLevel = Item.uNextNestLevel;
-
- uReturn = QCBOR_SUCCESS;
-
- } else {
- /* pItemToConsume is not a map or array. Just pass the nesting
- * level through. */
- *puNextNestLevel = pItemToConsume->uNextNestLevel;
-
- uReturn = QCBOR_SUCCESS;
- }
-
-Done:
- return uReturn;
-}
-
-
/*
* Public function, see header qcbor/qcbor_decode.h file
*/
@@ -3648,8 +4041,8 @@
bInMap = DecodeNesting_IsCurrentTypeMap(&(pMe->nesting));
/* Could call GetNext here, but don't need to because this
- * is only interested in arrays and maps. */
- uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem);
+ * is only interested in arrays and maps. TODO: switch to GetNext()? */
+ uErr = QCBORDecode_Private_GetNextMapOrArray(pMe, NULL, pItem, NULL);
if(uErr != QCBOR_SUCCESS) {
pMe->uLastError = (uint8_t)uErr;
return;
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index e54f03e..7d2f7fb 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -681,7 +681,7 @@
struct IEEE754_ToInt IntResult;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(IEEE754_IsNotStandardDoubleNaN(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+ if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
pMe->uError = QCBOR_ERR_NOT_ALLOWED;
return;
}
@@ -730,7 +730,7 @@
struct IEEE754_ToInt IntResult;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(IEEE754_IsNotStandardSingleNaN(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
+ if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
pMe->uError = QCBOR_ERR_NOT_ALLOWED;
return;
}
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index 0235466..6e47f3c 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -888,6 +888,31 @@
return "UIB SetBufferLength failed";
}
+ UsefulBufC CompCheck = UsefulBuf_FROM_SZ_LITERAL("abcd");
+ UsefulInputBuf_Init(&UIB, CompCheck);
+
+ if(UsefulInputBuf_Compare(&UIB, 0, 2, 2, 2) >= 0) {
+ return "UB 1 compared greater than UB2";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 0, 2, 0, 2) != 0) {
+ return "UB1 and UB2 didn't compare equally";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 2, 2, 0, 2) <= 0) {
+ return "UB2 compared less than UB1";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 4, 1, 2, 2) <= 0) {
+ return "Off-the-end UB1 compared as less than UB2";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 0, 5, 2, 2) <= 0) {
+ return "Off-the-end UB1 compared as less than UB2 (second)";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 0, 2, 5, 1) >= 0) {
+ return "Off-the-end UB2 compared as less than UB2";
+ }
+ if(UsefulInputBuf_Compare(&UIB, 0, 2, 2, 3) >= 0) {
+ return "Off-the-end UB2 compared as less than UB2 (second)";
+ }
+
return NULL;
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 14bfb53..191c448 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2558,7 +2558,11 @@
}
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
- if(nIndex == 4) {
+ if(nIndex == 54) {
+ uCBORError = 9; /* For setting break points */
+ }
+
+ if(strncmp("map with map label with non-preferred part", pF->szDescription, 25) == 0) {
uCBORError = 9; /* For setting break points */
}
@@ -2570,7 +2574,7 @@
uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
} while(uCBORError == QCBOR_SUCCESS);
- /* Must get the expected error or the this test fails.
+ /* Must get the expected error or the test fails.
* The data and label type must also be QCBOR_TYPE_NONE.
*/
if(uCBORError != pF->nError ||
@@ -7349,8 +7353,7 @@
},
{
"Decimal Fraction with positive bignum 257 * 10e3",
- {(uint8_t[]){0xC4, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0xC2, 0x42, 0x01, 0x01}, 15},
+ {(uint8_t[]){0xC4, 0x82, 0x03, 0xC2, 0x42, 0x01, 0x01}, 8},
257000,
EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
257000,
@@ -7360,8 +7363,7 @@
},
{
"bigfloat with negative bignum -258 * 2e3",
- {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0xC3, 0x42, 0x01, 0x01}, 15},
+ {(uint8_t[]){0xC5, 0x82, 0x03, 0xC3, 0x42, 0x01, 0x01}, 8},
-2064,
EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
0,
@@ -7371,8 +7373,7 @@
},
{
"bigfloat with positive bignum 257 * 2e3",
- {(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0xC2, 0x42, 0x01, 0x01}, 15},
+ {(uint8_t[]){0xC5, 0x82, 0x03, 0xC2, 0x42, 0x01, 0x01}, 8},
2056,
EXP_AND_MANTISSA_ERROR(QCBOR_SUCCESS),
2056,
@@ -9726,6 +9727,400 @@
}
+
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+#define PREFERRED_ERR QCBOR_ERR_PREFERRED_CONFORMANCE
+#define DCBOR_FLOAT_ERR QCBOR_ERR_DCBOR_CONFORMANCE
+#define HALF_FLOAT_ERR QCBOR_ERR_DCBOR_CONFORMANCE
+#else
+#define PREFERRED_ERR QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE
+#define DCBOR_FLOAT_ERR QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE
+#define HALF_FLOAT_ERR QCBOR_ERR_HALF_PRECISION_DISABLED
+#endif
+#else
+#define PREFERRED_ERR QCBOR_ERR_ALL_FLOAT_DISABLED
+#define DCBOR_FLOAT_ERR QCBOR_ERR_ALL_FLOAT_DISABLED
+#define HALF_FLOAT_ERR QCBOR_ERR_ALL_FLOAT_DISABLED
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+
+
+/* These are all well-formed and valid CBOR, but fail
+ * conformance with preferred, CDE or dCBOR.
+ */
+static const struct DecodeFailTestInput DecodeConformanceFailures[] = {
+ /* --- Major type 0 and 1 not shortest-form --- */
+ { "zero encoded in 2 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x18\x00", 2},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "23 encoded in 2 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x18\x17", 2},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "255 encoded in 3 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x19\x00\xff", 3},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "65535 encoded in 5 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x1a\x00\x00\xff\xff", 5},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "4294967295 encoded in 9 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x1b\x00\x00\x00\x00\xff\xff\xff\xff", 9},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "-24 encoded in 2 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x38\x17", 2},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "-256 encoded in 3 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x39\x00\xff", 3},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "-65536 encoded in 5 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x3a\x00\x00\xff\xff", 5},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "-4294967296 encoded in 9 bytes",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\x3b\x00\x00\x00\x00\xff\xff\xff\xff", 9},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ /* TODO: what to do about this test?
+ { "65-bit negative not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x3b\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },*/
+
+ /* --- Simple values not allowed in dCBOR --- */
+ { "undefined not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf7", 1},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },
+ { "Simple value 0 not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xe0", 1},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },
+ { "Simple value 19 not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf3", 1},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },
+ { "Simple value 32 not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xF8\x20", 2},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },
+ { "Simple value 255 not allowed in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xF8\xff", 2},
+ QCBOR_ERR_DCBOR_CONFORMANCE
+ },
+
+ /* --- Floats not in shortest-form --- */
+ { "1.5 single should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfa\x3f\xc0\x00\x00", 5},
+ PREFERRED_ERR
+ },
+ { "1.5 double should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfb\x3f\xf8\x00\x00\x00\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "8388607.0 double should be single",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xFB\x41\x5F\xFF\xFF\xC0\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "3.0517578125E-5 double should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xFB\x3F\x00\x00\x00\x00\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "255.875 single should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfa\x43\x7f\xe0\x00", 5},
+ PREFERRED_ERR
+ },
+ { "INFINITY single should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfa\x7f\x80\x00\x00", 5},
+ PREFERRED_ERR
+ },
+ { "INFINITY double should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "-INFINITY single should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfa\xff\x80\x00\x00", 5},
+ PREFERRED_ERR
+ },
+ { "-INFINITY double should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfb\xff\xf0\x00\x00\x00\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "NAN single should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfa\x7f\xc0\x00\x00", 5},
+ PREFERRED_ERR
+ },
+ { "NAN double should be half",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00", 9},
+ PREFERRED_ERR
+ },
+ { "NAN half with payload (signaling)",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf9\x7e\x01", 3},
+ HALF_FLOAT_ERR
+ },
+ { "NAN single with payload (signaling)",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xfa\x7f\xc0\x00\x01", 5},
+ DCBOR_FLOAT_ERR
+ },
+ { "NAN double with payload (signaling)",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xfb\x7f\xf8\x00\x00\x00\x00\x00\x01", 9},
+ DCBOR_FLOAT_ERR
+ },
+ { "NAN half with some payload",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf9\x7e\x80", 3},
+ HALF_FLOAT_ERR
+ },
+ { "NAN single with some payload",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xfa\x7f\xc4\x00\x00", 5},
+ DCBOR_FLOAT_ERR
+ },
+ { "NAN double with some payload",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xfb\x7f\xf8\x01\x01\x00\x00\x00\x00", 9},
+ DCBOR_FLOAT_ERR
+ },
+
+ /* --- Floats that should be integers --- */
+ { "0 half not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf9\x00\x00", 3},
+ HALF_FLOAT_ERR
+ },
+ { "0 double not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xfb\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+ DCBOR_FLOAT_ERR
+ },
+ { "-0 half not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xf9\x80\x00", 3},
+ HALF_FLOAT_ERR
+ },
+ { "18446744073709550000 double not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xFB\x43\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ DCBOR_FLOAT_ERR
+ },
+ { "4294967295 double not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xFB\x41\xEF\xFF\xFF\xFF\xE0\x00\x00", 9},
+ DCBOR_FLOAT_ERR
+ },
+ { "65535 single not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xFA\x47\x7F\xFF\x00", 5},
+ DCBOR_FLOAT_ERR
+ },
+ { "255 half not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xF9\x5C\x00", 3},
+ HALF_FLOAT_ERR
+ },
+ { "-1 half not an integer in dCBOR",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xF9\xBC\x00", 3},
+ HALF_FLOAT_ERR
+ },
+
+ /* --- Various non-shortest-form CBOR arguments ---*/
+ { "byte string length not-shortest form",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x59\x00\x01\x99", 4},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "array length not-shortest form",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x9a\x00\x00\x00\x02\x05\x06", 7},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "tag number not shortest-form",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xd9\x00\xff\x00", 4},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+#if !defined(QCBOR_DISABLE_TAGS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+ { "tag number on lable not shortest-form",
+ QCBOR_DECODE_MODE_PREFERRED,
+ {"\xA3\xC1\x00\x61\x61\xD8\x01\x00\x61\x62\xD9\x00\x01\x00\x61\x63", 16},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+#endif
+
+ /* --- Indefinite lengths --- */
+ { "indefinite-length byte string",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x5f\x62\x68\x69\xff", 5},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "indefinite-length text string",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x7f\x62\x68\x69\xff", 5},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "indefinite-length array",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\x9f\xff", 2},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+ { "indefinite-length map",
+ QCBOR_DECODE_MODE_DCBOR,
+ {"\xbf\xff", 2},
+ QCBOR_ERR_PREFERRED_CONFORMANCE
+ },
+
+ /* --- Unsorted maps --- */
+#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
+ { "simple unsorted map with text labels",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa2\x61\x62\x00\x61\x61\x01", 7},
+ QCBOR_ERR_UNSORTED
+ },
+#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
+ { "reverse sorted map with integer labels",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa5\x19\x03\xE8\xf6\x18\x64\xf6\x00\xf6\x29\xf6\x3A\x00\x01\x86\x9f\xf6", 18},
+ QCBOR_ERR_UNSORTED
+ },
+ {"map with out-of-order labels that are arrays",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xA3\x83\x00\x01\x02\x61\x63\x83\x00\x01\x00\x61\x61\x83\x00\x01\x01\x61\x62", 19},
+ QCBOR_ERR_MAP_LABEL_TYPE
+ },
+#if !defined(QCBOR_DISABLE_TAGS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
+ { "unsorted map with labels of all types including arrays and maps",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xA8\x80\x07\xC1\x18\x58\x02\x64\x74\x65\x78\x74\x03\x01\x01\xA0\x04"
+ "\x42\x78\x78\x05\xF5\x06\xFB\x40\x21\x8A\x3D\x70\xA3\xD7\x0A\x07", 33},
+ QCBOR_ERR_MAP_LABEL_TYPE
+ },
+ { "unsorted map with labels of all non-aggregate types",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xA6\xC1\x18\x58\x02\x64\x74\x65\x78\x74\x03\x01\x01"
+ "\x42\x78\x78\x05\xF5\x06\xFB\x40\x21\x8A\x3D\x70\xA3\xD7\x0A\x07", 29},
+ QCBOR_ERR_UNSORTED
+ },
+ {"map with out-of-order labels that have tags",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xA3\xC1\x18\x63\x61\x61\xD9\x07\xD0\x18\x63\x61\x63\xD8\x30\x18\x63\x61\x62", 19},
+ QCBOR_ERR_UNSORTED
+ },
+#endif
+
+ /* --- Maps with dup labels --- */
+ { "simple map with dup integer labels",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa2\x00\x00\x00\x00", 5},
+ QCBOR_ERR_DUPLICATE_LABEL
+ },
+ {"map with dup map labels",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xA3\xA1\x03\x03\x61\x61\xA1\x02\x02\x61\x62\xA1\x03\x03\x61\x63", 16},
+ QCBOR_ERR_MAP_LABEL_TYPE
+ },
+
+ /* --- Maps with bad labels --- */
+ { "map with invalid label",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa1\x1c\x01", 3},
+ QCBOR_ERR_UNSUPPORTED
+ },
+
+ { "map with array label with invalid parts",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa1\x81\x1c\x01", 4},
+ QCBOR_ERR_MAP_LABEL_TYPE
+ },
+
+ { "map with map label with non-preferred part",
+ QCBOR_DECODE_MODE_CDE,
+ {"\xa1\xa1\x19\x00\x00\x01\x02", 7},
+ QCBOR_ERR_MAP_LABEL_TYPE
+ }};
+
+
+static UsefulBufC CorrectlySorted[] = {
+ /* This one is correctly sorted, but is not correct preferred serialization. QCBOR checks
+ * the sort order of the map without checking the preferred serialization of the
+ * map items, so this test passes. */
+ {"\xa4\x01\x61\x61\xf9\x3C\x00\x61\x62\xFA\x3F\x80\x00\x00\x61\x63\xFB\x3F\xF0\x00\x00\x00\x00\x00\x00\x61\x64", 27},
+ {"\xa3\x00\x61\x61\x01\x61\x62\xa3\x0c\x61\x78\x0b\x61\x79\x0a\x61\x7a\x61\x63", 19},
+ {"\xA3\xE0\x61\x61\xF5\x61\x62\xFB\x3F\xF1\x99\x99\x99\x99\x99\x9A\x61\x63", 18},
+ {"\xa2\x00\x00\x01\x01", 5},
+ {"\xA0", 1},
+ NULLUsefulBufC
+};
+
+
+
+int32_t
+DecodeConformanceTests(void)
+{
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ QCBORError uErr;
+ uint32_t uTestIndex;
+
+ for(uTestIndex = 0; UsefulBuf_IsNULLC(CorrectlySorted[uTestIndex]); uTestIndex++) {
+ QCBORDecode_Init(&DCtx, CorrectlySorted[uTestIndex], QCBOR_DECODE_MODE_CDE);
+
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(1, uTestIndex, uErr);
+ }
+ }
+
+ /* Make sure EnterMap is handling errors */
+ QCBORDecode_Init(&DCtx,UsefulBuf_FROM_SZ_LITERAL("\xa2\x00\x00\x00\x00"), QCBOR_DECODE_MODE_CDE);
+ QCBORDecode_EnterMap(&DCtx, &Item);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 5000;
+ }
+
+ return ProcessDecodeFailures(DecodeConformanceFailures,
+ C_ARRAY_COUNT(DecodeConformanceFailures, struct DecodeFailTestInput));
+
+}
+
+
+
+
+
#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
struct PreciseNumberConversion {
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 4c4c0a6..a6d5837 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2024, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -319,6 +319,11 @@
int32_t CBORTestIssue134(void);
+/*
+ * Test the decode checking features for dCBOR, CDE and preferred.
+ */
+int32_t DecodeConformanceTests(void);
+
int32_t PreciseNumbersTest(void);
int32_t ErrorHandlingTests(void);
diff --git a/test/run_tests.c b/test/run_tests.c
index e24506a..3fe4905 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,10 +67,18 @@
static test_entry s_tests[] = {
+#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
+ TEST_ENTRY(DecodeConformanceTests),
+#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
+ TEST_ENTRY(ErrorHandlingTests),
+ TEST_ENTRY(OpenCloseBytesTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(GetMapAndArrayTest),
TEST_ENTRY(TellTests),
TEST_ENTRY(ParseMapAsArrayTest),
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ TEST_ENTRY(ArrayNestingTest3),
+#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
TEST_ENTRY(SpiffyDateDecodeTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(ErrorHandlingTests),
@@ -86,7 +94,7 @@
TEST_ENTRY(IndefiniteLengthNestTest),
TEST_ENTRY(IndefiniteLengthArrayMapTest),
TEST_ENTRY(NestedMapTestIndefLen),
-#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
TEST_ENTRY(SimpleValueDecodeTests),
TEST_ENTRY(DecodeFailureTests),
@@ -95,11 +103,6 @@
TEST_ENTRY(MapEncodeTest),
TEST_ENTRY(ArrayNestingTest1),
TEST_ENTRY(ArrayNestingTest2),
-
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- TEST_ENTRY(ArrayNestingTest3),
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-
TEST_ENTRY(EncodeDateTest),
TEST_ENTRY(SimpleValuesTest1),
TEST_ENTRY(IntegerValuesTest1),
@@ -165,8 +168,6 @@
TEST_ENTRY(PeekAndRewindTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
-
-
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
TEST_ENTRY(ExponentAndMantissaDecodeTests),
#ifndef QCBOR_DISABLE_TAGS