Duplicate map label detection for encoding (#209)
This adds duplicate map label detection during encoding as part of sorting.
There was a lot of rework of map sorting.
UsefulOutBuf_Compare() was changed to behave differently and more universally.
* Duplicate detection for encoding
* rework UsefulOutBuf_Compare and test
* Dup detection seems to be working
* Final tidy-up
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 37b1c89..e4a7bf6 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -1287,7 +1287,7 @@
/**
- * @brief Decoded next item to get its length.
+ * @brief Decoded next item to get its lengths.
*
* Decode the next item in map no matter what type it is. It works
* recursively when an item is a map or array It returns offset just
@@ -1299,16 +1299,25 @@
* stuff that came in from outside. We still want a check for safety
* in case of bugs here, but it is OK to report end of input on error.
*/
-static uint32_t
+struct ItemLens {
+ uint32_t uLabelLen;
+ uint32_t uItemLen;
+};
+
+static struct ItemLens
QCBOREncode_Private_DecodeNextInMap(QCBOREncodeContext *pMe, uint32_t uStart)
{
- UsefulInputBuf InBuf;
- UsefulBufC EncodedMapBytes;
- QCBORError uCBORError;
+ UsefulInputBuf InBuf;
+ UsefulBufC EncodedMapBytes;
+ QCBORError uCBORError;
+ struct ItemLens Result;
+
+ Result.uLabelLen = 0;
+ Result.uItemLen = 0;
EncodedMapBytes = UsefulOutBuf_OutUBufOffset(&(pMe->OutBuf), uStart);
if(UsefulBuf_IsNULLC(EncodedMapBytes)) {
- return 0;
+ return Result;
}
UsefulInputBuf_Init(&InBuf, EncodedMapBytes);
@@ -1316,15 +1325,22 @@
/* This is always used on maps, so consume two, the label and the value */
uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
if(uCBORError) {
- return 0;
- }
- uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
- if(uCBORError) {
- return 0;
+ return Result;
}
/* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
- return (uint32_t)UsefulInputBuf_Tell(&InBuf);
+ Result.uLabelLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ uCBORError = QCBOR_Private_ConsumeNext(&InBuf);
+ if(uCBORError) {
+ Result.uLabelLen = 0;
+ return Result;
+ }
+
+ Result.uItemLen = (uint32_t)UsefulInputBuf_Tell(&InBuf);
+
+ /* Cast is safe because this is QCBOR which limits sizes to UINT32_MAX */
+ return Result;
}
@@ -1342,12 +1358,13 @@
static void
QCBOREncode_Private_SortMap(QCBOREncodeContext *pMe, uint32_t uStart)
{
- bool bSwapped;
- int nComparison;
- uint32_t uLen2;
- uint32_t uLen1;
- uint32_t uStart1;
- uint32_t uStart2;
+ bool bSwapped;
+ int nComparison;
+ uint32_t uStart1;
+ uint32_t uStart2;
+ struct ItemLens Lens1;
+ struct ItemLens Lens2;
+
if(pMe->uError != QCBOR_SUCCESS) {
return;
@@ -1367,31 +1384,38 @@
* sizes are not the same and overlap may occur in the bytes being
* swapped.
*/
- do {
- uLen1 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart);
- if(uLen1 == 0) {
+ do { /* Loop until nothing was swapped */
+ Lens1 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart);
+ if(Lens1.uLabelLen == 0) {
/* It's an empty map. Nothing to do. */
break;
}
uStart1 = uStart;
- uStart2 = uStart1 + uLen1;
+ uStart2 = uStart1 + Lens1.uItemLen;
bSwapped = false;
while(1) {
- uLen2 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart2);
- if(uLen2 == 0) {
+ Lens2 = QCBOREncode_Private_DecodeNextInMap(pMe, uStart2);
+ if(Lens2.uLabelLen == 0) {
break;
}
- nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf), uStart1, uStart2);
+ nComparison = UsefulOutBuf_Compare(&(pMe->OutBuf),
+ uStart1, Lens1.uLabelLen,
+ uStart2, Lens2.uLabelLen);
if(nComparison < 0) {
- UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + uLen2);
- uStart1 = uStart1 + uLen2;
+ UsefulOutBuf_Swap(&(pMe->OutBuf), uStart1, uStart2, uStart2 + Lens2.uItemLen);
+ uStart1 = uStart1 + Lens2.uItemLen; /* item 2 now in position of item 1 */
+ /* Lens1 is still valid as Lens1 for the next loop */
bSwapped = true;
- } else {
+ } else if(nComparison > 0) {
uStart1 = uStart2;
+ Lens1 = Lens2;
+ } else /* nComparison == 0 */ {
+ pMe->uError = QCBOR_ERR_DUPLICATE_LABEL;
+ return;
}
- uStart2 = uStart2 + uLen2;
+ uStart2 = uStart2 + Lens2.uItemLen;
}
} while(bSwapped);
}
@@ -1400,7 +1424,8 @@
/*
* Public functions for closing sorted maps. See qcbor/qcbor_encode.h
*/
-void QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
+void
+QCBOREncode_CloseAndSortMap(QCBOREncodeContext *pMe)
{
uint32_t uStart;
@@ -1419,7 +1444,8 @@
/*
* Public functions for closing sorted maps. See qcbor/qcbor_encode.h
*/
-void QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
+void
+QCBOREncode_CloseAndSortMapIndef(QCBOREncodeContext *pMe)
{
uint32_t uStart;