boot_serial: Port encoding to use cddl-gen

Non-generated. Using cbor_encode directly
This removes the dependence on TinyCBOR.

Fixes #978

Signed-off-by: Øyvind Rønningstad <oyvind.ronningstad@nordicsemi.no>
diff --git a/boot/boot_serial/pkg.yml b/boot/boot_serial/pkg.yml
index f1431c7..19f9a32 100644
--- a/boot/boot_serial/pkg.yml
+++ b/boot/boot_serial/pkg.yml
@@ -28,7 +28,6 @@
 pkg.deps:
     - "@apache-mynewt-core/hw/hal"
     - "@apache-mynewt-core/kernel/os"
-    - "@apache-mynewt-core/encoding/tinycbor"
     - "@apache-mynewt-core/encoding/base64"
     - "@mcuboot/boot/mynewt/flash_map_backend"
     - "@mcuboot/boot/mynewt/boot_uart"
diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c
index 5fa794a..88c0029 100644
--- a/boot/boot_serial/src/boot_serial.c
+++ b/boot/boot_serial/src/boot_serial.c
@@ -25,6 +25,7 @@
 #include "sysflash/sysflash.h"
 
 #include "bootutil/bootutil_log.h"
+#include "cbor_encode.h"
 
 #ifdef __ZEPHYR__
 #include <power/reboot.h>
@@ -33,8 +34,6 @@
 #include <drivers/flash.h>
 #include <sys/crc.h>
 #include <sys/base64.h>
-#include <tinycbor/cbor.h>
-#include <tinycbor/cbor_buf_reader.h>
 #else
 #include <bsp/bsp.h>
 #include <hal/hal_system.h>
@@ -42,7 +41,6 @@
 #include <os/os_cputime.h>
 #include <crc/crc16.h>
 #include <base64/base64.h>
-#include <tinycbor/cbor.h>
 #endif /* __ZEPHYR__ */
 
 #include <flash_map_backend/flash_map_backend.h>
@@ -97,28 +95,13 @@
 
 static char bs_obuf[BOOT_SERIAL_OUT_MAX];
 
-static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
-  int len);
 static void boot_serial_output(void);
 
-static struct cbor_encoder_writer bs_writer = {
-    .write = bs_cbor_writer
+static cbor_state_backups_t dummy_backups;
+static cbor_state_t cbor_state = {
+    .backups = &dummy_backups
 };
-static CborEncoder bs_root;
-static CborEncoder bs_rsp;
 
-int
-bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
-{
-    if (cew->bytes_written + len > sizeof(bs_obuf)) {
-        return CborErrorOutOfMemory;
-    }
-
-    memcpy(&bs_obuf[cew->bytes_written], data, len);
-    cew->bytes_written += len;
-
-    return 0;
-}
 
 /*
  * Convert version into string without use of snprintf().
@@ -172,17 +155,15 @@
 static void
 bs_list(char *buf, int len)
 {
-    CborEncoder images;
-    CborEncoder image;
     struct image_header hdr;
     uint8_t tmpbuf[64];
-    int slot, area_id;
+    uint32_t slot, area_id;
     const struct flash_area *fap;
     uint8_t image_index;
 
-    cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
-    cbor_encode_text_stringz(&bs_rsp, "images");
-    cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
+    map_start_encode(&cbor_state, 1);
+    tstrx_put(&cbor_state, "images");
+    list_start_encode(&cbor_state, 5);
     image_index = 0;
     IMAGES_ITER(image_index) {
         for (slot = 0; slot < 2; slot++) {
@@ -201,24 +182,24 @@
             }
             flash_area_close(fap);
 
-            cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
+            map_start_encode(&cbor_state, 20);
 
 #if (BOOT_IMAGE_NUMBER > 1)
-            cbor_encode_text_stringz(&image, "image");
-            cbor_encode_int(&image, image_index);
+            tstrx_put(&cbor_state, "image");
+            uintx32_put(&cbor_state, image_index);
 #endif
 
-            cbor_encode_text_stringz(&image, "slot");
-            cbor_encode_int(&image, slot);
-            cbor_encode_text_stringz(&image, "version");
+            tstrx_put(&cbor_state, "slot");
+            uintx32_put(&cbor_state, slot);
+            tstrx_put(&cbor_state, "version");
 
             bs_list_img_ver((char *)tmpbuf, sizeof(tmpbuf), &hdr.ih_ver);
-            cbor_encode_text_stringz(&image, (char *)tmpbuf);
-            cbor_encoder_close_container(&images, &image);
+            tstrx_put_term(&cbor_state, (char *)tmpbuf);
+            map_end_encode(&cbor_state, 20);
         }
     }
-    cbor_encoder_close_container(&bs_rsp, &images);
-    cbor_encoder_close_container(&bs_root, &bs_rsp);
+    list_end_encode(&cbor_state, 5);
+    map_end_encode(&cbor_state, 1);
     boot_serial_output();
 }
 
@@ -229,10 +210,10 @@
 bs_upload(char *buf, int len)
 {
     const uint8_t *img_data = NULL;
-    long long int off = UINT_MAX;
+    long long int off = UINT64_MAX;
     size_t img_blen = 0;
     uint8_t rem_bytes;
-    long long int data_len = UINT_MAX;
+    long long int data_len = UINT64_MAX;
     int img_num;
     size_t slen;
     const struct flash_area *fap = NULL;
@@ -255,7 +236,7 @@
      */
 
     struct Upload upload;
-    size_t decoded_len;
+    uint32_t decoded_len;
     bool result = cbor_decode_Upload((const uint8_t *)buf, len, &upload, &decoded_len);
 
     if (!result || (len != decoded_len)) {
@@ -286,7 +267,7 @@
         }
     }
 
-    if (off == UINT_MAX || img_data == NULL) {
+    if (off == UINT64_MAX || img_data == NULL) {
         /*
          * Offset must be set in every block.
          */
@@ -403,14 +384,14 @@
 
 out:
     BOOT_LOG_INF("RX: 0x%x", rc);
-    cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
-    cbor_encode_text_stringz(&bs_rsp, "rc");
-    cbor_encode_int(&bs_rsp, rc);
+    map_start_encode(&cbor_state, 10);
+    tstrx_put(&cbor_state, "rc");
+    uintx32_put(&cbor_state, rc);
     if (rc == 0) {
-        cbor_encode_text_stringz(&bs_rsp, "off");
-        cbor_encode_uint(&bs_rsp, curr_off);
+        tstrx_put(&cbor_state, "off");
+        uintx32_put(&cbor_state, curr_off);
     }
-    cbor_encoder_close_container(&bs_root, &bs_rsp);
+    map_end_encode(&cbor_state, 10);
 
     boot_serial_output();
     flash_area_close(fap);
@@ -422,10 +403,10 @@
 static void
 bs_empty_rsp(char *buf, int len)
 {
-    cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
-    cbor_encode_text_stringz(&bs_rsp, "rc");
-    cbor_encode_int(&bs_rsp, 0);
-    cbor_encoder_close_container(&bs_root, &bs_rsp);
+    map_start_encode(&cbor_state, 10);
+    tstrx_put(&cbor_state, "rc");
+    uintx32_put(&cbor_state, 0);
+    map_end_encode(&cbor_state, 10);
     boot_serial_output();
 }
 
@@ -472,8 +453,9 @@
     buf += sizeof(*hdr);
     len -= sizeof(*hdr);
 
-    bs_writer.bytes_written = 0;
-    cbor_encoder_init(&bs_root, &bs_writer, 0);
+    cbor_state.payload_mut = (uint8_t *)bs_obuf;
+    cbor_state.payload_end = (const uint8_t *)bs_obuf
+                             + sizeof(bs_obuf);
 
     /*
      * Limited support for commands.
@@ -516,7 +498,7 @@
     char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
 
     data = bs_obuf;
-    len = bs_writer.bytes_written;
+    len = (uint32_t)cbor_state.payload_mut - (uint32_t)bs_obuf;
 
     bs_hdr->nh_op++;
     bs_hdr->nh_flags = 0;
diff --git a/boot/boot_serial/src/cbor_encode.c b/boot/boot_serial/src/cbor_encode.c
new file mode 100644
index 0000000..c2e9191
--- /dev/null
+++ b/boot/boot_serial/src/cbor_encode.c
@@ -0,0 +1,440 @@
+/*
+ * This file has been copied from the cddl-gen submodule.
+ * Commit 8f9358a0b4b0e9b0cd579f0988056ef0b60760e4
+ */
+
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include "cbor_encode.h"
+#include "cbor_common.h"
+
+_Static_assert((sizeof(size_t) == sizeof(void *)),
+	"This code needs size_t to be the same length as pointers.");
+
+uint8_t get_additional(uint32_t len, uint8_t value0)
+{
+	switch(len) {
+		case 0: return value0;
+		case 1: return 24;
+		case 2: return 25;
+		case 3: return 25;
+		case 4: return 26;
+		case 5: return 26;
+		case 6: return 26;
+		case 7: return 26;
+		case 8: return 27;
+	}
+
+	cbor_assert(false, NULL);
+	return 0;
+}
+
+static bool encode_header_byte(cbor_state_t *state,
+	cbor_major_type_t major_type, uint8_t additional)
+{
+	if ((state->payload + 1) > state->payload_end) {
+		FAIL();
+	}
+
+	cbor_assert(additional < 32, NULL);
+
+	*(state->payload_mut++) = (major_type << 5) | (additional & 0x1F);
+	return true;
+}
+
+/** Encode a single value.
+ */
+static bool value_encode_len(cbor_state_t *state, cbor_major_type_t major_type,
+		const void *const input, uint32_t result_len)
+{
+	uint8_t *u8_result  = (uint8_t *)input;
+
+	if ((state->payload + 1 + result_len) > state->payload_end) {
+		FAIL();
+	}
+
+	if (!encode_header_byte(state, major_type,
+				get_additional(result_len, u8_result[0]))) {
+		FAIL();
+	}
+	state->payload_mut--;
+	cbor_trace();
+	state->payload_mut++;
+
+#ifdef CONFIG_BIG_ENDIAN
+	memcpy(state->payload_mut, u8_result, result_len);
+	state->payload_mut += result_len;
+#else
+	for (; result_len > 0; result_len--) {
+		*(state->payload_mut++) = u8_result[result_len - 1];
+	}
+#endif
+
+	state->elem_count++;
+	return true;
+}
+
+
+static uint32_t get_result_len(const void *const input, uint32_t max_result_len)
+{
+	uint8_t *u8_result  = (uint8_t *)input;
+
+	for (; max_result_len > 0; max_result_len--) {
+		if (u8_result[max_result_len - 1] != 0) {
+			break;
+		}
+	}
+	if ((max_result_len == 1) && (u8_result[0] <= VALUE_IN_HEADER)) {
+		max_result_len = 0;
+	}
+
+	return max_result_len;
+}
+
+
+static bool value_encode(cbor_state_t *state, cbor_major_type_t major_type,
+		const void *const input, uint32_t max_result_len)
+{
+	cbor_assert(max_result_len != 0, "0-length result not supported.\n");
+	return value_encode_len(state, major_type, input,
+				get_result_len(input, max_result_len));
+}
+
+
+bool intx32_put(cbor_state_t *state, int32_t input)
+{
+	cbor_major_type_t major_type;
+
+	if (input < 0) {
+		major_type = CBOR_MAJOR_TYPE_NINT;
+		/* Convert from CBOR's representation. */
+		input = -1 - input;
+	} else {
+		major_type = CBOR_MAJOR_TYPE_PINT;
+		input = input;
+	}
+
+	if (!value_encode(state, major_type, &input, 4)) {
+		FAIL();
+	}
+
+	return true;
+}
+
+bool intx32_encode(cbor_state_t *state, const int32_t *input)
+{
+	return intx32_put(state, *input);
+}
+
+
+static bool uint32_encode(cbor_state_t *state, const uint32_t *input,
+		cbor_major_type_t major_type)
+{
+	if (!value_encode(state, major_type, input, 4)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+bool uintx32_encode(cbor_state_t *state, const uint32_t *input)
+{
+	if (!uint32_encode(state, input, CBOR_MAJOR_TYPE_PINT)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+bool uintx32_put(cbor_state_t *state, uint32_t input)
+{
+	if (!uint32_encode(state, &input, CBOR_MAJOR_TYPE_PINT)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+static bool strx_start_encode(cbor_state_t *state,
+		const cbor_string_type_t *input, cbor_major_type_t major_type)
+{
+	if (input->value && ((get_result_len(&input->len, sizeof(input->len))
+			+ 1 + input->len + (size_t)state->payload)
+			> (size_t)state->payload_end)) {
+		FAIL();
+	}
+	if (!uint32_encode(state, &input->len, major_type)) {
+		FAIL();
+	}
+
+	return true;
+}
+
+
+static bool primx_encode(cbor_state_t *state, uint32_t input)
+{
+	if (!uint32_encode(state, &input, CBOR_MAJOR_TYPE_PRIM)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+static uint32_t remaining_str_len(cbor_state_t *state)
+{
+	uint32_t max_len = (size_t)state->payload_end - (size_t)state->payload;
+	uint32_t result_len = get_result_len(&max_len, sizeof(uint32_t));
+	return max_len - result_len - 1;
+}
+
+
+bool bstrx_cbor_start_encode(cbor_state_t *state, const cbor_string_type_t *result)
+{
+	if (!new_backup(state, 0)) {
+		FAIL();
+	}
+
+	uint32_t max_len = remaining_str_len(state);
+
+	/* Encode a dummy header */
+	if (!uint32_encode(state, &max_len,
+			CBOR_MAJOR_TYPE_BSTR)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+bool bstrx_cbor_end_encode(cbor_state_t *state)
+{
+	const uint8_t *payload = state->payload;
+
+	if (!restore_backup(state, FLAG_RESTORE | FLAG_DISCARD, 0xFFFFFFFF)) {
+		FAIL();
+	}
+	cbor_string_type_t value;
+
+	value.value = state->payload_end - remaining_str_len(state);
+	value.len = (size_t)payload - (size_t)value.value;
+
+	/* Reencode header of list now that we know the number of elements. */
+	if (!bstrx_encode(state, &value)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+static bool strx_encode(cbor_state_t *state,
+		const cbor_string_type_t *input, cbor_major_type_t major_type)
+{
+	if (!strx_start_encode(state, input, major_type)) {
+		FAIL();
+	}
+	if (state->payload_mut != input->value) {
+		memmove(state->payload_mut, input->value, input->len);
+	}
+	state->payload += input->len;
+	return true;
+}
+
+
+bool bstrx_encode(cbor_state_t *state, const cbor_string_type_t *input)
+{
+	return strx_encode(state, input, CBOR_MAJOR_TYPE_BSTR);
+}
+
+
+bool tstrx_encode(cbor_state_t *state, const cbor_string_type_t *input)
+{
+	return strx_encode(state, input, CBOR_MAJOR_TYPE_TSTR);
+}
+
+
+static bool list_map_start_encode(cbor_state_t *state, uint32_t max_num,
+		cbor_major_type_t major_type)
+{
+#ifdef CDDL_CBOR_CANONICAL
+	if (!new_backup(state, 0)) {
+		FAIL();
+	}
+
+	/* Encode dummy header with max number of elements. */
+	if (!uint32_encode(state, &max_num, major_type)) {
+		FAIL();
+	}
+	state->elem_count--; /* Because of dummy header. */
+#else
+	if (!encode_header_byte(state, major_type, 31)) {
+		FAIL();
+	}
+#endif
+	return true;
+}
+
+
+bool list_start_encode(cbor_state_t *state, uint32_t max_num)
+{
+	return list_map_start_encode(state, max_num, CBOR_MAJOR_TYPE_LIST);
+}
+
+
+bool map_start_encode(cbor_state_t *state, uint32_t max_num)
+{
+	return list_map_start_encode(state, max_num, CBOR_MAJOR_TYPE_MAP);
+}
+
+
+bool list_map_end_encode(cbor_state_t *state, uint32_t max_num,
+			cbor_major_type_t major_type)
+{
+#ifdef CDDL_CBOR_CANONICAL
+	uint32_t list_count = ((major_type == CBOR_MAJOR_TYPE_LIST) ?
+					state->elem_count
+					: (state->elem_count / 2));
+
+	const uint8_t *payload = state->payload;
+	uint32_t max_header_len = get_result_len(&max_num, 4);
+	uint32_t header_len = get_result_len(&list_count, 4);
+
+	if (!restore_backup(state, FLAG_RESTORE | FLAG_DISCARD, 0xFFFFFFFF)) {
+		FAIL();
+	}
+
+	cbor_print("list_count: %d\r\n", list_count);
+
+	/* Reencode header of list now that we know the number of elements. */
+	if (!(uint32_encode(state, &list_count, major_type))) {
+		FAIL();
+	}
+
+	if (max_header_len != header_len) {
+		const uint8_t *start = state->payload + max_header_len - header_len;
+		uint32_t body_size = payload - start;
+		memmove(state->payload_mut,
+			state->payload + max_header_len - header_len,
+			body_size);
+		/* Reset payload pointer to end of list */
+		state->payload += body_size;
+	} else {
+		/* Reset payload pointer to end of list */
+		state->payload = payload;
+	}
+#else
+	if (!encode_header_byte(state, CBOR_MAJOR_TYPE_PRIM, 31)) {
+		FAIL();
+	}
+#endif
+	return true;
+}
+
+
+bool list_end_encode(cbor_state_t *state, uint32_t max_num)
+{
+	return list_map_end_encode(state, max_num, CBOR_MAJOR_TYPE_LIST);
+}
+
+
+bool map_end_encode(cbor_state_t *state, uint32_t max_num)
+{
+	return list_map_end_encode(state, max_num, CBOR_MAJOR_TYPE_MAP);
+}
+
+
+bool nilx_put(cbor_state_t *state, const void *input)
+{
+	(void)input;
+	return primx_encode(state, 22);
+}
+
+
+bool boolx_encode(cbor_state_t *state, const bool *input)
+{
+	if (!primx_encode(state, *input + BOOL_TO_PRIM)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+bool boolx_put(cbor_state_t *state, bool input)
+{
+	if (!primx_encode(state, input + BOOL_TO_PRIM)) {
+		FAIL();
+	}
+	return true;
+}
+
+
+bool double_encode(cbor_state_t *state, double *input)
+{
+	if (!value_encode(state, CBOR_MAJOR_TYPE_PRIM, input,
+			sizeof(*input))) {
+		FAIL();
+	}
+
+	return true;
+}
+
+
+bool double_put(cbor_state_t *state, double input)
+{
+	return double_encode(state, &input);
+}
+
+
+bool any_encode(cbor_state_t *state, void *input)
+{
+	return nilx_put(state, input);
+}
+
+
+bool tag_encode(cbor_state_t *state, uint32_t tag)
+{
+	if (!value_encode(state, CBOR_MAJOR_TYPE_TAG, &tag, sizeof(tag))) {
+		FAIL();
+	}
+	state->elem_count--;
+
+	return true;
+}
+
+
+bool multi_encode(uint32_t min_encode,
+		uint32_t max_encode,
+		const uint32_t *num_encode,
+		cbor_encoder_t encoder,
+		cbor_state_t *state,
+		const void *input,
+		uint32_t result_len)
+{
+	if (!PTR_VALUE_IN_RANGE(uint32_t, num_encode, NULL, &max_encode)) {
+		FAIL();
+	}
+	for (uint32_t i = 0; i < *num_encode; i++) {
+		if (!encoder(state, (const uint8_t *)input + i*result_len)) {
+			FAIL();
+		}
+	}
+	cbor_print("Found %zu elements.\n", *num_encode);
+	return true;
+}
+
+
+bool present_encode(const uint32_t *present,
+		cbor_encoder_t encoder,
+		cbor_state_t *state,
+		const void *input)
+{
+	uint32_t num_encode = *present;
+	bool retval = multi_encode(0, 1, &num_encode, encoder, state, input, 0);
+	return retval;
+}
diff --git a/boot/boot_serial/src/cbor_encode.h b/boot/boot_serial/src/cbor_encode.h
new file mode 100644
index 0000000..ca0a8f8
--- /dev/null
+++ b/boot/boot_serial/src/cbor_encode.h
@@ -0,0 +1,150 @@
+/*
+ * This file has been copied from the cddl-gen submodule.
+ * Commit 8f9358a0b4b0e9b0cd579f0988056ef0b60760e4
+ */
+
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef CBOR_ENCODE_H__
+#define CBOR_ENCODE_H__
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "cbor_common.h"
+
+
+/** Encode a PINT/NINT into a int32_t.
+ *
+ * @param[inout] state        The current state of the decoding.
+ * @param[out]   result       Where to place the encoded value.
+ *
+ * @retval true   Everything is ok.
+ * @retval false  If the payload is exhausted.
+ */
+bool intx32_encode(cbor_state_t *state, const int32_t *input);
+bool intx32_put(cbor_state_t *state, int32_t result);
+
+/** Encode a PINT into a uint32_t. */
+bool uintx32_encode(cbor_state_t *state, const uint32_t *result);
+bool uintx32_put(cbor_state_t *state, uint32_t result);
+
+/** Encode a BSTR header.
+ *
+ * The rest of the string can be encoded as CBOR.
+ * A state backup is created to keep track of the element count.
+ *
+ * @retval true   Header encoded correctly
+ * @retval false  Header encoded incorrectly, or backup failed.
+ */
+bool bstrx_cbor_start_encode(cbor_state_t *state, const cbor_string_type_t *result);
+
+/** Finalize encoding a CBOR-encoded BSTR.
+ *
+ * Restore element count from backup.
+ */
+bool bstrx_cbor_end_encode(cbor_state_t *state);
+
+/** Encode a BSTR, */
+bool bstrx_encode(cbor_state_t *state, const cbor_string_type_t *result);
+
+/** Encode a TSTR. */
+bool tstrx_encode(cbor_state_t *state, const cbor_string_type_t *result);
+
+#define tstrx_put(state, string) \
+	tstrx_encode(state, &(cbor_string_type_t){.value = (const uint8_t *)string, len = (sizeof(string) - 1)})
+
+#define tstrx_put_term(state, string) \
+	tstrx_encode(state, &(cbor_string_type_t){.value = (const uint8_t *)string, len = strlen((const char *)string)})
+
+/** Encode a LIST header.
+ *
+ * The contents of the list can be decoded via subsequent function calls.
+ * A state backup is created to keep track of the element count.
+ */
+bool list_start_encode(cbor_state_t *state, uint32_t max_num);
+
+/** Encode a MAP header. */
+bool map_start_encode(cbor_state_t *state, uint32_t max_num);
+
+/** Encode end of a LIST. Do some checks and deallocate backup. */
+bool list_end_encode(cbor_state_t *state, uint32_t max_num);
+
+/** Encode end of a MAP. Do some checks and deallocate backup. */
+bool map_end_encode(cbor_state_t *state, uint32_t max_num);
+
+/** Encode a "nil" primitive value. result should be NULL. */
+bool nilx_put(cbor_state_t *state, const void *result);
+
+/** Encode a boolean primitive value. */
+bool boolx_encode(cbor_state_t *state, const bool *result);
+bool boolx_put(cbor_state_t *state, bool result);
+
+/** Encode a float */
+bool float_encode(cbor_state_t *state, double *result);
+bool float_put(cbor_state_t *state, double result);
+
+/** Dummy encode "any": Encode a "nil". input should be NULL. */
+bool any_encode(cbor_state_t *state, void *input);
+
+/** Encode a tag. */
+bool tag_encode(cbor_state_t *state, uint32_t tag);
+
+/** Encode 0 or more elements with the same type and constraints.
+ *
+ * @details This must not necessarily encode all elements in a list. E.g. if
+ *          the list contains 3 INTS between 0 and 100 followed by 0 to 2 BSTRs
+ *          with length 8, that could be done with:
+ *
+ *          @code{c}
+ *              uint32_t int_min = 0;
+ *              uint32_t int_max = 100;
+ *              uint32_t bstr_size = 8;
+ *              uint32_t ints[3];
+ *              cbor_string_type_t bstrs[2] = <initialize here>;
+ *              bool res;
+ *
+ *              res = list_start_encode(state, 5);
+ *              // check res
+ *              res = multi_encode(3, 3, &num_encode, uintx32_encode, state,
+ *                           ints, 4);
+ *              // check res
+ *              res = multi_encode(0, 2, &num_encode, strx_encode, state,
+ *                           bstrs, sizeof(cbor_string_type_t));
+ *              // check res
+ *              res = list_end_encode(state, 5);
+ *              // check res
+ *          @endcode
+ *
+ * @param[in]  min_encode    The minimum acceptable number of elements.
+ * @param[in]  max_encode    The maximum acceptable number of elements.
+ * @param[in]  num_encode    The actual number of elements.
+ * @param[in]  encoder       The encoder function to call under the hood. This
+ *                           function will be called with the provided arguments
+ *                           repeatedly until the function fails (returns false)
+ *                           or until it has been called @p max_encode times.
+ *                           result is moved @p result_len bytes for each call
+ *                           to @p encoder, i.e. @p result refers to an array
+ *                           of result variables.
+ * @param[in]  input         Source of the encoded values. Must be an array
+ *                           of length at least @p max_encode.
+ * @param[in]  result_len    The length of the result variables. Must be the
+ *                           length of the elements in result.
+ *
+ * @retval true   If at least @p min_encode variables were correctly encoded.
+ * @retval false  If @p encoder failed before having encoded @p min_encode
+ *                values.
+ */
+bool multi_encode(uint32_t min_encode, uint32_t max_encode, const uint32_t *num_encode,
+		cbor_encoder_t encoder, cbor_state_t *state, const void *input,
+		uint32_t result_len);
+
+bool present_encode(const uint32_t *present,
+		cbor_encoder_t encoder,
+		cbor_state_t *state,
+		const void *input);
+
+#endif /* CBOR_ENCODE_H__ */
diff --git a/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c b/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c
index 53a7199..83ba50a 100644
--- a/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c
+++ b/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c
@@ -18,7 +18,6 @@
  */
 
 #include <flash_map_backend/flash_map_backend.h>
-#include <tinycbor/cborconstants_p.h>
 
 #include "boot_test.h"
 
diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt
index 29cff0a..259e903 100644
--- a/boot/zephyr/CMakeLists.txt
+++ b/boot/zephyr/CMakeLists.txt
@@ -239,6 +239,7 @@
   zephyr_sources(${BOOT_DIR}/boot_serial/src/boot_serial.c)
   zephyr_sources(${BOOT_DIR}/boot_serial/src/serial_recovery_cbor.c)
   zephyr_sources(${BOOT_DIR}/boot_serial/src/cbor_decode.c)
+  zephyr_sources(${BOOT_DIR}/boot_serial/src/cbor_encode.c)
   zephyr_sources(${BOOT_DIR}/boot_serial/src/cbor_common.c)
 
   zephyr_include_directories(${BOOT_DIR}/bootutil/include)
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index 932110d..aa4ee1f 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -469,7 +469,6 @@
 	select SERIAL
 	select UART_INTERRUPT_DRIVEN
 	select BASE64
-	select TINYCBOR
 	help
 	  If y, enables a serial-port based update mode. This allows
 	  MCUboot itself to load update images into flash over a UART.