33
44#include " crypto/cbor.h"
55
6+ #include " ccf/ds/hex.h"
7+
68#include < algorithm>
79#include < iomanip>
10+ #include < list>
811#include < sstream>
912
1013#define FMT_HEADER_ONLY
@@ -19,6 +22,65 @@ using namespace ccf::cbor;
1922
2023namespace
2124{
25+ /* Handy storage of 'cbor_raw's when recursively nesting objects. EverCBOR
26+ * collections work as pointers from one cbor_raw to another, with arrays
27+ * relying on space continuity, and that has to stay intact until calling
28+ * cbor_nondet_serialize. Therefore, the following choices have been made:
29+ *
30+ * - individual items stored in lists rather than collections to avoid
31+ * move-on-resize
32+ * - CBOR collections are made vectors for continuity, and only referenced
33+ * after filled up.
34+ */
35+ class CborRawArena
36+ {
37+ public:
38+ CborRawArena () = default ;
39+ ~CborRawArena () = default ;
40+
41+ void push (cbor_raw&& single)
42+ {
43+ singles.push_back (single);
44+ }
45+
46+ [[nodiscard]] cbor_raw* single () const
47+ {
48+ return const_cast <cbor_raw*>(&singles.back ());
49+ }
50+
51+ void push (std::vector<cbor_raw>&& array)
52+ {
53+ arrays.push_back (array);
54+ }
55+
56+ [[nodiscard]] cbor_raw* array () const
57+ {
58+ return const_cast <cbor_raw*>(&arrays.back ().front ());
59+ }
60+
61+ void push (std::vector<cbor_map_entry>&& map)
62+ {
63+ maps.push_back (map);
64+ }
65+
66+ [[nodiscard]] cbor_map_entry* map () const
67+ {
68+ return const_cast <cbor_map_entry*>(&maps.back ().front ());
69+ }
70+
71+ // No copy
72+ CborRawArena (const CborRawArena&) = delete ;
73+ CborRawArena& operator =(const CborRawArena&) = delete ;
74+
75+ // No move
76+ CborRawArena (CborRawArena&&) = delete ;
77+ CborRawArena& operator =(CborRawArena&&) = delete ;
78+
79+ private:
80+ std::list<cbor_raw> singles;
81+ std::list<std::vector<cbor_raw>> arrays;
82+ std::list<std::vector<cbor_map_entry>> maps;
83+ };
2284 Value consume (cbor_nondet_t cbor);
2385
2486 void print_indent (std::ostringstream& os, size_t indent)
@@ -169,6 +231,181 @@ namespace
169231 }
170232 }
171233
234+ std::string format_simple (const Simple& v)
235+ {
236+ const auto casted = static_cast <int >(v);
237+ switch (casted)
238+ {
239+ case SimpleValue::False:
240+ return " Simple: False" ;
241+ case SimpleValue::True:
242+ return " Simple: True" ;
243+ case SimpleValue::Null:
244+ return " Simple: Null" ;
245+ case SimpleValue::Undefined:
246+ return " Simple: Undefined" ;
247+ default :
248+ return " Simple: " + std::to_string (casted);
249+ }
250+ }
251+
252+ cbor_raw to_raw_cbor (const Value& value, CborRawArena& arena);
253+
254+ cbor_raw to_raw_signed (const Signed& v)
255+ {
256+ return cbor_nondet_mk_int64 (v);
257+ }
258+
259+ cbor_raw to_raw_string (const String& v)
260+ {
261+ cbor_raw result;
262+ if (!cbor_nondet_mk_text_string (
263+ reinterpret_cast <uint8_t *>(const_cast <char *>(v.data ())),
264+ v.size (),
265+ &result))
266+ {
267+ throw CBOREncodeError (
268+ Error::ENCODE_FAILED, fmt::format (" Encoding text string {} failed" , v));
269+ }
270+ return result;
271+ }
272+
273+ cbor_raw to_raw_bytes (const Bytes& v)
274+ {
275+ cbor_raw result;
276+ if (!cbor_nondet_mk_byte_string (
277+ const_cast <uint8_t *>(v.data ()), v.size (), &result))
278+ {
279+ throw CBOREncodeError (
280+ Error::ENCODE_FAILED,
281+ fmt::format (" Encoding bytes string {} failed" , ccf::ds::to_hex (v)));
282+ }
283+ return result;
284+ }
285+
286+ cbor_raw to_raw_simple (const Simple& v)
287+ {
288+ cbor_raw result;
289+ if (!cbor_nondet_mk_simple_value (v, &result))
290+ {
291+ throw CBOREncodeError (
292+ Error::ENCODE_FAILED,
293+ fmt::format (" Encoding simple value {} failed" , format_simple (v)));
294+ }
295+ return result;
296+ }
297+
298+ cbor_raw to_raw_tagged (const Tagged& v, CborRawArena& arena)
299+ {
300+ cbor_raw result;
301+ arena.push (to_raw_cbor (v.item , arena));
302+ if (!cbor_nondet_mk_tagged (v.tag , arena.single (), &result))
303+ {
304+ throw CBOREncodeError (
305+ Error::ENCODE_FAILED, fmt::format (" Encoding tag {} failed" , v.tag ));
306+ }
307+
308+ return result;
309+ }
310+
311+ cbor_raw to_raw_array (const Array& v, CborRawArena& arena)
312+ {
313+ cbor_raw result;
314+ std::vector<cbor_raw> items;
315+ items.reserve (v.items .size ());
316+ for (const auto & item : v.items )
317+ {
318+ items.push_back (to_raw_cbor (item, arena));
319+ }
320+
321+ size_t arr_size = items.size ();
322+
323+ // A workaround to encode an empty array by passing a fake ptr with size=0.
324+ if (items.empty ())
325+ {
326+ items.push_back (cbor_raw{});
327+ }
328+
329+ arena.push (std::move (items));
330+ if (!cbor_nondet_mk_array (arena.array (), arr_size, &result))
331+ {
332+ throw CBOREncodeError (
333+ Error::ENCODE_FAILED,
334+ fmt::format (" Encoding array of size {} failed" , arr_size));
335+ }
336+
337+ return result;
338+ }
339+
340+ cbor_raw to_raw_map (const Map& v, CborRawArena& arena)
341+ {
342+ cbor_raw result;
343+
344+ std::vector<cbor_map_entry> entries;
345+ entries.reserve (v.items .size ());
346+ for (const auto & [key, value] : v.items )
347+ {
348+ auto cbor_key = to_raw_cbor (key, arena);
349+ auto cbor_value = to_raw_cbor (value, arena);
350+ entries.push_back (cbor_nondet_mk_map_entry (cbor_key, cbor_value));
351+ }
352+
353+ size_t map_size = entries.size ();
354+
355+ // A workaround to encode an empty map by passing a fake ptr with size=0.
356+ if (entries.empty ())
357+ {
358+ entries.push_back (cbor_map_entry{});
359+ }
360+
361+ arena.push (std::move (entries));
362+ if (!cbor_nondet_mk_map (arena.map (), map_size, &result))
363+ {
364+ throw CBOREncodeError (
365+ Error::ENCODE_FAILED,
366+ fmt::format (" Encoding map of size {} failed" , map_size));
367+ }
368+
369+ return result;
370+ }
371+
372+ cbor_raw to_raw_cbor (const Value& value, CborRawArena& arena)
373+ {
374+ return std::visit (
375+ [&](const auto & v) {
376+ using T = std::decay_t <decltype (v)>;
377+ if constexpr (std::is_same_v<T, Signed>)
378+ {
379+ return to_raw_signed (v);
380+ }
381+ if constexpr (std::is_same_v<T, String>)
382+ {
383+ return to_raw_string (v);
384+ }
385+ if constexpr (std::is_same_v<T, Bytes>)
386+ {
387+ return to_raw_bytes (v);
388+ }
389+ if constexpr (std::is_same_v<T, Simple>)
390+ {
391+ return to_raw_simple (v);
392+ }
393+ if constexpr (std::is_same_v<T, Tagged>)
394+ {
395+ return to_raw_tagged (v, arena);
396+ }
397+ if constexpr (std::is_same_v<T, Array>)
398+ {
399+ return to_raw_array (v, arena);
400+ }
401+ if constexpr (std::is_same_v<T, Map>)
402+ {
403+ return to_raw_map (v, arena);
404+ }
405+ },
406+ value->value );
407+ }
408+
172409 void print_value_impl (
173410 std::ostringstream& os, const Value& value, size_t indent)
174411 {
@@ -195,12 +432,7 @@ namespace
195432 {
196433 os << " " ;
197434 }
198- for (size_t i = 0 ; i < v.size (); ++i)
199- {
200- os << std::hex << std::setw (2 ) << std::setfill (' 0' )
201- << static_cast <int >(v[i]);
202- }
203- os << std::dec << std::endl;
435+ os << ccf::ds::to_hex (v) << std::endl;
204436 }
205437 else if constexpr (std::is_same_v<T, String>)
206438 {
@@ -239,24 +471,7 @@ namespace
239471 else if constexpr (std::is_same_v<T, Simple>)
240472 {
241473 print_indent (os, indent);
242- const auto casted = static_cast <int >(v);
243- switch (casted)
244- {
245- case SimpleValue::False:
246- os << " Simple: False" << std::endl;
247- break ;
248- case SimpleValue::True:
249- os << " Simple: True" << std::endl;
250- break ;
251- case SimpleValue::Null:
252- os << " Simple: Null" << std::endl;
253- break ;
254- case SimpleValue::Undefined:
255- os << " Simple: Undefined" << std::endl;
256- break ;
257- default :
258- os << " Simple: " << casted << std::endl;
259- }
474+ os << format_simple (v) << std::endl;
260475 }
261476 },
262477 value->value );
@@ -265,6 +480,16 @@ namespace
265480
266481namespace ccf ::cbor
267482{
483+ CBOREncodeError::CBOREncodeError (Error err, const std::string& what) :
484+ std::runtime_error (what),
485+ error (err)
486+ {}
487+
488+ Error CBOREncodeError::error_code () const
489+ {
490+ return error;
491+ }
492+
268493 CBORDecodeError::CBORDecodeError (Error err, const std::string& what) :
269494 std::runtime_error (what),
270495 error (err)
@@ -311,6 +536,30 @@ namespace ccf::cbor
311536 return consume (cbor);
312537 }
313538
539+ std::vector<uint8_t > serialize (const Value& value)
540+ {
541+ CborRawArena arena{};
542+ auto raw = to_raw_cbor (value, arena);
543+ const auto expected_size =
544+ cbor_nondet_size (raw, std::numeric_limits<size_t >::max ());
545+
546+ std::vector<uint8_t > result (expected_size);
547+
548+ const auto bytes_written =
549+ cbor_nondet_serialize (raw, result.data (), expected_size);
550+ if (bytes_written != expected_size)
551+ {
552+ throw CBOREncodeError (
553+ Error::ENCODE_FAILED,
554+ fmt::format (
555+ " Encoded CBOR of size {} when expected {}" ,
556+ bytes_written,
557+ expected_size));
558+ }
559+
560+ return result;
561+ }
562+
314563 std::string to_string (const Value& value)
315564 {
316565 std::ostringstream os;
0 commit comments