Add QCBOREncode_CancelBstrWrap() (#110)
* cancel bstr seems to be working
* test and documentation
* documentation and error track fixes
* tidy tests
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 7e52a26..8ccc8b4 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -502,6 +502,10 @@
indefinite length map or array in the input CBOR. */
QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 44,
+ /** Trying to cancel a byte string wrapping after items have been
+ added to it. */
+ QCBOR_ERR_CANNOT_CANCEL = 45,
+
/* This is stored in uint8_t; never add values > 255 */
} QCBORError;
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 016226b..969deee 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -1774,6 +1774,25 @@
/**
+ * @brief Cancel byte string wrapping.
+ *
+ * @param[in] pCtx The encoding context.
+ *
+ * This cancels QCBOREncode_BstrWrap() making tghe encoding as if it
+ * were never called.
+ *
+ * WARNING: This does not work on QCBOREncode_BstrWrapInMap()
+ * or QCBOREncode_BstrWrapInMapN() and there is no error detection
+ * of an attempt at their use.
+ *
+ * This only works if nothing has been added into the wrapped byte
+ * string. If something has been added, this sets the error
+ * @ref QCBOR_ERR_CANNOT_CANCEL.
+ */
+void QCBOREncode_CancelBstrWrap(QCBOREncodeContext *pCtx);
+
+
+/**
@brief Add some already-encoded CBOR bytes.
@param[in] pCtx The encoding context to add the already-encode CBOR to.
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index c93be05..7c248ef 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -114,6 +114,14 @@
return QCBOR_SUCCESS;
}
+inline static void Nesting_Decrement(QCBORTrackNesting *pNesting)
+{
+ /* No error check for going below 0 here needed because this
+ * is only used by QCBOREncode_CancelBstrWrap() and it checks
+ * the nesting level before calling this. */
+ pNesting->pCurrentNesting->uCount--;
+}
+
inline static uint16_t Nesting_GetCount(QCBORTrackNesting *pNesting)
{
/* The nesting count recorded is always the actual number of
@@ -886,7 +894,42 @@
/*
- * Public functions for closing arrays and maps. See qcbor/qcbor_encode.h
+ * Public function for canceling a bstr wrap. See qcbor/qcbor_encode.h
+ */
+void QCBOREncode_CancelBstrWrap(QCBOREncodeContext *pMe)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(pMe->uError == QCBOR_SUCCESS) {
+ if(!Nesting_IsInNest(&(pMe->nesting))) {
+ pMe->uError = QCBOR_ERR_TOO_MANY_CLOSES;
+ return;
+ } else if(Nesting_GetMajorType(&(pMe->nesting)) != CBOR_MAJOR_TYPE_BYTE_STRING) {
+ pMe->uError = QCBOR_ERR_CLOSE_MISMATCH;
+ return;
+ }
+ const size_t uCurrent = UsefulOutBuf_GetEndPosition(&(pMe->OutBuf));
+ if(pMe->nesting.pCurrentNesting->uStart != uCurrent) {
+ pMe->uError = QCBOR_ERR_CANNOT_CANCEL;
+ return;
+ }
+ }
+ /* QCBOREncode_CancelBstrWrap() can't correctly undo
+ * QCBOREncode_BstrWrapInMap() or QCBOREncode_BstrWrapInMapN(). It
+ * can't undo the labels they add. It also doesn't catch the error
+ * of using it this way. QCBOREncode_CancelBstrWrap() is used
+ * infrequently and the the result is incorrect CBOR, not a
+ * security hole, so no extra code or state is added to handle this
+ * condition.
+ */
+#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+ Nesting_Decrease(&(pMe->nesting));
+ Nesting_Decrement(&(pMe->nesting));
+}
+
+
+/*
+ * Public function for closing arrays and maps. See qcbor/qcbor_encode.h
*/
void QCBOREncode_CloseMapOrArrayIndefiniteLength(QCBOREncodeContext *me, uint8_t uMajorType)
{
@@ -911,7 +954,7 @@
/*
- * Public functions to finish and get the encoded result. See qcbor/qcbor_encode.h
+ * Public function to finish and get the encoded result. See qcbor/qcbor_encode.h
*/
QCBORError QCBOREncode_Finish(QCBOREncodeContext *me, UsefulBufC *pEncodedCBOR)
{
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 18116cb..8b41af3 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -1745,8 +1745,10 @@
*/
static const uint8_t spExpectedTypeAndLen[] = {0x81, 0x58, 0x25};
+static const uint8_t spExpectedForBstrWrapCancel[] = {0x82, 0x19, 0x01, 0xC3, 0x18, 0x2A};
+
/*
- Very basic bstr wrapping test
+ * bstr wrapping test
*/
int BstrWrapTest()
{
@@ -1806,6 +1808,63 @@
return -7;
}
+ // Fourth test, cancelling a byte string
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, 451);
+
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_CancelBstrWrap(&EC);
+
+
+ QCBOREncode_AddUInt64(&EC, 42);
+ QCBOREncode_CloseArray(&EC);
+ if(QCBOREncode_Finish(&EC, &Encoded)) {
+ return -8;
+ }
+ if(CheckResults(Encoded, spExpectedForBstrWrapCancel)) {
+ return -9;
+ }
+
+ QCBORError uErr;
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ // Fifth test, failed cancelling
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenArray(&EC);
+ QCBOREncode_AddUInt64(&EC, 451);
+
+ QCBOREncode_BstrWrap(&EC);
+ QCBOREncode_AddUInt64(&EC, 99);
+ QCBOREncode_CancelBstrWrap(&EC);
+
+ QCBOREncode_AddUInt64(&EC, 42);
+ QCBOREncode_CloseArray(&EC);
+ uErr = QCBOREncode_Finish(&EC, &Encoded);
+ if(uErr != QCBOR_ERR_CANNOT_CANCEL) {
+ return -10;
+ }
+#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+ // Sixth test, another cancel, but the error is not caught
+ // This use will produce unintended CBOR. The error
+ // is not caught because it would require tracking state
+ // for QCBOREncode_BstrWrapInMapN.
+ QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+ QCBOREncode_OpenMap(&EC);
+ QCBOREncode_AddUInt64ToMapN(&EC, 451, 88);
+
+ QCBOREncode_BstrWrapInMapN(&EC, 55);
+ QCBOREncode_CancelBstrWrap(&EC);
+
+ QCBOREncode_CloseMap(&EC);
+ uErr = QCBOREncode_Finish(&EC, &Encoded);
+ if(uErr != QCBOR_SUCCESS) {
+ return -11;
+ }
+
return 0;
}