New encode feature allows direct writing of byte string value (#137)

New features for UsefulBuf and for QCBOREncode allows direct writing to the output buffer. For QCBOREncode, this is the direct writing of the value of a byte string. This allows it to be written in chunks or be the output buffer of some function like symmetric encryption.


* fix grammer in security policy

* Half-way start a encoding feature to write byte string values into output

* Add documentation for OpenBstr

* UsefulBuf_Advance mostly working and testing

* OpenBytes() is mostly working and somewhat tested

* Finish up OpenBytes -- error handing, documentation...

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 1662fcc..e7f4d4f 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-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
  Copyright (c) 2021, Arm Limited. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,7 @@
 
  when         who             what, where, why
  --------     ----            --------------------------------------------------
+ 4/11/2022    llundblade      Add GetOutPlace and Advance to UsefulOutBuf.
  9/21/2021    llundbla        Clarify UsefulOutBuf size calculation mode
  8/8/2021     dthaler/llundbla Work with C++ without compiler extensions
  5/11/2021    llundblade      Improve comments and comment formatting.
@@ -1287,6 +1288,53 @@
 
 
 /**
+ * @brief Returns pointer and length of the output buffer not yet used.
+ *
+ * @param[in] pUOutBuf  Pointer to the @ref UsefulOutBuf.
+ *
+ * @return pointer and length of output buffer not used.
+ *
+ * This is an escape that allows the caller to write directly
+ * to the output buffer without any checks. This doesn't
+ * change the output buffer or state. It just returns a pointer
+ * and length of the bytes remaining.
+ *
+ * This is useful to avoid having the bytes to be added all
+ * in a contiguous buffer. Its use can save memory. A good
+ * example is in the COSE encrypt implementation where
+ * the output of the symmetric cipher can go directly
+ * into the output buffer, rather than having to go into
+ * an intermediate buffer.
+ *
+ * See UsefulOutBuf_Advance() which is used to tell
+ * UsefulOutBuf how much was written.
+ *
+ * Warning: this bypasses the buffer safety provided by
+ * UsefulOutBuf!
+ */
+static inline UsefulBuf
+UsefulOutBuf_GetOutPlace(UsefulOutBuf *pUOutBuf);
+
+
+/**
+ * @brief Advance the amount output assuming it was written by the caller.
+ *
+ * @param[in] pUOutBuf  Pointer to the @ref UsefulOutBuf.
+ * @param[in] uAmount  The amount to advance.
+ *
+ * This advances the position in the output buffer
+ * by \c uAmount. This assumes that the
+ * caller has written \c uAmount to the pointer obtained
+ * with UsefulOutBuf_GetOutPlace().
+ *
+ * Warning: this bypasses the buffer safety provided by
+ * UsefulOutBuf!
+ */
+void
+UsefulOutBuf_Advance(UsefulOutBuf *pUOutBuf, size_t uAmount);
+
+
+/**
  *  @brief Returns the resulting valid data in a UsefulOutBuf
  *
  *  @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf.
@@ -2132,6 +2180,22 @@
 }
 
 
+static inline UsefulBuf UsefulOutBuf_GetOutPlace(UsefulOutBuf *pUOutBuf)
+{
+   UsefulBuf R;
+
+   R.len = UsefulOutBuf_RoomLeft(pUOutBuf);
+   if(R.len > 0) {
+      R.ptr = (uint8_t *)pUOutBuf->UB.ptr + pUOutBuf->data_len;
+   } else {
+      R.ptr = NULL;
+   }
+
+   return R;
+}
+
+
+
 
 static inline void UsefulInputBuf_Init(UsefulInputBuf *pMe, UsefulBufC UB)
 {
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index b9353b7..6c95c4b 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -1,6 +1,6 @@
 /*==============================================================================
  Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
  Copyright (c) 2021, Arm Limited.
  All rights reserved.
 
@@ -314,7 +314,8 @@
    QCBOR_ERR_TOO_MANY_CLOSES = 7,
 
    /** During encoding the number of array or map opens was not
-       matched by the number of closes. */
+       matched by the number of closes. Also occurs with opened
+       byte strings that are not closed. */
    QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN = 8,
 
 #define QCBOR_START_OF_NOT_WELL_FORMED_ERRORS 9
@@ -489,7 +490,7 @@
        non-CBOR reason */
    QCBOR_ERR_CALLBACK_FAIL = 38,
 
-   /** This error code is deprecated. Instead, 
+   /** This error code is deprecated. Instead,
        \ref QCBOR_ERR_HALF_PRECISION_DISABLED,
        \ref QCBOR_ERR_HW_FLOAT_DISABLED or \ref QCBOR_ERR_ALL_FLOAT_DISABLED
        is returned depending on the specific floating-point functionality
@@ -524,7 +525,11 @@
 
    /** Floating point support is completely turned off, encoding/decoding
        floating point numbers is not possible. */
-   QCBOR_ERR_ALL_FLOAT_DISABLED = 46
+   QCBOR_ERR_ALL_FLOAT_DISABLED = 46,
+
+   /** During encode, opening a byte string while a byte string is open
+       is not allowed. . */
+   QCBOR_ERR_OPEN_BYTE_STRING = 47
 
    /* 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 0abc594..8b2ed90 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -841,6 +841,62 @@
 
 
 /**
+ @brief Set up to write a byte string value directly to encoded output.
+
+ @param[in] pCtx     The encoding context to add the bytes to.
+ @param[out] pPlace  Pointer and length of place to write byte string value.
+
+ QCBOREncode_AddBytes() is the normal way to encode a byte string.
+ This is for special cases and by passes some of the pointer safety.
+
+ The purpose of this is to output the bytes that make up a byte string
+ value directly to the QCBOR output buffer so you don't need to have a
+ copy of it in memory. This is particularly useful if the byte string
+ is large, for example, the encrypted payload of a COSE_Encrypt
+ message. The payload encryption algorithm can output directly to the
+ encoded CBOR buffer, perhaps by making it the output buffer
+ for some function (e.g. symmetric encryption) or by multiple writes.
+
+ The pointer in \c pPlace is where to start writing. Writing is just
+ copying bytes to the location by the pointer in \c pPlace.  Writing
+ past the length in \c pPlace will be writing off the end of the
+ output buffer.
+
+ If there is no room in the output buffer @ref NULLUsefulBuf will be
+ returned and there is no need to call QCBOREncode_CloseBytes().
+
+ The byte string must be closed by calling QCBOREncode_CloseBytes().
+
+ Warning: this bypasses some of the usual checks provided by QCBOR
+ against writing off the end of the encoded output buffer.
+ */
+void
+QCBOREncode_OpenBytes(QCBOREncodeContext *pCtx, UsefulBuf *pPlace);
+
+static void
+QCBOREncode_OpenBytesInMapSZ(QCBOREncodeContext *pCtx, const char *szLabel, UsefulBuf *pPlace);
+
+static void
+QCBOREncode_OpenBytesInMapN(QCBOREncodeContext *pCtx, int64_t nLabel, UsefulBuf *pPlace);
+
+
+/**
+  @brief Close out a byte string written directly to encoded output.
+
+  @param[in] pCtx      The encoding context to add the bytes to.
+  @param[out] uAmount  The number of bytes written, the length of the byte string.
+
+ This closes out a call to QCBOREncode_OpenBytes().  This inserts a
+ CBOR header at the front of the byte string value to make it a
+ well-formed byte string.
+
+ If there was no call to QCBOREncode_OpenBytes() then
+ @ref QCBOR_ERR_TOO_MANY_CLOSES is set.
+ */
+void QCBOREncode_CloseBytes(QCBOREncodeContext *pCtx, size_t uAmount);
+
+
+/**
  @brief Add a binary UUID to the encoded output.
 
  @param[in] pCtx   The encoding context to add the UUID to.
@@ -2371,6 +2427,20 @@
 }
 
 static inline void
+QCBOREncode_OpenBytesInMapSZ(QCBOREncodeContext *pMe, const char *szLabel, UsefulBuf *pPlace)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_OpenBytes(pMe, pPlace);
+}
+
+static inline void
+QCBOREncode_OpenBytesInMapN(QCBOREncodeContext *pMe, int64_t nLabel, UsefulBuf *pPlace)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_OpenBytes(pMe, pPlace);
+}
+
+static inline void
 QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pMe, UsefulBufC Bytes)
 {
     QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY, Bytes);
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 9f189e6..9a2a720 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -320,6 +320,8 @@
 #define CBOR_MAJOR_NONE_TYPE_RAW  9
 #define CBOR_MAJOR_NONE_TAG_LABEL_REORDER 10
 #define CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY 11
+#define CBOR_MAJOR_NONE_TYPE_OPEN_BSTR 12
+
 
 // Add this to types to indicate they are to be encoded as indefinite lengths
 #define QCBOR_INDEFINITE_LEN_TYPE_MODIFIER 0x80