New Rewind method for entered maps...; bug fix for entering tagged wrapping byte strings
The new QCBORDecode_Rewind method allows the pre-order traversal cursor to be reset to the beginning of an entered array, map or byte string. If nothing was entered, then the reset is to the initial state, the beginning of the input encoded CBOR.
This also fixes a bug that prevented tagged wrapping byte strings from being entered.
This also adds a method to UsefulBuf and UsefulInBuf to convert a pointer to an offset.
* first version of rewind
* Make rewind work when not bounded and reset error state
* Rewind working for maps and arrays, but not byte strings
* rewind almost working for byte strings
* src/qcbor_decode.c
* minor tidying
* rewinding wrapped byte strings works, but needs more tests
* Full testing of UsefulBuf pointer to offset functions
* tidy up rewind; tests all passing
* fix bug entering tagged byte-string wrapped CBOR; entering indefinite-length byte-string wrapped is still broken
* one more test; give up on entering indef length wrapping strings
* spelling fix
* ifdefs for disabling tests
* refactor rewind
* more nits
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 6fe33a1..6c057fe 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -1,6 +1,6 @@
/*============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2020, Laurence Lundblade.
+ Copyright (c) 2018-2021, Laurence Lundblade.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -41,6 +41,7 @@
when who what, where, why
-------- ---- --------------------------------------------------
+ 2/17/2021 llundblade Add method to go from a pointer to an offset.
1/25/2020 llundblade Add some casts so static anlyzers don't complain.
5/21/2019 llundblade #define configs for efficient endianness handling.
5/16/2019 llundblade Add UsefulOutBuf_IsBufferNULL().
@@ -589,6 +590,17 @@
size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind);
+/**
+ @brief Convert a pointer to an offset with bounds checking.
+
+ @param[in] UB Pointer to the UsefulInputBuf.
+ @param[in] p Pointer to convert to offset.
+
+ @return SIZE_MAX if @c p is out of range, the byte offset if not.
+*/
+static inline size_t UsefulBuf_PointerToOffset(UsefulBufC UB, const void *p);
+
+
#if 1 // NOT_DEPRECATED
/** Deprecated macro; use @ref UsefulBuf_FROM_SZ_LITERAL instead */
#define SZLiteralToUsefulBufC(szString) \
@@ -1332,6 +1344,17 @@
/**
+ @brief Convert a pointer to an offset with bounds checking.
+
+ @param[in] pUInBuf Pointer to the UsefulInputBuf.
+ @param[in] p Pointer to convert to offset.
+
+ @return SIZE_MAX if @c p is out of range, the byte offset if not.
+*/
+static inline size_t UsefulInputBuf_PointerToOffset(UsefulInputBuf *pUInBuf, const void *p);
+
+
+/**
@brief Get pointer to bytes out of the input buffer.
@param[in] pUInBuf Pointer to the UsefulInputBuf.
@@ -1626,6 +1649,28 @@
}
+static inline size_t UsefulBuf_PointerToOffset(UsefulBufC UB, const void *p)
+{
+ if(UB.ptr == NULL) {
+ return SIZE_MAX;
+ }
+
+ if(p < UB.ptr) {
+ /* given pointer is before start of buffer */
+ return SIZE_MAX;
+ }
+
+ // Cast to size_t (from ptrdiff_t) is OK because of check above
+ const size_t uOffset = (size_t)((uint8_t *)p - (uint8_t *)UB.ptr);
+
+ if(uOffset >= UB.len) {
+ /* given pointer is off the end of the buffer */
+ return SIZE_MAX;
+ }
+
+ return uOffset;
+}
+
static inline uint32_t UsefulBufUtil_CopyFloatToUint32(float f)
{
@@ -1993,6 +2038,12 @@
}
+static inline size_t UsefulInputBuf_PointerToOffset(UsefulInputBuf *pUInBuf, const void *p)
+{
+ return UsefulBuf_PointerToOffset(pUInBuf->UB, p);
+}
+
+
static inline UsefulBufC UsefulInputBuf_GetUsefulBuf(UsefulInputBuf *pMe, size_t uNum)
{
const void *pResult = UsefulInputBuf_GetBytes(pMe, uNum);
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 960e810..95f41a0 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -881,7 +881,7 @@
This is useful for looking ahead to determine the type
of a data item to know which type-specific spiffy decode
- function to call.
+ function to call.
*/
QCBORError
QCBORDecode_PeekNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 5ddc16f..abd6ba2 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -152,11 +152,11 @@
QCBOR_TYPE_MAP or QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP_AS_ARRAY
for 2).
- Item tracking is either be for definite or indefinite length
+ Item tracking is either for definite or indefinite-length
maps/arrays. For definite lengths, the total count and items
- unconsumed are tracked. For indefinite length, uTotalCount is
+ unconsumed are tracked. For indefinite-length, uTotalCount is
QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH (UINT16_MAX) and there
- is no per-item count of members. For indefinite length maps and
+ is no per-item count of members. For indefinite-length maps and
arrays, uCountCursor is UINT16_MAX if not consumed and zero if
it is consumed in the pre-order traversal. Additionally, if
entered in bounded mode, uCountCursor is
@@ -185,9 +185,12 @@
uint32_t uStartOffset;
} ma; /* for maps and arrays */
struct {
- uint32_t uEndOfBstr;
- uint32_t uPreviousEndOffset;
- } bs; /* for top-level sequence and bstr wrapped CBOR */
+ /* The end of the input before the bstr was entered so that
+ * it can be restored when the bstr is exited. */
+ uint32_t uSavedEndOffset;
+ /* The beginning of the bstr so that it can be rewound. */
+ uint32_t uBstrStartOffset;
+ } bs; /* for top-level sequence and bstr-wrapped CBOR */
} u;
} pLevels[QCBOR_MAX_ARRAY_NESTING1+1],
*pCurrent,
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index 0ea6a71..d1a391d 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -151,7 +151,7 @@
example, to decode an epoch date tag the content must be an integer
or floating-point value.
- If the parameter indicates it should not be a tag
+ If the parameter indicates it should not be a tag
(@ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG), then
@ref QCBOR_ERR_UNEXPECTED_TYPE set if it is a tag or the type of the
encoded CBOR is not what is expected. In the example of an epoch
@@ -736,6 +736,25 @@
/**
+ @brief Reset traversal cursor to start of map, array, byte-string
+ wrapped CBOR or start of input.
+
+ @param[in] pCtx The decode context.
+
+ If an array, map or wrapping byte string has been entered this sets
+ the traversal cursor to its beginning. If several arrays, maps or
+ byte strings have been entered, this sets the traversal cursor to the
+ beginning of the one most recently entered.
+
+ If no map or array has been entered, this resets the traversal cursor
+ to the beginning of the input CBOR.
+
+ This also resets the error state.
+ */
+void QCBORDecode_Rewind(QCBORDecodeContext *pCtx);
+
+
+/**
@brief Get an item in map by label and type.
@param[in] pCtx The decode context.
@@ -743,8 +762,8 @@
@param[in] uQcborType The QCBOR type. One of @c QCBOR_TYPE_XXX.
@param[out] pItem The returned item.
- A map must have been entered to use this. If not @ref QCBOR_ERR_MAP_NOT_ENTERED is
- set.
+ A map must have been entered to use this. If not @ref
+ QCBOR_ERR_MAP_NOT_ENTERED is set.
The map is searched for an item of the requested label and type.
@ref QCBOR_TYPE_ANY can be given to search for the label without
@@ -1512,6 +1531,9 @@
CBOR. QCBORDecode_ExitBstrWrapped() must be called to resume processing
CBOR outside the wrapped CBOR.
+ This does not (currently) work on indefinite-length strings. The
+ (confusing) error @ref QCBOR_ERR_INPUT_TOO_LARGE will be set.
+
If @c pBstr is not @c NULL the pointer and length of the wrapped
CBOR will be returned. This is usually not needed, but sometimes
useful, particularly in the case of verifying signed data like the