1 /*
2  * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stddef.h>
8 #include <string.h>
9 #include <qcbor/qcbor_decode.h>
10 #include "cbor_dump.h"
11 
12 /* Dump context structure */
13 struct dump_context
14 {
15     FILE *outfile;
16     unsigned int initial_indent;
17     const char *root_label;
18     const struct cbor_dictionary_entry *dictionary;
19     unsigned int dictionary_len;
20 };
21 
22 static int dump_next_item(QCBORDecodeContext *decode_ctx, struct dump_context *dump_ctx);
23 static void dump_indent(struct dump_context *dump_ctx, const QCBORItem *item);
24 static void dump_label(struct dump_context *dump_ctx, const QCBORItem *item);
25 static void dump_value_separator(struct dump_context *dump_ctx, const QCBORItem *item);
26 static void dump_value(struct dump_context *dump_ctx, const QCBORItem *item);
27 static void dump_text_string(struct dump_context *dump_ctx, const char *data, size_t len);
28 static void dump_byte_string(struct dump_context *dump_ctx, const uint8_t *data, size_t len);
29 static const char *dictionary_lookup(struct dump_context *dump_ctx, int64_t id);
30 
cbor_dump(FILE * file,const uint8_t * cbor,size_t cbor_len,unsigned int indent,const char * root_label,const struct cbor_dictionary_entry * dictionary,unsigned int dictionary_len)31 int cbor_dump(FILE *file,
32     const uint8_t *cbor, size_t cbor_len,
33     unsigned int indent, const char *root_label,
34     const struct cbor_dictionary_entry *dictionary, unsigned int dictionary_len)
35 {
36     int status = -1;
37     UsefulBufC cbor_buf;
38     UsefulBuf mem_pool;
39     uint8_t mem_pool_space[cbor_len + QCBOR_DECODE_MIN_MEM_POOL_SIZE];
40 
41     cbor_buf.ptr = cbor;
42     cbor_buf.len = cbor_len;
43 
44     mem_pool.ptr = mem_pool_space;
45     mem_pool.len = sizeof(mem_pool_space);
46 
47     QCBORDecodeContext decode_ctx;
48 
49     QCBORDecode_Init(&decode_ctx, cbor_buf, QCBOR_DECODE_MODE_NORMAL);
50     status = QCBORDecode_SetMemPool(&decode_ctx, mem_pool, true);
51 
52     if (status == QCBOR_SUCCESS) {
53 
54         struct dump_context dump_ctx;
55 
56         dump_ctx.outfile = file;
57         dump_ctx.initial_indent = indent;
58         dump_ctx.root_label = root_label;
59         dump_ctx.dictionary = dictionary;
60         dump_ctx.dictionary_len = dictionary_len;
61 
62         while ((status = dump_next_item(&decode_ctx, &dump_ctx)) == QCBOR_SUCCESS);
63 
64         // Hitting the end of data is not an error.
65         if (status == QCBOR_ERR_NO_MORE_ITEMS)
66         	status=QCBOR_SUCCESS;
67     }
68 
69     return status;
70 }
71 
dump_next_item(QCBORDecodeContext * decode_ctx,struct dump_context * dump_ctx)72 static int dump_next_item(QCBORDecodeContext *decode_ctx, struct dump_context *dump_ctx)
73 {
74     int status = -1;
75     QCBORItem item;
76 
77     status = QCBORDecode_GetNext(decode_ctx, &item);
78 
79     if (status == QCBOR_SUCCESS) {
80 
81         dump_indent(dump_ctx, &item);
82         dump_label(dump_ctx, &item);
83         dump_value_separator(dump_ctx, &item);
84         dump_value(dump_ctx, &item);
85     }
86 
87     return status;
88 }
89 
dump_indent(struct dump_context * dump_ctx,const QCBORItem * item)90 static void dump_indent(struct dump_context *dump_ctx, const QCBORItem *item)
91 {
92     unsigned int num_tabs = dump_ctx->initial_indent + item->uNestingLevel;
93 
94     for (unsigned int i = 0; i < num_tabs; ++i) {
95 
96         fprintf(dump_ctx->outfile, "    ");
97     }
98 }
99 
dump_label(struct dump_context * dump_ctx,const QCBORItem * item)100 static void dump_label(struct dump_context *dump_ctx, const QCBORItem *item)
101 {
102     switch (item->uLabelType)
103     {
104         case QCBOR_TYPE_INT64:
105         case QCBOR_TYPE_UINT64:
106         {
107             const char *label_string = dictionary_lookup(dump_ctx, item->label.int64);
108             if (label_string)
109                 fprintf(dump_ctx->outfile, "%s:", label_string);
110             else
111                 fprintf(dump_ctx->outfile, "%ld:", item->label.int64);
112             break;
113         }
114         case QCBOR_TYPE_TEXT_STRING:
115             fprintf(dump_ctx->outfile, "%s:", (const char*)item->label.string.ptr);
116             break;
117         case QCBOR_TYPE_NONE:
118             if (item->uNestingLevel == 0 && dump_ctx->root_label) {
119                 fprintf(dump_ctx->outfile, "%s:", dump_ctx->root_label);
120             }
121             break;
122         default:
123             break;
124     }
125 }
126 
dump_value_separator(struct dump_context * dump_ctx,const QCBORItem * item)127 static void dump_value_separator(struct dump_context *dump_ctx, const QCBORItem *item)
128 {
129     if ((item->uDataType == QCBOR_TYPE_ARRAY) ||
130         (item->uDataType == QCBOR_TYPE_MAP)) {
131 
132         fprintf(dump_ctx->outfile, "\n");
133     }
134     else {
135 
136         fprintf(dump_ctx->outfile, "\t");
137     }
138 
139 }
140 
dump_value(struct dump_context * dump_ctx,const QCBORItem * item)141 static void dump_value(struct dump_context *dump_ctx, const QCBORItem *item)
142 {
143     if (item->uDataType == QCBOR_TYPE_TEXT_STRING) {
144 
145         dump_text_string(dump_ctx, (const char*)item->val.string.ptr, item->val.string.len);
146         fprintf(dump_ctx->outfile, "\n");
147     }
148     else if (item->uDataType == QCBOR_TYPE_BYTE_STRING) {
149 
150         dump_byte_string(dump_ctx, (const uint8_t*)item->val.string.ptr, item->val.string.len);
151         fprintf(dump_ctx->outfile, "\n");
152     }
153     else if (item->uDataType == QCBOR_TYPE_INT64) {
154 
155         fprintf(dump_ctx->outfile, "%ld\n", item->val.int64);
156     }
157     else if (item->uDataType == QCBOR_TYPE_UINT64) {
158 
159         fprintf(dump_ctx->outfile, "%lu\n", item->val.uint64);
160     }
161     else if ((item->uDataType != QCBOR_TYPE_NONE) &&
162         (item->uDataType != QCBOR_TYPE_ARRAY) &&
163         (item->uDataType != QCBOR_TYPE_MAP))
164     {
165 
166         fprintf(dump_ctx->outfile, "value %d", item->uDataType);
167     }
168 }
169 
dump_text_string(struct dump_context * dump_ctx,const char * data,size_t len)170 static void dump_text_string(struct dump_context *dump_ctx, const char *data, size_t len)
171 {
172     char text_buf[len + 1];
173 
174     memcpy(text_buf, data, len);
175     text_buf[len] = '\0';
176 
177     fprintf(dump_ctx->outfile, "%s", text_buf);
178 }
179 
dump_byte_string(struct dump_context * dump_ctx,const uint8_t * data,size_t len)180 static void dump_byte_string(struct dump_context *dump_ctx, const uint8_t *data, size_t len)
181 {
182     for (size_t i = 0; i < len; ++i) {
183 
184         fprintf(dump_ctx->outfile, "%02x ", data[i]);
185     }
186 }
187 
dictionary_lookup(struct dump_context * dump_ctx,int64_t id)188 static const char *dictionary_lookup(struct dump_context *dump_ctx, int64_t id)
189 {
190     const char *match = NULL;
191 
192     if (dump_ctx->dictionary) {
193 
194         for (size_t i = 0; i < dump_ctx->dictionary_len; ++i) {
195 
196             if (dump_ctx->dictionary[i].id == id) {
197 
198                 match = dump_ctx->dictionary[i].string;
199                 break;
200             }
201         }
202     }
203 
204     return match;
205 }
206