]> git.zerfleddert.de Git - proxmark3-svn/blame - client/tinycbor/cborpretty.c
Fido2 (#727)
[proxmark3-svn] / client / tinycbor / cborpretty.c
CommitLineData
0bb51450
OM
1/****************************************************************************
2**
3** Copyright (C) 2018 Intel Corporation
4**
5** Permission is hereby granted, free of charge, to any person obtaining a copy
6** of this software and associated documentation files (the "Software"), to deal
7** in the Software without restriction, including without limitation the rights
8** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9** copies of the Software, and to permit persons to whom the Software is
10** furnished to do so, subject to the following conditions:
11**
12** The above copyright notice and this permission notice shall be included in
13** all copies or substantial portions of the Software.
14**
15** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21** THE SOFTWARE.
22**
23****************************************************************************/
24
25#define _BSD_SOURCE 1
26#define _DEFAULT_SOURCE 1
27#ifndef __STDC_LIMIT_MACROS
28# define __STDC_LIMIT_MACROS 1
29#endif
30
31#include "cbor.h"
32#include "cborinternal_p.h"
33#include "compilersupport_p.h"
34#include "utf8_p.h"
35
36#include <inttypes.h>
37#include <string.h>
38
39/**
40 * \defgroup CborPretty Converting CBOR to text
41 * \brief Group of functions used to convert CBOR to text form.
42 *
43 * This group contains two functions that can be used to convert a \ref
44 * CborValue object to a text representation. This module attempts to follow
45 * the recommendations from RFC 7049 section 6 "Diagnostic Notation", though it
46 * has a few differences. They are noted below.
47 *
48 * TinyCBOR does not provide a way to convert from the text representation back
49 * to encoded form. To produce a text form meant to be parsed, CborToJson is
50 * recommended instead.
51 *
52 * Either of the functions in this section will attempt to convert exactly one
53 * CborValue object to text. Those functions may return any error documented
54 * for the functions for CborParsing. In addition, if the C standard library
55 * stream functions return with error, the text conversion will return with
56 * error CborErrorIO.
57 *
58 * These functions also perform UTF-8 validation in CBOR text strings. If they
59 * encounter a sequence of bytes that is not permitted in UTF-8, they will return
60 * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points
61 * in UTF-8.
62 *
63 * \warning The output type produced by these functions is not guaranteed to
64 * remain stable. A future update of TinyCBOR may produce different output for
65 * the same input and parsers may be unable to handle it.
66 *
67 * \sa CborParsing, CborToJson, cbor_parser_init()
68 */
69
70/**
71 * \addtogroup CborPretty
72 * @{
73 * <h2 class="groupheader">Text format</h2>
74 *
75 * As described in RFC 7049 section 6 "Diagnostic Notation", the format is
76 * largely borrowed from JSON, but modified to suit CBOR's different data
77 * types. TinyCBOR makes further modifications to distinguish different, but
78 * similar values.
79 *
80 * CBOR values are currently encoded as follows:
81 * \par Integrals (unsigned and negative)
82 * Base-10 (decimal) text representation of the value
83 * \par Byte strings:
84 * <tt>"h'"</tt> followed by the Base16 (hex) representation of the binary data, followed by an ending quote (')
85 * \par Text strings:
86 * C-style escaped string in quotes, with C11/C++11 escaping of Unicode codepoints above U+007F.
87 * \par Tags:
88 * Tag value, with the tagged value in parentheses. No special encoding of the tagged value is performed.
89 * \par Simple types:
90 * <tt>"simple(nn)"</tt> where \c nn is the simple value
91 * \par Null:
92 * \c null
93 * \par Undefined:
94 * \c undefined
95 * \par Booleans:
96 * \c true or \c false
97 * \par Floating point:
98 * If NaN or infinite, the actual words \c NaN or \c infinite.
99 * Otherwise, the decimal representation with as many digits as necessary to ensure no loss of information.
100 * By default, float values are suffixed by "f" and half-float values suffixed by "f16" (doubles have no suffix).
101 * If the CborPrettyNumericEncodingIndicators flag is active, the values instead are encoded following the
102 * Section 6 recommended encoding indicators: float values are suffixed with "_2" and half-float with "_1".
103 * A decimal point is always present.
104 * \par Arrays:
105 * Comma-separated list of elements, enclosed in square brackets ("[" and "]").
106 * \par Maps:
107 * Comma-separated list of key-value pairs, with the key and value separated
108 * by a colon (":"), enclosed in curly braces ("{" and "}").
109 *
110 * The CborPrettyFlags enumerator contains flags to control some aspects of the
111 * encoding:
112 * \par String fragmentation
113 * When the CborPrettyShowStringFragments option is active, text and byte
114 * strings that are transmitted in fragments are shown instead inside
115 * parentheses ("(" and ")") with no preceding number and each fragment is
116 * displayed individually. If a tag precedes the string, then the output
117 * will contain a double set of parentheses. If the option is not active,
118 * the fragments are merged together and the display will not show any
119 * difference from a string transmitted with determinate length.
120 * \par Encoding indicators
121 * Numbers and lengths in CBOR can be encoded in multiple representations.
122 * If the CborPrettyIndicateOverlongNumbers option is active, numbers
123 * and lengths that are transmitted in a longer encoding than necessary
124 * will be indicated, by appending an underscore ("_") to either the
125 * number or the opening bracket or brace, followed by a number
126 * indicating the CBOR additional information: 0 for 1 byte, 1 for 2
127 * bytes, 2 for 4 bytes and 3 for 8 bytes.
128 * If the CborPrettyIndicateIndeterminateLength option is active, maps,
129 * arrays and strings encoded with indeterminate length will be marked by
130 * an underscore after the opening bracket or brace or the string (if not
131 * showing fragments), without a number after it.
132 */
133
134/**
135 * \enum CborPrettyFlags
136 * The CborPrettyFlags enum contains flags that control the conversion of CBOR to text format.
137 *
138 * \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float.
139 * \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16").
140 * \value CborPrettyIndicateIndeterminateLength (default) Indicate when a map or array has indeterminate length.
141 * \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed.
142 * \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually.
143 * \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry.
144 * \value CborPrettyDefaultFlags Default conversion flags.
145 */
146
147#ifndef CBOR_NO_FLOATING_POINT
148static inline bool convertToUint64(double v, uint64_t *absolute)
149{
150 double supremum;
151 v = fabs(v);
152
153 /* C11 standard section 6.3.1.4 "Real floating and integer" says:
154 *
155 * 1 When a finite value of real floating type is converted to an integer
156 * type other than _Bool, the fractional part is discarded (i.e., the
157 * value is truncated toward zero). If the value of the integral part
158 * cannot be represented by the integer type, the behavior is undefined.
159 *
160 * So we must perform a range check that v <= UINT64_MAX, but we can't use
161 * UINT64_MAX + 1.0 because the standard continues:
162 *
163 * 2 When a value of integer type is converted to a real floating type, if
164 * the value being converted can be represented exactly in the new type,
165 * it is unchanged. If the value being converted is in the range of
166 * values that can be represented but cannot be represented exactly, the
167 * result is either the nearest higher or nearest lower representable
168 * value, chosen in an implementation-defined manner.
169 */
170 supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */
171 if (v >= supremum)
172 return false;
173
174 /* Now we can convert, these two conversions cannot be UB */
175 *absolute = v;
176 return *absolute == v;
177}
178#endif
179
180static void printRecursionLimit(CborStreamFunction stream, void *out)
181{
182 stream(out, "<nesting too deep, recursion stopped>");
183}
184
185static CborError hexDump(CborStreamFunction stream, void *out, const void *ptr, size_t n)
186{
187 const uint8_t *buffer = (const uint8_t *)ptr;
188 CborError err = CborNoError;
189 while (n-- && !err)
190 err = stream(out, "%02" PRIx8, *buffer++);
191
192 return err;
193}
194
195/* This function decodes buffer as UTF-8 and prints as escaped UTF-16.
196 * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */
197static CborError utf8EscapedDump(CborStreamFunction stream, void *out, const void *ptr, size_t n)
198{
199 const uint8_t *buffer = (const uint8_t *)ptr;
200 const uint8_t * const end = buffer + n;
201 CborError err = CborNoError;
202
203 while (buffer < end && !err) {
204 uint32_t uc = get_utf8(&buffer, end);
205 if (uc == ~0U)
206 return CborErrorInvalidUtf8TextString;
207
208 if (uc < 0x80) {
209 /* single-byte UTF-8 */
210 unsigned char escaped = (unsigned char)uc;
211 if (uc < 0x7f && uc >= 0x20 && uc != '\\' && uc != '"') {
212 err = stream(out, "%c", (char)uc);
213 continue;
214 }
215
216 /* print as an escape sequence */
217 switch (uc) {
218 case '"':
219 case '\\':
220 break;
221 case '\b':
222 escaped = 'b';
223 break;
224 case '\f':
225 escaped = 'f';
226 break;
227 case '\n':
228 escaped = 'n';
229 break;
230 case '\r':
231 escaped = 'r';
232 break;
233 case '\t':
234 escaped = 't';
235 break;
236 default:
237 goto print_utf16;
238 }
239 err = stream(out, "\\%c", escaped);
240 continue;
241 }
242
243 /* now print the sequence */
244 if (uc > 0xffffU) {
245 /* needs surrogate pairs */
246 err = stream(out, "\\u%04" PRIX32 "\\u%04" PRIX32,
247 (uc >> 10) + 0xd7c0, /* high surrogate */
248 (uc % 0x0400) + 0xdc00);
249 } else {
250print_utf16:
251 /* no surrogate pair needed */
252 err = stream(out, "\\u%04" PRIX32, uc);
253 }
254 }
255 return err;
256}
257
258static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int flags)
259{
260 static const char indicators[8][3] = {
261 "_0", "_1", "_2", "_3",
262 "", "", "", /* these are not possible */
263 "_"
264 };
265 const char *no_indicator = indicators[5]; /* empty string */
266 uint8_t additional_information;
267 uint8_t expected_information;
268 uint64_t value;
269 CborError err;
270
271 if (ptr == end)
272 return NULL; /* CborErrorUnexpectedEOF */
273
274 additional_information = (*ptr & SmallValueMask);
275 if (additional_information < Value8Bit)
276 return no_indicator;
277
278 /* determine whether to show anything */
279 if ((flags & CborPrettyIndicateIndeterminateLength) &&
280 additional_information == IndefiniteLength)
281 return indicators[IndefiniteLength - Value8Bit];
282 if ((flags & CborPrettyIndicateOverlongNumbers) == 0)
283 return no_indicator;
284
285 err = _cbor_value_extract_number(&ptr, end, &value);
286 if (err)
287 return NULL; /* CborErrorUnexpectedEOF */
288
289 expected_information = Value8Bit - 1;
290 if (value >= Value8Bit)
291 ++expected_information;
292 if (value > 0xffU)
293 ++expected_information;
294 if (value > 0xffffU)
295 ++expected_information;
296 if (value > 0xffffffffU)
297 ++expected_information;
298 return expected_information == additional_information ?
299 no_indicator :
300 indicators[additional_information - Value8Bit];
301}
302
303static const char *get_indicator(const CborValue *it, int flags)
304{
305 return resolve_indicator(it->ptr, it->parser->end, flags);
306}
307
308static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft);
309static CborError container_to_pretty(CborStreamFunction stream, void *out, CborValue *it, CborType containerType,
310 int flags, int recursionsLeft)
311{
312 const char *comma = "";
313 CborError err = CborNoError;
314
315 if (!recursionsLeft) {
316 printRecursionLimit(stream, out);
317 return err; /* do allow the dumping to continue */
318 }
319
320 while (!cbor_value_at_end(it) && !err) {
321 err = stream(out, "%s", comma);
322 comma = ", ";
323
324 if (!err)
325 err = value_to_pretty(stream, out, it, flags, recursionsLeft);
326
327 if (containerType == CborArrayType)
328 continue;
329
330 /* map: that was the key, so get the value */
331 if (!err)
332 err = stream(out, ": ");
333 if (!err)
334 err = value_to_pretty(stream, out, it, flags, recursionsLeft);
335 }
336 return err;
337}
338
339static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft)
340{
341 CborError err = CborNoError;
342 CborType type = cbor_value_get_type(it);
343 switch (type) {
344 case CborArrayType:
345 case CborMapType: {
346 /* recursive type */
347 CborValue recursed;
348 const char *indicator = get_indicator(it, flags);
349 const char *space = *indicator ? " " : indicator;
350
351 err = stream(out, "%c%s%s", type == CborArrayType ? '[' : '{', indicator, space);
352 if (err)
353 return err;
354
355 err = cbor_value_enter_container(it, &recursed);
356 if (err) {
357 it->ptr = recursed.ptr;
358 return err; /* parse error */
359 }
360 err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1);
361 if (err) {
362 it->ptr = recursed.ptr;
363 return err; /* parse error */
364 }
365 err = cbor_value_leave_container(it, &recursed);
366 if (err)
367 return err; /* parse error */
368
369 return stream(out, type == CborArrayType ? "]" : "}");
370 }
371
372 case CborIntegerType: {
373 uint64_t val;
374 cbor_value_get_raw_integer(it, &val); /* can't fail */
375
376 if (cbor_value_is_unsigned_integer(it)) {
377 err = stream(out, "%" PRIu64, val);
378 } else {
379 /* CBOR stores the negative number X as -1 - X
380 * (that is, -1 is stored as 0, -2 as 1 and so forth) */
381 if (++val) { /* unsigned overflow may happen */
382 err = stream(out, "-%" PRIu64, val);
383 } else {
384 /* overflown
385 * 0xffff`ffff`ffff`ffff + 1 =
386 * 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */
387 err = stream(out, "-18446744073709551616");
388 }
389 }
390 if (!err)
391 err = stream(out, "%s", get_indicator(it, flags));
392 break;
393 }
394
395 case CborByteStringType:
396 case CborTextStringType: {
397 size_t n = 0;
398 const void *ptr;
399 bool showingFragments = (flags & CborPrettyShowStringFragments) && !cbor_value_is_length_known(it);
400 const char *separator = "";
401 char close = '\'';
402 char open[3] = "h'";
403 const char *indicator = NULL;
404
405 if (type == CborTextStringType) {
406 close = open[0] = '"';
407 open[1] = '\0';
408 }
409
410 if (showingFragments) {
411 err = stream(out, "(_ ");
412 if (!err)
413 err = _cbor_value_prepare_string_iteration(it);
414 } else {
415 err = stream(out, "%s", open);
416 }
417
418 while (!err) {
419 if (showingFragments || indicator == NULL) {
420 /* any iteration, except the second for a non-chunked string */
421 indicator = resolve_indicator(it->ptr, it->parser->end, flags);
422 }
423
424 err = _cbor_value_get_string_chunk(it, &ptr, &n, it);
425 if (!ptr)
426 break;
427
428 if (!err && showingFragments)
429 err = stream(out, "%s%s", separator, open);
430 if (!err)
431 err = (type == CborByteStringType ?
432 hexDump(stream, out, ptr, n) :
433 utf8EscapedDump(stream, out, ptr, n));
434 if (!err && showingFragments) {
435 err = stream(out, "%c%s", close, indicator);
436 separator = ", ";
437 }
438 }
439
440 if (!err) {
441 if (showingFragments)
442 err = stream(out, ")");
443 else
444 err = stream(out, "%c%s", close, indicator);
445 }
446 return err;
447 }
448
449 case CborTagType: {
450 CborTag tag;
451 cbor_value_get_tag(it, &tag); /* can't fail */
452 err = stream(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags));
453 if (!err)
454 err = cbor_value_advance_fixed(it);
455 if (!err && recursionsLeft)
456 err = value_to_pretty(stream, out, it, flags, recursionsLeft - 1);
457 else if (!err)
458 printRecursionLimit(stream, out);
459 if (!err)
460 err = stream(out, ")");
461 return err;
462 }
463
464 case CborSimpleType: {
465 /* simple types can't fail and can't have overlong encoding */
466 uint8_t simple_type;
467 cbor_value_get_simple_type(it, &simple_type);
468 err = stream(out, "simple(%" PRIu8 ")", simple_type);
469 break;
470 }
471
472 case CborNullType:
473 err = stream(out, "null");
474 break;
475
476 case CborUndefinedType:
477 err = stream(out, "undefined");
478 break;
479
480 case CborBooleanType: {
481 bool val;
482 cbor_value_get_boolean(it, &val); /* can't fail */
483 err = stream(out, val ? "true" : "false");
484 break;
485 }
486
487#ifndef CBOR_NO_FLOATING_POINT
488 case CborDoubleType: {
489 const char *suffix;
490 double val;
491 int r;
492 uint64_t ival;
493
494 if (false) {
495 float f;
496 case CborFloatType:
497 cbor_value_get_float(it, &f);
498 val = f;
499 suffix = flags & CborPrettyNumericEncodingIndicators ? "_2" : "f";
500 } else if (false) {
501 uint16_t f16;
502 case CborHalfFloatType:
503#ifndef CBOR_NO_HALF_FLOAT_TYPE
504 cbor_value_get_half_float(it, &f16);
505 val = decode_half(f16);
506 suffix = flags & CborPrettyNumericEncodingIndicators ? "_1" : "f16";
507#else
508 (void)f16;
509 err = CborErrorUnsupportedType;
510 break;
511#endif
512 } else {
513 cbor_value_get_double(it, &val);
514 suffix = "";
515 }
516
517 if ((flags & CborPrettyNumericEncodingIndicators) == 0) {
518 r = fpclassify(val);
519 if (r == FP_NAN || r == FP_INFINITE)
520 suffix = "";
521 }
522
523 if (convertToUint64(val, &ival)) {
524 /* this double value fits in a 64-bit integer, so show it as such
525 * (followed by a floating point suffix, to disambiguate) */
526 err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix);
527 } else {
528 /* this number is definitely not a 64-bit integer */
529 err = stream(out, "%." DBL_DECIMAL_DIG_STR "g%s", val, suffix);
530 }
531 break;
532 }
533#else
534 case CborDoubleType:
535 case CborFloatType:
536 case CborHalfFloatType:
537 err = CborErrorUnsupportedType;
538 break;
539#endif /* !CBOR_NO_FLOATING_POINT */
540
541 case CborInvalidType:
542 err = stream(out, "invalid");
543 if (err)
544 return err;
545 return CborErrorUnknownType;
546 }
547
548 if (!err)
549 err = cbor_value_advance_fixed(it);
550 return err;
551}
552
553/**
554 * Converts the current CBOR type pointed by \a value to its textual
555 * representation and writes it to the stream by calling the \a streamFunction.
556 * If an error occurs, this function returns an error code similar to
557 * \ref CborParsing.
558 *
559 * The textual representation can be controlled by the \a flags parameter (see
560 * \ref CborPrettyFlags for more information).
561 *
562 * If no error ocurred, this function advances \a value to the next element.
563 * Often, concatenating the text representation of multiple elements can be
564 * done by appending a comma to the output stream in between calls to this
565 * function.
566 *
567 * The \a streamFunction function will be called with the \a token value as the
568 * first parameter and a printf-style format string as the second, with a variable
569 * number of further parameters.
570 *
571 * \sa cbor_value_to_pretty(), cbor_value_to_json_advance()
572 */
573CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags)
574{
575 return value_to_pretty(streamFunction, token, value, flags, CBOR_PARSER_MAX_RECURSIONS);
576}
577
578/** @} */
Impressum, Datenschutz