From 3359b9adcf4041b183db379d9a363844de898725 Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Wed, 7 Jan 2026 10:58:35 +0000 Subject: [PATCH 1/6] Improve CBOR API --- CMakeLists.txt | 6 + src/crypto/cbor.cpp | 343 +++---- src/crypto/cbor.h | 54 +- src/crypto/test/cbor.cpp | 1638 +++++++++++++++++++++++++++++++++ src/node/cose_common.h | 3 + src/node/uvm_endorsements.cpp | 216 +++-- 6 files changed, 2000 insertions(+), 260 deletions(-) create mode 100644 src/crypto/test/cbor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 80db4fd89c4..a24021b701f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -618,6 +618,12 @@ if(BUILD_TESTS) target_include_directories(crypto_test PRIVATE ${CCFCRYPTO_INC}) target_link_libraries(crypto_test PRIVATE ccfcrypto) + add_unit_test( + cbor_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/cbor.cpp + ) + target_include_directories(cbor_test PRIVATE ${CCFCRYPTO_INC}) + target_link_libraries(cbor_test PRIVATE ccfcrypto) + add_unit_test( sharing_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/secret_sharing.cpp diff --git a/src/crypto/cbor.cpp b/src/crypto/cbor.cpp index 73f407715e0..6a49a5f46d0 100644 --- a/src/crypto/cbor.cpp +++ b/src/crypto/cbor.cpp @@ -21,26 +21,6 @@ namespace { Value consume(cbor_nondet_t cbor); - // Helper to wrap operations with context-aware error messages - template - decltype(auto) with_context( - std::string_view context, std::string_view operation, const F& func) - { - try - { - return func(); - } - catch (const CBORDecodeError& e) - { - if (!context.empty()) - { - throw CBORDecodeError( - fmt::format("Failed to {} {}: {}", operation, context, e.what())); - } - throw; - } - } - void print_indent(std::ostringstream& os, size_t indent) { for (size_t i = 0; i < indent; ++i) @@ -217,15 +197,15 @@ namespace else if constexpr (std::is_same_v) { print_indent(os, indent); - os << "Bytes[" << v.size() << "]: "; - for (size_t i = 0; i < std::min(v.size(), size_t(16)); ++i) + os << "Bytes[" << v.size() << "]:"; + if (!v.empty()) { - os << std::hex << std::setw(2) << std::setfill('0') - << static_cast(v[i]); + os << " "; } - if (v.size() > 16) + for (size_t i = 0; i < v.size(); ++i) { - os << "..."; + os << std::hex << std::setw(2) << std::setfill('0') + << static_cast(v[i]); } os << std::dec << std::endl; } @@ -266,12 +246,34 @@ namespace else if constexpr (std::is_same_v) { print_indent(os, indent); - os << "Simple: " << static_cast(v) << std::endl; + const auto casted = static_cast(v); + switch (casted) + { + case SimpleValue::False: + os << "Simple: False" << std::endl; + break; + case SimpleValue::True: + os << "Simple: True" << std::endl; + break; + case SimpleValue::Null: + os << "Simple: Null" << std::endl; + break; + default: + os << "Simple: " << casted << std::endl; + } } }, value->value); } + bool signed_equals_unsigned(Signed a, Unsigned b) + { + if (a < 0 || b > std::numeric_limits::max()) + { + return false; + } + return a == b; + } } // namespace namespace ccf::cbor @@ -280,124 +282,141 @@ namespace ccf::cbor { return std::make_unique(value); } + Value make_signed(int64_t value) { return std::make_unique(value); } + Value make_string(std::string_view data) { return std::make_unique(data); } - Value parse_value(std::span raw, std::string_view context) + Value make_bytes(std::span data) { - return with_context(context, "parse", [&] { - cbor_nondet_t cbor; - const bool check_map_key_bound = false; - const size_t map_key_bound = 0; - auto* cbor_parse_input = const_cast(raw.data()); - size_t cbor_parse_size = raw.size(); - if (!cbor_nondet_parse( - check_map_key_bound, - map_key_bound, - &cbor_parse_input, - &cbor_parse_size, - &cbor)) - { - throw CBORDecodeError("Failed to parse top-level cbor"); - } + return std::make_unique(data); + } + + Value parse(std::span raw) + { + cbor_nondet_t cbor; + const bool check_map_key_bound = false; + const size_t map_key_bound = 0; + auto* cbor_parse_input = const_cast(raw.data()); + size_t cbor_parse_size = raw.size(); + if (!cbor_nondet_parse( + check_map_key_bound, + map_key_bound, + &cbor_parse_input, + &cbor_parse_size, + &cbor)) + { + throw CBORDecodeError("Failed to parse top-level cbor"); + } - return consume(cbor); - }); + return consume(cbor); } - std::string print_value(const Value& value, size_t indent) + std::string to_string(const Value& value) { std::ostringstream os; - print_value_impl(os, value, indent); - return os.str(); + constexpr size_t initial_indent{0}; + print_value_impl(os, value, initial_indent); + auto as_string = os.str(); + if (!as_string.empty() && as_string.back() == '\n') + { + as_string.pop_back(); + } + return as_string; } - const Value& ValueImpl::array_at(size_t index, std::string_view context) const + const Value& ValueImpl::array_at(size_t index) const { - return with_context(context, "access array element", [&]() -> const Value& { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not an array"); - } + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not an array"); + } - const auto& arr = std::get(value); - if (index >= arr.items.size()) - { - throw CBORDecodeError("Array index out of bounds"); - } + const auto& arr = std::get(value); + if (index >= arr.items.size()) + { + throw CBORDecodeError("Array index out of bounds"); + } - return arr.items[index]; - }); + return arr.items[index]; } - const Value& ValueImpl::map_at( - const Value& key, std::string_view context) const + const Value& ValueImpl::map_at(const Value& key) const { - return with_context(context, "access map key", [&]() -> const Value& { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not a map"); - } + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not a map"); + } - // Fail fast: Array, Map, Tagged are not supported as map keys in this - // version, and probably shouldn't be in the future. - std::visit( - [](const auto& k) { - using T = std::decay_t; - if constexpr ( - std::is_same_v || std::is_same_v || - std::is_same_v) - { - throw CBORDecodeError( - "Array, Map, and Tagged values cannot be used as map keys"); - } - }, - key->value); + // Fail fast: Array, Map, Tagged are not supported as map keys in this + // version, and probably shouldn't be in the future. + std::visit( + [](const auto& k) { + using T = std::decay_t; + if constexpr ( + std::is_same_v || std::is_same_v || + std::is_same_v) + { + throw CBORDecodeError( + "Array, Map, and Tagged values cannot be used as map keys"); + } + }, + key->value); - const auto& map = std::get(value); - for (const auto& [k, v] : map.items) - { - const bool match = std::visit( - [](const auto& a, const auto& b) -> bool { - using TA = std::decay_t; - using TB = std::decay_t; + const auto& map = std::get(value); + for (const auto& [k, v] : map.items) + { + const bool match = std::visit( + [](const auto& a, const auto& b) -> bool { + using TA = std::decay_t; + using TB = std::decay_t; - if constexpr (!std::is_same_v) - { - return false; - } - else if constexpr ( - std::is_same_v || std::is_same_v) - { - return a == b; - } - else if constexpr ( - std::is_same_v || std::is_same_v) + if constexpr (!std::is_same_v) + { + // Handle unsigned/signed equally for key comparison. + if constexpr ( + std::is_same_v && std::is_same_v) { - return std::equal(a.begin(), a.end(), b.begin(), b.end()); + return signed_equals_unsigned(a, b); } - else + if constexpr ( + std::is_same_v && std::is_same_v) { - return false; + return signed_equals_unsigned(a, b); } - }, - key->value, - k->value); + return false; + } + else if constexpr ( + std::is_same_v || std::is_same_v) + { + return a == b; + } + else if constexpr ( + std::is_same_v || std::is_same_v) + { + return std::equal(a.begin(), a.end(), b.begin(), b.end()); + } + else + { + return false; + } + }, + key->value, + k->value); - if (match) - { - return v; - } + if (match) + { + return v; } + } - throw CBORDecodeError("Key not found in map"); - }); + throw CBORDecodeError("Key not found in map"); } size_t ValueImpl::size() const @@ -415,72 +434,78 @@ namespace ccf::cbor throw CBORDecodeError("Not a collection"); } - const Value& ValueImpl::tag_at(uint64_t tag, std::string_view context) const + const Value& ValueImpl::tag_at(uint64_t tag) const { - return with_context(context, "extract tag", [&]() -> const Value& { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not a tagged value"); - } + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not a tagged value"); + } - const auto& tagged = std::get(value); - if (tagged.tag != tag) - { - throw CBORDecodeError("Tag does not match"); - } + const auto& tagged = std::get(value); + if (tagged.tag != tag) + { + throw CBORDecodeError("Tag does not match"); + } - return tagged.item; - }); + return tagged.item; } - Unsigned ValueImpl::as_unsigned(std::string_view context) const + Unsigned ValueImpl::as_unsigned() const { - return with_context(context, "convert to unsigned", [&] { - if (!std::holds_alternative(value)) + if (!std::holds_alternative(value)) + { + if (!std::holds_alternative(value)) { throw CBORDecodeError("Not an unsigned value"); } - return std::get(value); - }); + const auto s_value = std::get(value); + if (s_value < 0) + { + throw CBORDecodeError("Casting signed to unsigned will overflow"); + } + return static_cast(s_value); + } + return std::get(value); } - Signed ValueImpl::as_signed(std::string_view context) const + Signed ValueImpl::as_signed() const { - return with_context(context, "convert to signed", [&] { - if (!std::holds_alternative(value)) + if (!std::holds_alternative(value)) + { + if (!std::holds_alternative(value)) { throw CBORDecodeError("Not a signed value"); } - return std::get(value); - }); - } - Bytes ValueImpl::as_bytes(std::string_view context) const - { - return with_context(context, "convert to bytes", [&] { - if (!std::holds_alternative(value)) + const auto u_value = std::get(value); + if (u_value > std::numeric_limits::max()) { - throw CBORDecodeError("Not a bytes value"); + throw CBORDecodeError("Casting unsigned to signed will overflow"); } - return std::get(value); - }); + return static_cast(u_value); + } + return std::get(value); } - String ValueImpl::as_string(std::string_view context) const + Bytes ValueImpl::as_bytes() const { - return with_context(context, "convert to string", [&] { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not a string value"); - } - return std::get(value); - }); + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not a bytes value"); + } + return std::get(value); } - Simple ValueImpl::as_simple(std::string_view context) const + String ValueImpl::as_string() const { - return with_context(context, "convert to simple", [&] { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not a simple value"); - } - return std::get(value); - }); + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not a string value"); + } + return std::get(value); + } + Simple ValueImpl::as_simple() const + { + if (!std::holds_alternative(value)) + { + throw CBORDecodeError("Not a simple value"); + } + return std::get(value); } } // namespace ccf::cbor \ No newline at end of file diff --git a/src/crypto/cbor.h b/src/crypto/cbor.h index 84212dc5992..148a4f515fe 100644 --- a/src/crypto/cbor.h +++ b/src/crypto/cbor.h @@ -11,6 +11,9 @@ #include #include +#define FMT_HEADER_ONLY +#include + namespace ccf::cbor { struct ValueImpl; @@ -22,6 +25,16 @@ namespace ccf::cbor using String = std::string_view; using Simple = uint8_t; + // https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml. + // + // To be filled further on demand, currently only those to be (likely) used. + enum SimpleValue : uint8_t + { + False = 20, + True = 21, + Null = 22, + }; + struct Array { std::vector items; @@ -48,25 +61,38 @@ namespace ccf::cbor ValueImpl(Type value_) : value(std::move(value_)) {} Type value; - [[nodiscard]] const Value& array_at( - size_t index, std::string_view context = {}) const; - [[nodiscard]] const Value& map_at( - const Value& key, std::string_view context = {}) const; - [[nodiscard]] const Value& tag_at( - uint64_t tag, std::string_view context = {}) const; - [[nodiscard]] Unsigned as_unsigned(std::string_view context = {}) const; - [[nodiscard]] Signed as_signed(std::string_view context = {}) const; - [[nodiscard]] Bytes as_bytes(std::string_view context = {}) const; - [[nodiscard]] String as_string(std::string_view context = {}) const; - [[nodiscard]] Simple as_simple(std::string_view context = {}) const; + [[nodiscard]] const Value& array_at(size_t index) const; + [[nodiscard]] const Value& map_at(const Value& key) const; + [[nodiscard]] const Value& tag_at(uint64_t tag) const; + [[nodiscard]] Unsigned as_unsigned() const; + [[nodiscard]] Signed as_signed() const; + [[nodiscard]] Bytes as_bytes() const; + [[nodiscard]] String as_string() const; + [[nodiscard]] Simple as_simple() const; [[nodiscard]] size_t size() const; }; Value make_unsigned(uint64_t value); Value make_signed(int64_t value); Value make_string(std::string_view data); + Value make_bytes(std::span data); + + Value parse(std::span raw); + std::string to_string(const Value& value); - Value parse_value( - std::span raw, std::string_view context = {}); - std::string print_value(const Value& value, size_t indent = 0); + decltype(auto) rethrow_with_context(auto&& f, std::string_view context = {}) + { + try + { + return f(); + } + catch (const CBORDecodeError& err) + { + if (!context.empty()) + { + throw CBORDecodeError(fmt::format("{}: {}", err.what(), context)); + } + throw err; + } + } } // namespace ccf::cbor \ No newline at end of file diff --git a/src/crypto/test/cbor.cpp b/src/crypto/test/cbor.cpp new file mode 100644 index 00000000000..bd74c32c443 --- /dev/null +++ b/src/crypto/test/cbor.cpp @@ -0,0 +1,1638 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "crypto/cbor.h" + +#include "ccf/ds/hex.h" + +#include +#include +#include +#include + +using namespace ccf::cbor; + +TEST_CASE("CBOR: unsigned integer 0") +{ + auto cbor_bytes = ccf::ds::from_hex("00"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_unsigned() == 0); + + const std::string expected_repr = "Unsigned: 0"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: unsigned integer 42") +{ + auto cbor_bytes = ccf::ds::from_hex("182a"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_unsigned() == 42); + + const std::string expected_repr = "Unsigned: 42"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: unsigned integer 255") +{ + auto cbor_bytes = ccf::ds::from_hex("18ff"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_unsigned() == 255); + + const std::string expected_repr = "Unsigned: 255"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: unsigned integer 65535") +{ + auto cbor_bytes = ccf::ds::from_hex("19ffff"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_unsigned() == 65535); + + const std::string expected_repr = "Unsigned: 65535"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: unsigned integer max uint64") +{ + auto cbor_bytes = ccf::ds::from_hex("1bffffffffffffffff"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_unsigned() == 18446744073709551615ULL); + + const std::string expected_repr = "Unsigned: 18446744073709551615"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: signed integer -1") +{ + auto cbor_bytes = ccf::ds::from_hex("20"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_signed() == -1); + + const std::string expected_repr = "Signed: -1"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: signed integer -42") +{ + auto cbor_bytes = ccf::ds::from_hex("3829"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_signed() == -42); + + const std::string expected_repr = "Signed: -42"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: signed integer -1000") +{ + auto cbor_bytes = ccf::ds::from_hex("3903e7"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_signed() == -1000); + + const std::string expected_repr = "Signed: -1000"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: signed integer min int64") +{ + auto cbor_bytes = ccf::ds::from_hex("3b7fffffffffffffff"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_signed() == INT64_MIN); + + const std::string expected_repr = "Signed: -9223372036854775808"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: empty string") +{ + auto cbor_bytes = ccf::ds::from_hex("60"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_string() == ""); + + const std::string expected_repr = R"(String: "")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: string 'hello'") +{ + auto cbor_bytes = ccf::ds::from_hex("6568656c6c6f"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_string() == "hello"); + + const std::string expected_repr = R"(String: "hello")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: string 'Hello, World!'") +{ + auto cbor_bytes = ccf::ds::from_hex("6d48656c6c6f2c20576f726c6421"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_string() == "Hello, World!"); + + const std::string expected_repr = R"(String: "Hello, World!")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: empty bytes") +{ + auto cbor_bytes = ccf::ds::from_hex("40"); + auto value = parse(cbor_bytes); + + auto bytes = value->as_bytes(); + REQUIRE(bytes.size() == 0); + + const std::string expected_repr = "Bytes[0]:"; + + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: bytes [0,1,2,3]") +{ + auto cbor_bytes = ccf::ds::from_hex("4400010203"); + auto value = parse(cbor_bytes); + + auto bytes = value->as_bytes(); + std::vector expected{0x00, 0x01, 0x02, 0x03}; + REQUIRE( + std::equal(expected.begin(), expected.end(), bytes.begin(), bytes.end())); + + const std::string expected_repr = "Bytes[4]: 00010203"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: bytes deadbeef") +{ + auto cbor_bytes = ccf::ds::from_hex("44deadbeef"); + auto value = parse(cbor_bytes); + + auto bytes = value->as_bytes(); + std::vector expected{0xde, 0xad, 0xbe, 0xef}; + REQUIRE( + std::equal(expected.begin(), expected.end(), bytes.begin(), bytes.end())); + + const std::string expected_repr = "Bytes[4]: deadbeef"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: simple value false") +{ + auto cbor_bytes = ccf::ds::from_hex("f4"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_simple() == SimpleValue::False); + + const std::string expected_repr = "Simple: False"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: simple value true") +{ + auto cbor_bytes = ccf::ds::from_hex("f5"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_simple() == SimpleValue::True); + + const std::string expected_repr = "Simple: True"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: simple value null") +{ + auto cbor_bytes = ccf::ds::from_hex("f6"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_simple() == SimpleValue::Null); + + const std::string expected_repr = "Simple: Null"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: simple value undefined") +{ + auto cbor_bytes = ccf::ds::from_hex("f7"); + auto value = parse(cbor_bytes); + + REQUIRE(value->as_simple() == 23); + + const std::string expected_repr = "Simple: 23"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged value Tag(9000) with unsigned 42") +{ + auto cbor_bytes = ccf::ds::from_hex("d92328182a"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9000); + REQUIRE(item->as_unsigned() == 42); + + const std::string expected_repr = R"(Tagged[9000]: + Unsigned: 42)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged value Tag(9001) with signed -42") +{ + auto cbor_bytes = ccf::ds::from_hex("d923293829"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9001); + REQUIRE(item->as_signed() == -42); + + const std::string expected_repr = R"(Tagged[9001]: + Signed: -42)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged value Tag(9002) with string") +{ + auto cbor_bytes = ccf::ds::from_hex("d9232a6d74616767656420737472696e67"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9002); + REQUIRE(item->as_string() == "tagged string"); + + const std::string expected_repr = R"(Tagged[9002]: + String: "tagged string")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged value Tag(9003) with bytes") +{ + auto cbor_bytes = ccf::ds::from_hex("d9232b42cafe"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9003); + auto bytes = item->as_bytes(); + REQUIRE(bytes.size() == 2); + REQUIRE(bytes[0] == 0xca); + REQUIRE(bytes[1] == 0xfe); + + const std::string expected_repr = R"(Tagged[9003]: + Bytes[2]: cafe)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged value Tag(9004) with boolean") +{ + auto cbor_bytes = ccf::ds::from_hex("d9232cf5"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9004); + REQUIRE(item->as_simple() == SimpleValue::True); + + const std::string expected_repr = R"(Tagged[9004]: + Simple: True)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: nested tags Tag(9010, Tag(9020))") +{ + auto cbor_bytes = ccf::ds::from_hex("d92332d9233c666e6573746564"); + auto value = parse(cbor_bytes); + + const auto& outer_item = value->tag_at(9010); + const auto& inner_item = outer_item->tag_at(9020); + REQUIRE(inner_item->as_string() == "nested"); + + const std::string expected_repr = R"(Tagged[9010]: + Tagged[9020]: + String: "nested")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: empty array") +{ + auto cbor_bytes = ccf::ds::from_hex("80"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 0); + + const std::string expected_repr = "Array[0]:"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [1, 2, 3, 4, 5]") +{ + auto cbor_bytes = ccf::ds::from_hex("850102030405"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 5); + + for (size_t i = 0; i < 5; i++) + { + REQUIRE(arr.items[i]->as_unsigned() == i + 1); + } + + const std::string expected_repr = R"(Array[5]: + Unsigned: 1 + Unsigned: 2 + Unsigned: 3 + Unsigned: 4 + Unsigned: 5)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [-1, -2, -3]") +{ + auto cbor_bytes = ccf::ds::from_hex("83202122"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 3); + + REQUIRE(arr.items[0]->as_signed() == -1); + REQUIRE(arr.items[1]->as_signed() == -2); + REQUIRE(arr.items[2]->as_signed() == -3); + + const std::string expected_repr = R"(Array[3]: + Signed: -1 + Signed: -2 + Signed: -3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array ['a', 'b', 'c']") +{ + auto cbor_bytes = ccf::ds::from_hex("83616161626163"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 3); + + REQUIRE(arr.items[0]->as_string() == "a"); + REQUIRE(arr.items[1]->as_string() == "b"); + REQUIRE(arr.items[2]->as_string() == "c"); + + const std::string expected_repr = R"(Array[3]: + String: "a" + String: "b" + String: "c")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [b'x', b'y', b'z']") +{ + auto cbor_bytes = ccf::ds::from_hex("8341784179417a"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 3); + + auto bytes0 = arr.items[0]->as_bytes(); + REQUIRE(bytes0.size() == 1); + REQUIRE(bytes0[0] == 'x'); + + auto bytes1 = arr.items[1]->as_bytes(); + REQUIRE(bytes1.size() == 1); + REQUIRE(bytes1[0] == 'y'); + + auto bytes2 = arr.items[2]->as_bytes(); + REQUIRE(bytes2.size() == 1); + REQUIRE(bytes2[0] == 'z'); + + const std::string expected_repr = R"(Array[3]: + Bytes[1]: 78 + Bytes[1]: 79 + Bytes[1]: 7a)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [True, False, None]") +{ + auto cbor_bytes = ccf::ds::from_hex("83f5f4f6"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 3); + + REQUIRE(arr.items[0]->as_simple() == SimpleValue::True); + REQUIRE(arr.items[1]->as_simple() == SimpleValue::False); + REQUIRE(arr.items[2]->as_simple() == SimpleValue::Null); + + const std::string expected_repr = R"(Array[3]: + Simple: True + Simple: False + Simple: Null)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [1, 'two', b'3', True, None]") +{ + auto cbor_bytes = ccf::ds::from_hex("85016374776f4133f5f6"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 5); + + REQUIRE(arr.items[0]->as_unsigned() == 1); + + REQUIRE(arr.items[1]->as_string() == "two"); + + auto bytes = arr.items[2]->as_bytes(); + REQUIRE(bytes.size() == 1); + REQUIRE(bytes[0] == '3'); + + REQUIRE(arr.items[3]->as_simple() == SimpleValue::True); + + REQUIRE(arr.items[4]->as_simple() == SimpleValue::Null); + + const std::string expected_repr = R"(Array[5]: + Unsigned: 1 + String: "two" + Bytes[1]: 33 + Simple: True + Simple: Null)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [0, -1, 42, -100, 65535]") +{ + auto cbor_bytes = ccf::ds::from_hex("850020182a386319ffff"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 5); + + REQUIRE(arr.items[0]->as_unsigned() == 0); + + REQUIRE(arr.items[1]->as_signed() == -1); + + REQUIRE(arr.items[2]->as_unsigned() == 42); + + REQUIRE(arr.items[3]->as_signed() == -100); + + REQUIRE(arr.items[4]->as_unsigned() == 65535); + + const std::string expected_repr = R"(Array[5]: + Unsigned: 0 + Signed: -1 + Unsigned: 42 + Signed: -100 + Unsigned: 65535)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: nested array [[1, 2], [3, 4], [5, 6]]") +{ + auto cbor_bytes = ccf::ds::from_hex("83820102820304820506"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 3); + + for (size_t i = 0; i < 3; i++) + { + const auto& inner = std::get(arr.items[i]->value); + REQUIRE(inner.items.size() == 2); + + REQUIRE(inner.items[0]->as_unsigned() == i * 2 + 1); + REQUIRE(inner.items[1]->as_unsigned() == i * 2 + 2); + } + + const std::string expected_repr = R"(Array[3]: + Array[2]: + Unsigned: 1 + Unsigned: 2 + Array[2]: + Unsigned: 3 + Unsigned: 4 + Array[2]: + Unsigned: 5 + Unsigned: 6)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: deeply nested array [1, [2, 3], 4, [5, [6, 7]]]") +{ + auto cbor_bytes = ccf::ds::from_hex("8401820203048205820607"); + auto value = parse(cbor_bytes); + + const auto& arr = std::get(value->value); + REQUIRE(arr.items.size() == 4); + + REQUIRE(arr.items[0]->as_unsigned() == 1); + + const auto& arr1 = std::get(arr.items[1]->value); + REQUIRE(arr1.items.size() == 2); + REQUIRE(arr1.items[0]->as_unsigned() == 2); + REQUIRE(arr1.items[1]->as_unsigned() == 3); + + REQUIRE(arr.items[2]->as_unsigned() == 4); + + const auto& arr3 = std::get(arr.items[3]->value); + REQUIRE(arr3.items.size() == 2); + REQUIRE(arr3.items[0]->as_unsigned() == 5); + + const auto& arr3_1 = std::get(arr3.items[1]->value); + REQUIRE(arr3_1.items.size() == 2); + REQUIRE(arr3_1.items[0]->as_unsigned() == 6); + REQUIRE(arr3_1.items[1]->as_unsigned() == 7); + + const std::string expected_repr = R"(Array[4]: + Unsigned: 1 + Array[2]: + Unsigned: 2 + Unsigned: 3 + Unsigned: 4 + Array[2]: + Unsigned: 5 + Array[2]: + Unsigned: 6 + Unsigned: 7)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged array Tag(9100) with [1, 2, 3]") +{ + auto cbor_bytes = ccf::ds::from_hex("d9238c83010203"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9100); + const auto& arr = std::get(item->value); + REQUIRE(arr.items.size() == 3); + REQUIRE(arr.items[0]->as_unsigned() == 1); + REQUIRE(arr.items[1]->as_unsigned() == 2); + REQUIRE(arr.items[2]->as_unsigned() == 3); + + const std::string expected_repr = R"(Tagged[9100]: + Array[3]: + Unsigned: 1 + Unsigned: 2 + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged empty array Tag(9300) with []") +{ + auto cbor_bytes = ccf::ds::from_hex("d9245480"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9300); + const auto& arr = std::get(item->value); + REQUIRE(arr.items.size() == 0); + + const std::string expected_repr = R"(Tagged[9300]: + Array[0]:)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: empty map") +{ + auto cbor_bytes = ccf::ds::from_hex("a0"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 0); + + const std::string expected_repr = "Map[0]:"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {1: 'one', 2: 'two', 3: 'three'}") +{ + auto cbor_bytes = ccf::ds::from_hex("a301636f6e65026374776f03657468726565"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->map_at(make_unsigned(1))->as_string() == "one"); + REQUIRE(value->map_at(make_unsigned(2))->as_string() == "two"); + REQUIRE(value->map_at(make_unsigned(3))->as_string() == "three"); + + const std::string expected_repr = R"(Map[3]: + Key: + Unsigned: 1 + Value: + String: "one" + Key: + Unsigned: 2 + Value: + String: "two" + Key: + Unsigned: 3 + Value: + String: "three")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {0: 100, 1: 200, 2: 300}") +{ + auto cbor_bytes = ccf::ds::from_hex("a30018640118c80219012c"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->map_at(make_unsigned(0))->as_unsigned() == 100); + REQUIRE(value->map_at(make_unsigned(1))->as_unsigned() == 200); + REQUIRE(value->map_at(make_unsigned(2))->as_unsigned() == 300); + + const std::string expected_repr = R"(Map[3]: + Key: + Unsigned: 0 + Value: + Unsigned: 100 + Key: + Unsigned: 1 + Value: + Unsigned: 200 + Key: + Unsigned: 2 + Value: + Unsigned: 300)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'a': 1, 'b': 2, 'c': 3}") +{ + auto cbor_bytes = ccf::ds::from_hex("a3616101616202616303"); + auto value = parse(cbor_bytes); + + const auto& map = std::get(value->value); + REQUIRE(map.items.size() == 3); + + REQUIRE(map.items[0].first->as_string() == "a"); + REQUIRE(map.items[0].second->as_unsigned() == 1); + REQUIRE(map.items[1].first->as_string() == "b"); + REQUIRE(map.items[1].second->as_unsigned() == 2); + REQUIRE(map.items[2].first->as_string() == "c"); + REQUIRE(map.items[2].second->as_unsigned() == 3); + + const std::string expected_repr = R"(Map[3]: + Key: + String: "a" + Value: + Unsigned: 1 + Key: + String: "b" + Value: + Unsigned: 2 + Key: + String: "c" + Value: + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'x': 'y', 'foo': 'bar'}") +{ + auto cbor_bytes = ccf::ds::from_hex("a26178617963666f6f63626172"); + auto value = parse(cbor_bytes); + + const auto& map = std::get(value->value); + REQUIRE(map.items.size() == 2); + + REQUIRE(map.items[0].first->as_string() == "x"); + REQUIRE(map.items[0].second->as_string() == "y"); + REQUIRE(map.items[1].first->as_string() == "foo"); + REQUIRE(map.items[1].second->as_string() == "bar"); + + const std::string expected_repr = R"(Map[2]: + Key: + String: "x" + Value: + String: "y" + Key: + String: "foo" + Value: + String: "bar")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'enabled': True, 'disabled': False, 'unknown': None}") +{ + auto cbor_bytes = ccf::ds::from_hex( + "a367656e61626c6564f56864697361626c6564f467756e6b6e6f776ef6"); + auto value = parse(cbor_bytes); + + const auto& map = std::get(value->value); + REQUIRE(map.items.size() == 3); + + REQUIRE(map.items[0].first->as_string() == "enabled"); + REQUIRE(map.items[0].second->as_simple() == SimpleValue::True); + REQUIRE(map.items[1].first->as_string() == "disabled"); + REQUIRE(map.items[1].second->as_simple() == SimpleValue::False); + REQUIRE(map.items[2].first->as_string() == "unknown"); + REQUIRE(map.items[2].second->as_simple() == SimpleValue::Null); + + const std::string expected_repr = R"(Map[3]: + Key: + String: "enabled" + Value: + Simple: True + Key: + String: "disabled" + Value: + Simple: False + Key: + String: "unknown" + Value: + Simple: Null)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {-1: 'minus one', -10: 'minus ten'}") +{ + auto cbor_bytes = + ccf::ds::from_hex("a220696d696e7573206f6e6529696d696e75732074656e"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 2); + + REQUIRE(value->map_at(make_signed(-1))->as_string() == "minus one"); + REQUIRE(value->map_at(make_signed(-10))->as_string() == "minus ten"); + + const std::string expected_repr = R"(Map[2]: + Key: + Signed: -1 + Value: + String: "minus one" + Key: + Signed: -10 + Value: + String: "minus ten")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array with map [1, {'a': 2}, 3]") +{ + auto cbor_bytes = ccf::ds::from_hex("8301a161610203"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->array_at(0)->as_unsigned() == 1); + + const auto& map = value->array_at(1); + REQUIRE(map->size() == 1); + REQUIRE(map->map_at(make_string("a"))->as_unsigned() == 2); + + REQUIRE(value->array_at(2)->as_unsigned() == 3); + + const std::string expected_repr = R"(Array[3]: + Unsigned: 1 + Map[1]: + Key: + String: "a" + Value: + Unsigned: 2 + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array of maps [{'x': 1}, {'y': 2}, {'z': 3}]") +{ + auto cbor_bytes = ccf::ds::from_hex("83a1617801a1617902a1617a03"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->array_at(0)->size() == 1); + REQUIRE(value->array_at(0)->map_at(make_string("x"))->as_unsigned() == 1); + + REQUIRE(value->array_at(1)->size() == 1); + REQUIRE(value->array_at(1)->map_at(make_string("y"))->as_unsigned() == 2); + + REQUIRE(value->array_at(2)->size() == 1); + REQUIRE(value->array_at(2)->map_at(make_string("z"))->as_unsigned() == 3); + + const std::string expected_repr = R"(Array[3]: + Map[1]: + Key: + String: "x" + Value: + Unsigned: 1 + Map[1]: + Key: + String: "y" + Value: + Unsigned: 2 + Map[1]: + Key: + String: "z" + Value: + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map with array {'items': [1, 2, 3]}") +{ + auto cbor_bytes = ccf::ds::from_hex("a1656974656d7383010203"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 1); + + const auto& arr = value->map_at(make_string("items")); + REQUIRE(arr->size() == 3); + REQUIRE(arr->array_at(0)->as_unsigned() == 1); + REQUIRE(arr->array_at(1)->as_unsigned() == 2); + REQUIRE(arr->array_at(2)->as_unsigned() == 3); + + const std::string expected_repr = R"(Map[1]: + Key: + String: "items" + Value: + Array[3]: + Unsigned: 1 + Unsigned: 2 + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map with multiple arrays") +{ + auto cbor_bytes = ccf::ds::from_hex("a361618201026162820304616382050682"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + const auto& arr_a = value->map_at(make_string("a")); + REQUIRE(arr_a->size() == 2); + REQUIRE(arr_a->array_at(0)->as_unsigned() == 1); + REQUIRE(arr_a->array_at(1)->as_unsigned() == 2); + + const auto& arr_b = value->map_at(make_string("b")); + REQUIRE(arr_b->size() == 2); + REQUIRE(arr_b->array_at(0)->as_unsigned() == 3); + REQUIRE(arr_b->array_at(1)->as_unsigned() == 4); + + const auto& arr_c = value->map_at(make_string("c")); + REQUIRE(arr_c->size() == 2); + REQUIRE(arr_c->array_at(0)->as_unsigned() == 5); + REQUIRE(arr_c->array_at(1)->as_unsigned() == 6); + + const std::string expected_repr = R"(Map[3]: + Key: + String: "a" + Value: + Array[2]: + Unsigned: 1 + Unsigned: 2 + Key: + String: "b" + Value: + Array[2]: + Unsigned: 3 + Unsigned: 4 + Key: + String: "c" + Value: + Array[2]: + Unsigned: 5 + Unsigned: 6)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged map Tag(10000) with {'a': 1, 'b': 2}") +{ + auto cbor_bytes = ccf::ds::from_hex("d92710a26161016162021b"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(10000); + REQUIRE(item->size() == 2); + REQUIRE(item->map_at(make_string("a"))->as_unsigned() == 1); + REQUIRE(item->map_at(make_string("b"))->as_unsigned() == 2); + + const std::string expected_repr = R"(Tagged[10000]: + Map[2]: + Key: + String: "a" + Value: + Unsigned: 1 + Key: + String: "b" + Value: + Unsigned: 2)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE( + "CBOR: array [True, {'count': 42, 'label': 'items', 'active': True}, None]") +{ + auto cbor_bytes = ccf::ds::from_hex( + "83f5a365636f756e74182a656c6162656c656974656d7366616374697665f5f6"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->array_at(0)->as_simple() == SimpleValue::True); + + const auto& map = value->array_at(1); + REQUIRE(map->size() == 3); + REQUIRE(map->map_at(make_string("count"))->as_unsigned() == 42); + REQUIRE(map->map_at(make_string("label"))->as_string() == "items"); + REQUIRE(map->map_at(make_string("active"))->as_simple() == SimpleValue::True); + + REQUIRE(value->array_at(2)->as_simple() == SimpleValue::Null); + + const std::string expected_repr = R"(Array[3]: + Simple: True + Map[3]: + Key: + String: "count" + Value: + Unsigned: 42 + Key: + String: "label" + Value: + String: "items" + Key: + String: "active" + Value: + Simple: True + Simple: Null)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array ['header', {'id': 123, 'name': 'test'}, 'footer']") +{ + auto cbor_bytes = ccf::ds::from_hex( + "8366686561646572a2626964187b646e616d65647465737466666f6f746572"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->array_at(0)->as_string() == "header"); + + const auto& map = value->array_at(1); + REQUIRE(map->size() == 2); + REQUIRE(map->map_at(make_string("id"))->as_unsigned() == 123); + REQUIRE(map->map_at(make_string("name"))->as_string() == "test"); + + REQUIRE(value->array_at(2)->as_string() == "footer"); + + const std::string expected_repr = R"(Array[3]: + String: "header" + Map[2]: + Key: + String: "id" + Value: + Unsigned: 123 + Key: + String: "name" + Value: + String: "test" + String: "footer")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [1, 2, {'nested': {'key': 'value'}}, 3]") +{ + auto cbor_bytes = + ccf::ds::from_hex("840102a1666e6573746564a1636b65796576616c756503"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 4); + + REQUIRE(value->array_at(0)->as_unsigned() == 1); + REQUIRE(value->array_at(1)->as_unsigned() == 2); + + const auto& map = value->array_at(2); + REQUIRE(map->size() == 1); + + const auto& nested_map = map->map_at(make_string("nested")); + REQUIRE(nested_map->size() == 1); + REQUIRE(nested_map->map_at(make_string("key"))->as_string() == "value"); + + REQUIRE(value->array_at(3)->as_unsigned() == 3); + + const std::string expected_repr = R"(Array[4]: + Unsigned: 1 + Unsigned: 2 + Map[1]: + Key: + String: "nested" + Value: + Map[1]: + Key: + String: "key" + Value: + String: "value" + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: array [1, Tag(9600, 'tagged'), 3]") +{ + auto cbor_bytes = ccf::ds::from_hex("8301d925806674616767656403"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->array_at(0)->as_unsigned() == 1); + + const auto& item = value->array_at(1)->tag_at(9600); + REQUIRE(item->as_string() == "tagged"); + + REQUIRE(value->array_at(2)->as_unsigned() == 3); + + const std::string expected_repr = R"(Array[3]: + Unsigned: 1 + Tagged[9600]: + String: "tagged" + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: large bytes array (16 bytes)") +{ + auto cbor_bytes = ccf::ds::from_hex("50000102030405060708090a0b0c0d0e0f"); + auto value = parse(cbor_bytes); + + auto bytes = value->as_bytes(); + REQUIRE(bytes.size() == 16); + for (size_t i = 0; i < 16; i++) + { + REQUIRE(bytes[i] == static_cast(i)); + } + + const std::string expected_repr = + "Bytes[16]: 000102030405060708090a0b0c0d0e0f"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map with mixed key types") +{ + auto cbor_bytes = ccf::ds::from_hex("a301636e756d6373747202456279746573f5"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + REQUIRE(value->map_at(make_unsigned(1))->as_string() == "num"); + REQUIRE(value->map_at(make_string("str"))->as_unsigned() == 2); + + const auto bytes_key = ccf::ds::from_hex("6279746573"); + REQUIRE( + value->map_at(make_bytes(bytes_key))->as_simple() == SimpleValue::True); + + const std::string expected_repr = R"(Map[3]: + Key: + Unsigned: 1 + Value: + String: "num" + Key: + String: "str" + Value: + Unsigned: 2 + Key: + Bytes[5]: 6279746573 + Value: + Simple: True)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'a': 1, 'b': 'two', 'c': b'3', 'd': False}") +{ + auto cbor_bytes = ccf::ds::from_hex("a461610161626374776f616341336164f4"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 4); + + REQUIRE(value->map_at(make_string("a"))->as_unsigned() == 1); + REQUIRE(value->map_at(make_string("b"))->as_string() == "two"); + + const auto byte_value = value->map_at(make_string("c"))->as_bytes(); + REQUIRE(byte_value.size() == 1); + REQUIRE(byte_value[0] == 0x33); + + REQUIRE(value->map_at(make_string("d"))->as_simple() == SimpleValue::False); + + const std::string expected_repr = R"(Map[4]: + Key: + String: "a" + Value: + Unsigned: 1 + Key: + String: "b" + Value: + String: "two" + Key: + String: "c" + Value: + Bytes[1]: 33 + Key: + String: "d" + Value: + Simple: False)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'key1': b'value1', 'key2': b'value2'}") +{ + auto cbor_bytes = + ccf::ds::from_hex("a2646b6579314676616c756531646b6579324676616c756532"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 2); + + const auto value1 = value->map_at(make_string("key1"))->as_bytes(); + REQUIRE(value1.size() == 6); + + const auto value2 = value->map_at(make_string("key2"))->as_bytes(); + REQUIRE(value2.size() == 6); + + const std::string expected_repr = R"(Map[2]: + Key: + String: "key1" + Value: + Bytes[6]: 76616c756531 + Key: + String: "key2" + Value: + Bytes[6]: 76616c756532)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {1: [10, 20], 2: ['a', 'b'], 3: [b'x', b'y']}") +{ + auto cbor_bytes = ccf::ds::from_hex("a301820a14028261616162038241784179"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + const auto& arr0 = value->map_at(make_unsigned(1)); + REQUIRE(arr0->size() == 2); + REQUIRE(arr0->array_at(0)->as_unsigned() == 10); + REQUIRE(arr0->array_at(1)->as_unsigned() == 20); + + const auto& arr1 = value->map_at(make_unsigned(2)); + REQUIRE(arr1->size() == 2); + REQUIRE(arr1->array_at(0)->as_string() == "a"); + REQUIRE(arr1->array_at(1)->as_string() == "b"); + + const auto& arr2 = value->map_at(make_unsigned(3)); + REQUIRE(arr2->size() == 2); + REQUIRE(arr2->array_at(0)->as_bytes()[0] == 'x'); + REQUIRE(arr2->array_at(1)->as_bytes()[0] == 'y'); + + const std::string expected_repr = R"(Map[3]: + Key: + Unsigned: 1 + Value: + Array[2]: + Unsigned: 10 + Unsigned: 20 + Key: + Unsigned: 2 + Value: + Array[2]: + String: "a" + String: "b" + Key: + Unsigned: 3 + Value: + Array[2]: + Bytes[1]: 78 + Bytes[1]: 79)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {1: b'data1', 2: b'data2'}") +{ + auto cbor_bytes = ccf::ds::from_hex("a20145646174613102456461746132"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 2); + + const auto data1 = value->map_at(make_unsigned(1))->as_bytes(); + REQUIRE(data1.size() == 5); + + const auto data2 = value->map_at(make_unsigned(2))->as_bytes(); + REQUIRE(data2.size() == 5); + + const std::string expected_repr = R"(Map[2]: + Key: + Unsigned: 1 + Value: + Bytes[5]: 6461746131 + Key: + Unsigned: 2 + Value: + Bytes[5]: 6461746132)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'nested': [1, [2, 3], 4]}") +{ + auto cbor_bytes = ccf::ds::from_hex("a1666e6573746564830182020304"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 1); + + const auto& arr = value->map_at(make_string("nested")); + REQUIRE(arr->size() == 3); + + REQUIRE(arr->array_at(0)->as_unsigned() == 1); + + const auto& nested = arr->array_at(1); + REQUIRE(nested->size() == 2); + REQUIRE(nested->array_at(0)->as_unsigned() == 2); + REQUIRE(nested->array_at(1)->as_unsigned() == 3); + + REQUIRE(arr->array_at(2)->as_unsigned() == 4); + + const std::string expected_repr = R"(Map[1]: + Key: + String: "nested" + Value: + Array[3]: + Unsigned: 1 + Array[2]: + Unsigned: 2 + Unsigned: 3 + Unsigned: 4)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'tagged': Tag(9500, 'value')}") +{ + auto cbor_bytes = ccf::ds::from_hex("a166746167676564d9251c6576616c7565"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 1); + + const auto& tagged_value = value->map_at(make_string("tagged")); + const auto& item = tagged_value->tag_at(9500); + REQUIRE(item->as_string() == "value"); + + const std::string expected_repr = R"(Map[1]: + Key: + String: "tagged" + Value: + Tagged[9500]: + String: "value")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE( + "CBOR: map {'numbers': [1, 2, 3], 'strings': ['a', 'b'], 'flags': [True, " + "False]}") +{ + auto cbor_bytes = ccf::ds::from_hex( + "a3676e756d626572738301020367737472696e6773826161616265666c61677382f5f4"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + const auto& nums = value->map_at(make_string("numbers")); + REQUIRE(nums->size() == 3); + REQUIRE(nums->array_at(0)->as_unsigned() == 1); + REQUIRE(nums->array_at(1)->as_unsigned() == 2); + REQUIRE(nums->array_at(2)->as_unsigned() == 3); + + const auto& strs = value->map_at(make_string("strings")); + REQUIRE(strs->size() == 2); + REQUIRE(strs->array_at(0)->as_string() == "a"); + REQUIRE(strs->array_at(1)->as_string() == "b"); + + const auto& flags = value->map_at(make_string("flags")); + REQUIRE(flags->size() == 2); + REQUIRE(flags->array_at(0)->as_simple() == SimpleValue::True); + REQUIRE(flags->array_at(1)->as_simple() == SimpleValue::False); + + const std::string expected_repr = R"(Map[3]: + Key: + String: "numbers" + Value: + Array[3]: + Unsigned: 1 + Unsigned: 2 + Unsigned: 3 + Key: + String: "strings" + Value: + Array[2]: + String: "a" + String: "b" + Key: + String: "flags" + Value: + Array[2]: + Simple: True + Simple: False)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: map {'empty': [], 'single': [42], 'multiple': [1, 2, 3]}") +{ + auto cbor_bytes = ccf::ds::from_hex( + "a365656d707479806673696e676c6581182a686d756c7469706c6583010203"); + auto value = parse(cbor_bytes); + + REQUIRE(value->size() == 3); + + const auto& empty = value->map_at(make_string("empty")); + REQUIRE(empty->size() == 0); + + const auto& single = value->map_at(make_string("single")); + REQUIRE(single->size() == 1); + REQUIRE(single->array_at(0)->as_unsigned() == 42); + + const auto& multiple = value->map_at(make_string("multiple")); + REQUIRE(multiple->size() == 3); + REQUIRE(multiple->array_at(0)->as_unsigned() == 1); + REQUIRE(multiple->array_at(1)->as_unsigned() == 2); + REQUIRE(multiple->array_at(2)->as_unsigned() == 3); + + const std::string expected_repr = R"(Map[3]: + Key: + String: "empty" + Value: + Array[0]: + Key: + String: "single" + Value: + Array[1]: + Unsigned: 42 + Key: + String: "multiple" + Value: + Array[3]: + Unsigned: 1 + Unsigned: 2 + Unsigned: 3)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: large string (100 'A's)") +{ + auto cbor_bytes = ccf::ds::from_hex( + "786441414141414141414141414141414141414141414141414141414141414141414141" + "41414141414141414141414141414141414141414141414141414141414141414141414141" + "4141414141414141414141414141414141414141414141414141414141414141"); + auto value = parse(cbor_bytes); + + auto str = value->as_string(); + REQUIRE(str.size() == 100); + for (size_t i = 0; i < 100; i++) + { + REQUIRE(str[i] == 'A'); + } + + const std::string expected_repr = + R"(String: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged array Tag(9200, ['a', 'b', 'c'])") +{ + auto cbor_bytes = ccf::ds::from_hex("d923f083616161626163"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9200); + REQUIRE(item->size() == 3); + REQUIRE(item->array_at(0)->as_string() == "a"); + REQUIRE(item->array_at(1)->as_string() == "b"); + REQUIRE(item->array_at(2)->as_string() == "c"); + + const std::string expected_repr = R"(Tagged[9200]: + Array[3]: + String: "a" + String: "b" + String: "c")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged array Tag(9400, [1, 'two', b'3'])") +{ + auto cbor_bytes = ccf::ds::from_hex("d924b883016374776f4133"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(9400); + REQUIRE(item->size() == 3); + REQUIRE(item->array_at(0)->as_unsigned() == 1); + REQUIRE(item->array_at(1)->as_string() == "two"); + REQUIRE(item->array_at(2)->as_bytes()[0] == 0x33); + + const std::string expected_repr = R"(Tagged[9400]: + Array[3]: + Unsigned: 1 + String: "two" + Bytes[1]: 33)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: tagged array Tag(20000, [{'x': 1}, {'y': 2}])") +{ + auto cbor_bytes = ccf::ds::from_hex("d94e2082a1617801a1617902"); + auto value = parse(cbor_bytes); + + const auto& item = value->tag_at(20000); + const auto& arr = std::get(item->value); + REQUIRE(arr.items.size() == 2); + + const std::string expected_repr = R"(Tagged[20000]: + Array[2]: + Map[1]: + Key: + String: "x" + Value: + Unsigned: 1 + Map[1]: + Key: + String: "y" + Value: + Unsigned: 2)"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} +TEST_CASE("CBOR: helper function make_unsigned") +{ + auto value = make_unsigned(42); + REQUIRE(value != nullptr); + REQUIRE(value->as_unsigned() == 42); + + const std::string expected_repr = "Unsigned: 42"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: helper function make_signed") +{ + auto value = make_signed(-42); + REQUIRE(value != nullptr); + REQUIRE(value->as_signed() == -42); + + const std::string expected_repr = "Signed: -42"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: helper function make_string") +{ + auto value = make_string("hello"); + REQUIRE(value != nullptr); + REQUIRE(value->as_string() == "hello"); + + const std::string expected_repr = R"(String: "hello")"; + const std::string result = to_string(value); + REQUIRE(result == expected_repr); +} + +TEST_CASE("CBOR: error - invalid data") +{ + auto cbor_bytes = ccf::ds::from_hex("18"); + REQUIRE_THROWS_AS(parse(cbor_bytes), CBORDecodeError); +} + +TEST_CASE("CBOR: error - array out of bounds") +{ + auto cbor_bytes = ccf::ds::from_hex("83010203"); + auto value = parse(cbor_bytes); + REQUIRE_THROWS_AS((void)value->array_at(10), CBORDecodeError); +} + +TEST_CASE("CBOR: error - unexpected tag") +{ + auto cbor_bytes = ccf::ds::from_hex("d9232b42cafe"); // Tag 9003 + auto value = parse(cbor_bytes); + + REQUIRE_THROWS_AS((void)value->tag_at(9004), CBORDecodeError); +} + +TEST_CASE("CBOR: back and forth (un)signed") +{ + auto cbor_bytes = ccf::ds::from_hex("820120"); + auto value = parse(cbor_bytes); + + { + auto s = make_signed(64); + REQUIRE(s->as_signed() == 64); + REQUIRE(s->as_unsigned() == 64); + } + + { + auto u = make_unsigned(64); + REQUIRE(u->as_unsigned() == 64); + REQUIRE(u->as_signed() == 64); + } + + { + auto s = make_signed(-64); + REQUIRE(s->as_signed() == -64); + REQUIRE_THROWS_AS((void)s->as_unsigned(), CBORDecodeError); + } + + { + const auto value = 1ull + std::numeric_limits::max(); + auto u = make_unsigned(value); + REQUIRE(u->as_unsigned() == value); + REQUIRE_THROWS_AS((void)u->as_signed(), CBORDecodeError); + } +} + +TEST_CASE("CBOR: back and forth (un)signed") +{ + auto cbor_bytes = ccf::ds::from_hex( + "A320622D311B7FFFFFFFFFFFFFFF73393232333337323033363835343737353830371B80" + "00" + "0000000000007339323233333732303336383534373735383038"); + auto value = parse(cbor_bytes); + + // Signed only + REQUIRE(value->map_at(make_signed(-1))->as_string() == "-1"); + + // Both have to match + REQUIRE( + value->map_at(make_signed(9223372036854775807ll))->as_string() == + "9223372036854775807"); + + REQUIRE( + value->map_at(make_unsigned(9223372036854775807ull))->as_string() == + "9223372036854775807"); + + // Unsigned only + REQUIRE( + value->map_at(make_unsigned(9223372036854775808ull))->as_string() == + "9223372036854775808"); +} + +TEST_CASE("CBOR: throw with context") +{ + auto v = make_signed(105); + + const std::string context = "Custom enough context"; + const std::string err = "Not a string value"; + const std::string expected_err = err + ": " + context; + REQUIRE_THROWS_WITH_AS( + rethrow_with_context([&]() { std::ignore = v->as_string(); }, context), + expected_err.c_str(), + CBORDecodeError); +} \ No newline at end of file diff --git a/src/node/cose_common.h b/src/node/cose_common.h index 8bcfda5dac8..7eb9a638750 100644 --- a/src/node/cose_common.h +++ b/src/node/cose_common.h @@ -17,10 +17,13 @@ namespace ccf::cose { namespace headers { + // https://www.iana.org/assignments/cose/cose.xhtml static constexpr int64_t PARAM_ALG = 1; static constexpr int64_t PARAM_CONTENT_TYPE = 3; static constexpr int64_t PARAM_KID = 4; static constexpr int64_t PARAM_X5CHAIN = 33; + // https://www.ietf.org/archive/id/draft-ietf-cose-hash-envelope-10.html#section-4 + static constexpr int64_t PARAM_CONTENT_TYPE_HASH_ENVELOPE = 259; static constexpr int64_t PARAM_VDP = 396; static constexpr int64_t PARAM_INCLUSION_PROOFS = -1; diff --git a/src/node/uvm_endorsements.cpp b/src/node/uvm_endorsements.cpp index c419d4e0ba5..baa69f2d1f7 100644 --- a/src/node/uvm_endorsements.cpp +++ b/src/node/uvm_endorsements.cpp @@ -58,122 +58,164 @@ namespace ccf for (size_t i = 0; i < x5chain_value->size(); ++i) { const auto x5chain_ctx = "x5chain[" + std::to_string(i) + "]"; - const auto& bytes = - x5chain_value->array_at(i, x5chain_ctx)->as_bytes(x5chain_ctx); + const auto& bytes = ccf::cbor::rethrow_with_context( + [&]() { return x5chain_value->array_at(i)->as_bytes(); }, + x5chain_ctx); chain.emplace_back(bytes.begin(), bytes.end()); } } catch (const ccf::cbor::CBORDecodeError&) { - auto bytes = x5chain_value->as_bytes("x5chain"); + auto bytes = ccf::cbor::rethrow_with_context( + [&]() { return x5chain_value->as_bytes(); }, "x5chain"); chain.emplace_back(bytes.begin(), bytes.end()); } return chain; } UvmEndorsementsProtectedHeader decode_protected_header( - const std::vector& uvm_endorsements_raw) + std::span raw_endorsements) { - std::span as_span( - uvm_endorsements_raw.data(), uvm_endorsements_raw.size()); - - auto parsed = ccf::cbor::parse_value(as_span, "COSE envelope"); - const auto& cose_array = - parsed->tag_at(CBOR_TAG_COSE_SIGN1, "COSE_Sign1 tag"); + auto parsed = ccf::cbor::rethrow_with_context( + [&]() { return ccf::cbor::parse(raw_endorsements); }, + "COSE envelope"); + const auto& cose_array = ccf::cbor::rethrow_with_context( + [&]() -> const ccf::cbor::Value& { + return parsed->tag_at(CBOR_TAG_COSE_SIGN1); + }, + "COSE_Sign1 tag"); constexpr std::string_view phdr_context{"COSE_Sign1[0]"}; - const auto& phdr_bytes = cose_array->array_at(0, phdr_context); - auto phdr_bytes_span = phdr_bytes->as_bytes(phdr_context); - auto parsed_phdr = ccf::cbor::parse_value(phdr_bytes_span, "phdr CBOR"); + const auto& phdr_bytes = ccf::cbor::rethrow_with_context( + [&]() -> const ccf::cbor::Value& { return cose_array->array_at(0); }, + phdr_context); + auto phdr_bytes_span = ccf::cbor::rethrow_with_context( + [&]() { return phdr_bytes->as_bytes(); }, phdr_context); + auto parsed_phdr = ccf::cbor::rethrow_with_context( + [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "phdr CBOR"); UvmEndorsementsProtectedHeader result; - const auto alg_context = "phdr: " + std::to_string(headers::PARAM_ALG); - const auto& alg = parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_ALG), alg_context); - result.alg = alg->as_signed(alg_context); - - const auto ct_context = - "phdr: " + std::to_string(headers::PARAM_CONTENT_TYPE); - const auto& content_type = parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_CONTENT_TYPE), ct_context); - result.content_type = std::string(content_type->as_string(ct_context)); - - const auto x5chain_context = - "phdr: " + std::to_string(headers::PARAM_X5CHAIN); - result.x5_chain = parse_x5chain(parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN), x5chain_context)); - - constexpr std::string_view iss_context{"phdr: iss"}; - const auto& iss = - parsed_phdr->map_at(ccf::cbor::make_string("iss"), iss_context); - result.iss = iss->as_string(iss_context); - - constexpr std::string_view feed_context{"phdr: feed"}; - const auto& feed = - parsed_phdr->map_at(ccf::cbor::make_string("feed"), feed_context); - result.feed = std::string(feed->as_string(feed_context)); + result.alg = ccf::cbor::rethrow_with_context( + [&]() { + return parsed_phdr + ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) + ->as_signed(); + }, + "phdr: " + std::to_string(headers::PARAM_ALG)); + + result.content_type = ccf::cbor::rethrow_with_context( + [&]() { + return std::string( + parsed_phdr + ->map_at(ccf::cbor::make_unsigned(headers::PARAM_CONTENT_TYPE)) + ->as_string()); + }, + "phdr: " + std::to_string(headers::PARAM_CONTENT_TYPE)); + + result.x5_chain = ccf::cbor::rethrow_with_context( + [&]() { + return parse_x5chain(parsed_phdr->map_at( + ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); + }, + "phdr: " + std::to_string(headers::PARAM_X5CHAIN)); + + result.iss = ccf::cbor::rethrow_with_context( + [&]() { + return parsed_phdr->map_at(ccf::cbor::make_string("iss")) + ->as_string(); + }, + "phdr: iss"); + + result.feed = ccf::cbor::rethrow_with_context( + [&]() { + return std::string( + parsed_phdr->map_at(ccf::cbor::make_string("feed"))->as_string()); + }, + "phdr: feed"); return result; } std::pair decode_protected_header_with_cwt( - const std::vector& uvm_endorsements_raw) + std::span raw_endorsements) { - std::span as_span( - uvm_endorsements_raw.data(), uvm_endorsements_raw.size()); - - auto parsed = ccf::cbor::parse_value(as_span, "COSE envelope"); - const auto& cose_array = - parsed->tag_at(CBOR_TAG_COSE_SIGN1, "COSE_Sign1 tag"); + auto parsed = ccf::cbor::rethrow_with_context( + [&]() { return ccf::cbor::parse(raw_endorsements); }, + "COSE envelope"); + const auto& cose_array = ccf::cbor::rethrow_with_context( + [&]() -> const ccf::cbor::Value& { + return parsed->tag_at(CBOR_TAG_COSE_SIGN1); + }, + "COSE_Sign1 tag"); constexpr std::string_view phdr_context{"COSE_Sign1[0]"}; - const auto& phdr_bytes = cose_array->array_at(0, phdr_context); - auto phdr_bytes_span = phdr_bytes->as_bytes(phdr_context); + const auto& phdr_bytes = ccf::cbor::rethrow_with_context( + [&]() -> const ccf::cbor::Value& { return cose_array->array_at(0); }, + phdr_context); + auto phdr_bytes_span = ccf::cbor::rethrow_with_context( + [&]() { return phdr_bytes->as_bytes(); }, phdr_context); - auto parsed_phdr = ccf::cbor::parse_value(phdr_bytes_span, "phdr CBOR"); + auto parsed_phdr = ccf::cbor::rethrow_with_context( + [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "phdr CBOR"); UvmEndorsementsProtectedHeader result; - const auto alg_context = "phdr: " + std::to_string(headers::PARAM_ALG); - const auto& alg = parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_ALG), alg_context); - result.alg = alg->as_signed(alg_context); - - const auto ct_context = "phdr: " + std::to_string(259); - const auto& content_type = - parsed_phdr->map_at(ccf::cbor::make_unsigned(259), ct_context); - result.content_type = std::string(content_type->as_string(ct_context)); - - const auto x5chain_context = - "phdr: " + std::to_string(headers::PARAM_X5CHAIN); - result.x5_chain = parse_x5chain(parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN), x5chain_context)); - - const auto cwt_context = - "phdr: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_CWT); - const auto& cwt_claims = parsed_phdr->map_at( - ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT), - cwt_context); - - const auto iss_context = - "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_ISS); - const auto& iss = cwt_claims->map_at( - ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_ISS), - iss_context); - result.iss = std::string(iss->as_string(iss_context)); - - const auto feed_context = - "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_SUB); - const auto& feed = cwt_claims->map_at( - ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_SUB), - feed_context); - result.feed = std::string(feed->as_string(feed_context)); - - constexpr std::string_view svn_context{"cwt: svn"}; - const auto& svn_value = - cwt_claims->map_at(ccf::cbor::make_string("svn"), svn_context); - auto svn = svn_value->as_unsigned(svn_context); + result.alg = ccf::cbor::rethrow_with_context( + [&]() { + return parsed_phdr + ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) + ->as_signed(); + }, + "phdr: " + std::to_string(headers::PARAM_ALG)); + + result.content_type = ccf::cbor::rethrow_with_context( + [&]() { + return std::string(parsed_phdr + ->map_at(ccf::cbor::make_unsigned( + headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)) + ->as_string()); + }, + "phdr: " + std::to_string(headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)); + + result.x5_chain = ccf::cbor::rethrow_with_context( + [&]() { + return parse_x5chain(parsed_phdr->map_at( + ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); + }, + "phdr: " + std::to_string(headers::PARAM_X5CHAIN)); + + const ccf::cbor::Value& cwt_claims = ccf::cbor::rethrow_with_context( + [&]() -> const ccf::cbor::Value& { + return parsed_phdr->map_at( + ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT)); + }, + "phdr: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_CWT)); + + result.iss = ccf::cbor::rethrow_with_context( + [&]() { + return std::string(cwt_claims + ->map_at(ccf::cbor::make_unsigned( + ccf::crypto::COSE_PHEADER_KEY_ISS)) + ->as_string()); + }, + "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_ISS)); + + result.feed = ccf::cbor::rethrow_with_context( + [&]() { + return std::string(cwt_claims + ->map_at(ccf::cbor::make_unsigned( + ccf::crypto::COSE_PHEADER_KEY_SUB)) + ->as_string()); + }, + "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_SUB)); + + uint64_t svn = ccf::cbor::rethrow_with_context( + [&]() { + return cwt_claims->map_at(ccf::cbor::make_string("svn")) + ->as_unsigned(); + }, + "cwt: svn"); return {result, std::to_string(svn)}; } From 57be91260d4965e1189df8df03cce69ba942e7a9 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 7 Jan 2026 11:24:20 +0000 Subject: [PATCH 2/6] Fix signed/unsigned order Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/crypto/cbor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/cbor.cpp b/src/crypto/cbor.cpp index 6a49a5f46d0..1ae4ac5ff18 100644 --- a/src/crypto/cbor.cpp +++ b/src/crypto/cbor.cpp @@ -388,7 +388,7 @@ namespace ccf::cbor if constexpr ( std::is_same_v && std::is_same_v) { - return signed_equals_unsigned(a, b); + return signed_equals_unsigned(b, a); } return false; } From 9f048d2ec9e19c63924bc10c14ab1c6e2b662583 Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Wed, 7 Jan 2026 13:18:06 +0000 Subject: [PATCH 3/6] Code review formatting --- src/node/uvm_endorsements.cpp | 47 ++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/node/uvm_endorsements.cpp b/src/node/uvm_endorsements.cpp index baa69f2d1f7..e8ede546e8a 100644 --- a/src/node/uvm_endorsements.cpp +++ b/src/node/uvm_endorsements.cpp @@ -78,7 +78,7 @@ namespace ccf { auto parsed = ccf::cbor::rethrow_with_context( [&]() { return ccf::cbor::parse(raw_endorsements); }, - "COSE envelope"); + "UVM endorsements COSE envelope"); const auto& cose_array = ccf::cbor::rethrow_with_context( [&]() -> const ccf::cbor::Value& { return parsed->tag_at(CBOR_TAG_COSE_SIGN1); @@ -91,7 +91,8 @@ namespace ccf auto phdr_bytes_span = ccf::cbor::rethrow_with_context( [&]() { return phdr_bytes->as_bytes(); }, phdr_context); auto parsed_phdr = ccf::cbor::rethrow_with_context( - [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "phdr CBOR"); + [&]() { return ccf::cbor::parse(phdr_bytes_span); }, + "Parse protected header in UVM endorsements"); UvmEndorsementsProtectedHeader result; @@ -101,7 +102,9 @@ namespace ccf ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) ->as_signed(); }, - "phdr: " + std::to_string(headers::PARAM_ALG)); + fmt::format( + "Parse alg ({}) in protected header in UVM endorsements", + headers::PARAM_ALG)); result.content_type = ccf::cbor::rethrow_with_context( [&]() { @@ -110,28 +113,32 @@ namespace ccf ->map_at(ccf::cbor::make_unsigned(headers::PARAM_CONTENT_TYPE)) ->as_string()); }, - "phdr: " + std::to_string(headers::PARAM_CONTENT_TYPE)); + fmt::format( + "Parse content-type ({}) in protected header in UVM endorsements", + headers::PARAM_CONTENT_TYPE)); result.x5_chain = ccf::cbor::rethrow_with_context( [&]() { return parse_x5chain(parsed_phdr->map_at( ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); }, - "phdr: " + std::to_string(headers::PARAM_X5CHAIN)); + fmt::format( + "Parse x5chain ({}) in protected header in UVM endorsements", + headers::PARAM_X5CHAIN)); result.iss = ccf::cbor::rethrow_with_context( [&]() { return parsed_phdr->map_at(ccf::cbor::make_string("iss")) ->as_string(); }, - "phdr: iss"); + "Parse iss in protected header in UVM endorsements"); result.feed = ccf::cbor::rethrow_with_context( [&]() { return std::string( parsed_phdr->map_at(ccf::cbor::make_string("feed"))->as_string()); }, - "phdr: feed"); + "Parse feed in protected header in UVM endorsements"); return result; } @@ -167,7 +174,9 @@ namespace ccf ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) ->as_signed(); }, - "phdr: " + std::to_string(headers::PARAM_ALG)); + fmt::format( + "Parse alg ({}) in protected header in UVM endorsements", + headers::PARAM_ALG)); result.content_type = ccf::cbor::rethrow_with_context( [&]() { @@ -176,21 +185,27 @@ namespace ccf headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)) ->as_string()); }, - "phdr: " + std::to_string(headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)); + fmt::format( + "Parse content-type ({}) in protected header in UVM endorsements", + headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)); result.x5_chain = ccf::cbor::rethrow_with_context( [&]() { return parse_x5chain(parsed_phdr->map_at( ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); }, - "phdr: " + std::to_string(headers::PARAM_X5CHAIN)); + fmt::format( + "Parse x5chain ({}) in protected header in UVM endorsements", + headers::PARAM_X5CHAIN)); const ccf::cbor::Value& cwt_claims = ccf::cbor::rethrow_with_context( [&]() -> const ccf::cbor::Value& { return parsed_phdr->map_at( ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT)); }, - "phdr: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_CWT)); + fmt::format( + "Parse CWT ({}) in protected header in UVM endorsements", + ccf::crypto::COSE_PHEADER_KEY_CWT)); result.iss = ccf::cbor::rethrow_with_context( [&]() { @@ -199,7 +214,9 @@ namespace ccf ccf::crypto::COSE_PHEADER_KEY_ISS)) ->as_string()); }, - "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_ISS)); + fmt::format( + "Parse iss ({}) in CWT claims in UVM endorsements", + ccf::crypto::COSE_PHEADER_KEY_ISS)); result.feed = ccf::cbor::rethrow_with_context( [&]() { @@ -208,14 +225,16 @@ namespace ccf ccf::crypto::COSE_PHEADER_KEY_SUB)) ->as_string()); }, - "cwt: " + std::to_string(ccf::crypto::COSE_PHEADER_KEY_SUB)); + fmt::format( + "Parse feed ({}) in CWT claims in UVM endorsements", + ccf::crypto::COSE_PHEADER_KEY_SUB)); uint64_t svn = ccf::cbor::rethrow_with_context( [&]() { return cwt_claims->map_at(ccf::cbor::make_string("svn")) ->as_unsigned(); }, - "cwt: svn"); + "Parse svn in CWT claims in UVM endorsements"); return {result, std::to_string(svn)}; } From 1350c686abfc61f497091be35c270b3d8af29ea8 Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Wed, 7 Jan 2026 14:07:09 +0000 Subject: [PATCH 4/6] Better rethrow name --- src/crypto/cbor.h | 2 +- src/crypto/test/cbor.cpp | 2 +- src/node/uvm_endorsements.cpp | 48 +++++++++++++++++------------------ 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/crypto/cbor.h b/src/crypto/cbor.h index 148a4f515fe..bd8a09d0b59 100644 --- a/src/crypto/cbor.h +++ b/src/crypto/cbor.h @@ -80,7 +80,7 @@ namespace ccf::cbor Value parse(std::span raw); std::string to_string(const Value& value); - decltype(auto) rethrow_with_context(auto&& f, std::string_view context = {}) + decltype(auto) rethrow_with_msg(auto&& f, std::string_view context = {}) { try { diff --git a/src/crypto/test/cbor.cpp b/src/crypto/test/cbor.cpp index bd74c32c443..4e8b23a49ed 100644 --- a/src/crypto/test/cbor.cpp +++ b/src/crypto/test/cbor.cpp @@ -1632,7 +1632,7 @@ TEST_CASE("CBOR: throw with context") const std::string err = "Not a string value"; const std::string expected_err = err + ": " + context; REQUIRE_THROWS_WITH_AS( - rethrow_with_context([&]() { std::ignore = v->as_string(); }, context), + rethrow_with_msg([&]() { std::ignore = v->as_string(); }, context), expected_err.c_str(), CBORDecodeError); } \ No newline at end of file diff --git a/src/node/uvm_endorsements.cpp b/src/node/uvm_endorsements.cpp index e8ede546e8a..fd2ac04c75f 100644 --- a/src/node/uvm_endorsements.cpp +++ b/src/node/uvm_endorsements.cpp @@ -58,7 +58,7 @@ namespace ccf for (size_t i = 0; i < x5chain_value->size(); ++i) { const auto x5chain_ctx = "x5chain[" + std::to_string(i) + "]"; - const auto& bytes = ccf::cbor::rethrow_with_context( + const auto& bytes = ccf::cbor::rethrow_with_msg( [&]() { return x5chain_value->array_at(i)->as_bytes(); }, x5chain_ctx); chain.emplace_back(bytes.begin(), bytes.end()); @@ -66,7 +66,7 @@ namespace ccf } catch (const ccf::cbor::CBORDecodeError&) { - auto bytes = ccf::cbor::rethrow_with_context( + auto bytes = ccf::cbor::rethrow_with_msg( [&]() { return x5chain_value->as_bytes(); }, "x5chain"); chain.emplace_back(bytes.begin(), bytes.end()); } @@ -76,27 +76,27 @@ namespace ccf UvmEndorsementsProtectedHeader decode_protected_header( std::span raw_endorsements) { - auto parsed = ccf::cbor::rethrow_with_context( + auto parsed = ccf::cbor::rethrow_with_msg( [&]() { return ccf::cbor::parse(raw_endorsements); }, "UVM endorsements COSE envelope"); - const auto& cose_array = ccf::cbor::rethrow_with_context( + const auto& cose_array = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return parsed->tag_at(CBOR_TAG_COSE_SIGN1); }, "COSE_Sign1 tag"); constexpr std::string_view phdr_context{"COSE_Sign1[0]"}; - const auto& phdr_bytes = ccf::cbor::rethrow_with_context( + const auto& phdr_bytes = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return cose_array->array_at(0); }, phdr_context); - auto phdr_bytes_span = ccf::cbor::rethrow_with_context( + auto phdr_bytes_span = ccf::cbor::rethrow_with_msg( [&]() { return phdr_bytes->as_bytes(); }, phdr_context); - auto parsed_phdr = ccf::cbor::rethrow_with_context( + auto parsed_phdr = ccf::cbor::rethrow_with_msg( [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "Parse protected header in UVM endorsements"); UvmEndorsementsProtectedHeader result; - result.alg = ccf::cbor::rethrow_with_context( + result.alg = ccf::cbor::rethrow_with_msg( [&]() { return parsed_phdr ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) @@ -106,7 +106,7 @@ namespace ccf "Parse alg ({}) in protected header in UVM endorsements", headers::PARAM_ALG)); - result.content_type = ccf::cbor::rethrow_with_context( + result.content_type = ccf::cbor::rethrow_with_msg( [&]() { return std::string( parsed_phdr @@ -117,7 +117,7 @@ namespace ccf "Parse content-type ({}) in protected header in UVM endorsements", headers::PARAM_CONTENT_TYPE)); - result.x5_chain = ccf::cbor::rethrow_with_context( + result.x5_chain = ccf::cbor::rethrow_with_msg( [&]() { return parse_x5chain(parsed_phdr->map_at( ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); @@ -126,14 +126,14 @@ namespace ccf "Parse x5chain ({}) in protected header in UVM endorsements", headers::PARAM_X5CHAIN)); - result.iss = ccf::cbor::rethrow_with_context( + result.iss = ccf::cbor::rethrow_with_msg( [&]() { return parsed_phdr->map_at(ccf::cbor::make_string("iss")) ->as_string(); }, "Parse iss in protected header in UVM endorsements"); - result.feed = ccf::cbor::rethrow_with_context( + result.feed = ccf::cbor::rethrow_with_msg( [&]() { return std::string( parsed_phdr->map_at(ccf::cbor::make_string("feed"))->as_string()); @@ -147,28 +147,28 @@ namespace ccf decode_protected_header_with_cwt( std::span raw_endorsements) { - auto parsed = ccf::cbor::rethrow_with_context( + auto parsed = ccf::cbor::rethrow_with_msg( [&]() { return ccf::cbor::parse(raw_endorsements); }, "COSE envelope"); - const auto& cose_array = ccf::cbor::rethrow_with_context( + const auto& cose_array = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return parsed->tag_at(CBOR_TAG_COSE_SIGN1); }, "COSE_Sign1 tag"); constexpr std::string_view phdr_context{"COSE_Sign1[0]"}; - const auto& phdr_bytes = ccf::cbor::rethrow_with_context( + const auto& phdr_bytes = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return cose_array->array_at(0); }, phdr_context); - auto phdr_bytes_span = ccf::cbor::rethrow_with_context( + auto phdr_bytes_span = ccf::cbor::rethrow_with_msg( [&]() { return phdr_bytes->as_bytes(); }, phdr_context); - auto parsed_phdr = ccf::cbor::rethrow_with_context( + auto parsed_phdr = ccf::cbor::rethrow_with_msg( [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "phdr CBOR"); UvmEndorsementsProtectedHeader result; - result.alg = ccf::cbor::rethrow_with_context( + result.alg = ccf::cbor::rethrow_with_msg( [&]() { return parsed_phdr ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) @@ -178,7 +178,7 @@ namespace ccf "Parse alg ({}) in protected header in UVM endorsements", headers::PARAM_ALG)); - result.content_type = ccf::cbor::rethrow_with_context( + result.content_type = ccf::cbor::rethrow_with_msg( [&]() { return std::string(parsed_phdr ->map_at(ccf::cbor::make_unsigned( @@ -189,7 +189,7 @@ namespace ccf "Parse content-type ({}) in protected header in UVM endorsements", headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)); - result.x5_chain = ccf::cbor::rethrow_with_context( + result.x5_chain = ccf::cbor::rethrow_with_msg( [&]() { return parse_x5chain(parsed_phdr->map_at( ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); @@ -198,7 +198,7 @@ namespace ccf "Parse x5chain ({}) in protected header in UVM endorsements", headers::PARAM_X5CHAIN)); - const ccf::cbor::Value& cwt_claims = ccf::cbor::rethrow_with_context( + const ccf::cbor::Value& cwt_claims = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return parsed_phdr->map_at( ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT)); @@ -207,7 +207,7 @@ namespace ccf "Parse CWT ({}) in protected header in UVM endorsements", ccf::crypto::COSE_PHEADER_KEY_CWT)); - result.iss = ccf::cbor::rethrow_with_context( + result.iss = ccf::cbor::rethrow_with_msg( [&]() { return std::string(cwt_claims ->map_at(ccf::cbor::make_unsigned( @@ -218,7 +218,7 @@ namespace ccf "Parse iss ({}) in CWT claims in UVM endorsements", ccf::crypto::COSE_PHEADER_KEY_ISS)); - result.feed = ccf::cbor::rethrow_with_context( + result.feed = ccf::cbor::rethrow_with_msg( [&]() { return std::string(cwt_claims ->map_at(ccf::cbor::make_unsigned( @@ -229,7 +229,7 @@ namespace ccf "Parse feed ({}) in CWT claims in UVM endorsements", ccf::crypto::COSE_PHEADER_KEY_SUB)); - uint64_t svn = ccf::cbor::rethrow_with_context( + uint64_t svn = ccf::cbor::rethrow_with_msg( [&]() { return cwt_claims->map_at(ccf::cbor::make_string("svn")) ->as_unsigned(); From 0363e015bba69cd13895f5fd43d9f902c5fd734c Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Thu, 8 Jan 2026 12:59:27 +0000 Subject: [PATCH 5/6] Code review --- src/crypto/cbor.cpp | 3 +++ src/crypto/cbor.h | 7 ++++--- src/crypto/openssl/cose_sign.h | 1 + src/crypto/test/cbor.cpp | 4 ++-- src/node/uvm_endorsements.cpp | 15 ++++++++++----- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/crypto/cbor.cpp b/src/crypto/cbor.cpp index 1ae4ac5ff18..cae34382f7c 100644 --- a/src/crypto/cbor.cpp +++ b/src/crypto/cbor.cpp @@ -258,6 +258,9 @@ namespace case SimpleValue::Null: os << "Simple: Null" << std::endl; break; + case SimpleValue::Undefined: + os << "Simple: Undefined" << std::endl; + break; default: os << "Simple: " << casted << std::endl; } diff --git a/src/crypto/cbor.h b/src/crypto/cbor.h index bd8a09d0b59..a06ee2531b4 100644 --- a/src/crypto/cbor.h +++ b/src/crypto/cbor.h @@ -33,6 +33,7 @@ namespace ccf::cbor False = 20, True = 21, Null = 22, + Undefined = 23, }; struct Array @@ -80,7 +81,7 @@ namespace ccf::cbor Value parse(std::span raw); std::string to_string(const Value& value); - decltype(auto) rethrow_with_msg(auto&& f, std::string_view context = {}) + decltype(auto) rethrow_with_msg(auto&& f, std::string_view msg = {}) { try { @@ -88,9 +89,9 @@ namespace ccf::cbor } catch (const CBORDecodeError& err) { - if (!context.empty()) + if (!msg.empty()) { - throw CBORDecodeError(fmt::format("{}: {}", err.what(), context)); + throw CBORDecodeError(fmt::format("{}: {}", err.what(), msg)); } throw err; } diff --git a/src/crypto/openssl/cose_sign.h b/src/crypto/openssl/cose_sign.h index 413dbcd03ce..a6123b1e0b8 100644 --- a/src/crypto/openssl/cose_sign.h +++ b/src/crypto/openssl/cose_sign.h @@ -55,6 +55,7 @@ namespace ccf::crypto // CCF-specific: last signed Merkle root hash in the range. static const std::string COSE_PHEADER_KEY_EPOCH_LAST_MERKLE_ROOT = "epoch.end.merkle.root"; + static const std::string COSE_PHEADER_UVM_SVN = "svn"; class COSEMapKey { diff --git a/src/crypto/test/cbor.cpp b/src/crypto/test/cbor.cpp index 4e8b23a49ed..938fdcdcd0e 100644 --- a/src/crypto/test/cbor.cpp +++ b/src/crypto/test/cbor.cpp @@ -241,9 +241,9 @@ TEST_CASE("CBOR: simple value undefined") auto cbor_bytes = ccf::ds::from_hex("f7"); auto value = parse(cbor_bytes); - REQUIRE(value->as_simple() == 23); + REQUIRE(value->as_simple() == SimpleValue::Undefined); - const std::string expected_repr = "Simple: 23"; + const std::string expected_repr = "Simple: Undefined"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } diff --git a/src/node/uvm_endorsements.cpp b/src/node/uvm_endorsements.cpp index fd2ac04c75f..22eb55598f9 100644 --- a/src/node/uvm_endorsements.cpp +++ b/src/node/uvm_endorsements.cpp @@ -164,7 +164,8 @@ namespace ccf [&]() { return phdr_bytes->as_bytes(); }, phdr_context); auto parsed_phdr = ccf::cbor::rethrow_with_msg( - [&]() { return ccf::cbor::parse(phdr_bytes_span); }, "phdr CBOR"); + [&]() { return ccf::cbor::parse(phdr_bytes_span); }, + "Parse protected header in UVM endorsements"); UvmEndorsementsProtectedHeader result; @@ -204,7 +205,7 @@ namespace ccf ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT)); }, fmt::format( - "Parse CWT ({}) in protected header in UVM endorsements", + "Parse CWT claims ({}) in protected header in UVM endorsements", ccf::crypto::COSE_PHEADER_KEY_CWT)); result.iss = ccf::cbor::rethrow_with_msg( @@ -226,15 +227,19 @@ namespace ccf ->as_string()); }, fmt::format( - "Parse feed ({}) in CWT claims in UVM endorsements", + "Parse sub ({}) in CWT claims in UVM endorsements", ccf::crypto::COSE_PHEADER_KEY_SUB)); uint64_t svn = ccf::cbor::rethrow_with_msg( [&]() { - return cwt_claims->map_at(ccf::cbor::make_string("svn")) + return cwt_claims + ->map_at( + ccf::cbor::make_string(ccf::crypto::COSE_PHEADER_UVM_SVN)) ->as_unsigned(); }, - "Parse svn in CWT claims in UVM endorsements"); + fmt::format( + "Parse svn ({}) in CWT claims in UVM endorsements", + ccf::crypto::COSE_PHEADER_UVM_SVN)); return {result, std::to_string(svn)}; } From bbd76e11e3ef377806f5a004e4f2ceee0734e306 Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Thu, 8 Jan 2026 20:06:21 +0000 Subject: [PATCH 6/6] And more review, signed int only, test cases groups --- src/crypto/cbor.cpp | 77 +--- src/crypto/cbor.h | 6 +- src/crypto/test/cbor.cpp | 714 +++++++++++++--------------------- src/node/uvm_endorsements.cpp | 20 +- 4 files changed, 286 insertions(+), 531 deletions(-) diff --git a/src/crypto/cbor.cpp b/src/crypto/cbor.cpp index cae34382f7c..d4c0365cc49 100644 --- a/src/crypto/cbor.cpp +++ b/src/crypto/cbor.cpp @@ -29,16 +29,6 @@ namespace } } - Value consume_unsigned(cbor_nondet_t cbor) - { - Unsigned value{0}; - if (!cbor_nondet_read_uint64(cbor, &value)) - { - throw CBORDecodeError("Failed to consume unsigned value"); - } - return std::make_unique(value); - } - Value consume_signed(cbor_nondet_t cbor) { Signed value{0}; @@ -151,7 +141,6 @@ namespace switch (mt) { case CBOR_MAJOR_TYPE_UINT64: - return consume_unsigned(cbor); case CBOR_MAJOR_TYPE_NEG_INT64: return consume_signed(cbor); case CBOR_MAJOR_TYPE_BYTE_STRING: @@ -184,12 +173,7 @@ namespace std::visit( [&os, indent](const auto& v) { using T = std::decay_t; - if constexpr (std::is_same_v) - { - print_indent(os, indent); - os << "Unsigned: " << v << std::endl; - } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { print_indent(os, indent); os << "Signed: " << v << std::endl; @@ -268,24 +252,10 @@ namespace }, value->value); } - - bool signed_equals_unsigned(Signed a, Unsigned b) - { - if (a < 0 || b > std::numeric_limits::max()) - { - return false; - } - return a == b; - } } // namespace namespace ccf::cbor { - Value make_unsigned(uint64_t value) - { - return std::make_unique(value); - } - Value make_signed(int64_t value) { return std::make_unique(value); @@ -382,21 +352,9 @@ namespace ccf::cbor if constexpr (!std::is_same_v) { - // Handle unsigned/signed equally for key comparison. - if constexpr ( - std::is_same_v && std::is_same_v) - { - return signed_equals_unsigned(a, b); - } - if constexpr ( - std::is_same_v && std::is_same_v) - { - return signed_equals_unsigned(b, a); - } return false; } - else if constexpr ( - std::is_same_v || std::is_same_v) + else if constexpr (std::is_same_v) { return a == b; } @@ -453,40 +411,15 @@ namespace ccf::cbor return tagged.item; } - Unsigned ValueImpl::as_unsigned() const - { - if (!std::holds_alternative(value)) - { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not an unsigned value"); - } - const auto s_value = std::get(value); - if (s_value < 0) - { - throw CBORDecodeError("Casting signed to unsigned will overflow"); - } - return static_cast(s_value); - } - return std::get(value); - } Signed ValueImpl::as_signed() const { if (!std::holds_alternative(value)) { - if (!std::holds_alternative(value)) - { - throw CBORDecodeError("Not a signed value"); - } - const auto u_value = std::get(value); - if (u_value > std::numeric_limits::max()) - { - throw CBORDecodeError("Casting unsigned to signed will overflow"); - } - return static_cast(u_value); + throw CBORDecodeError("Not a signed value"); } return std::get(value); } + Bytes ValueImpl::as_bytes() const { if (!std::holds_alternative(value)) @@ -495,6 +428,7 @@ namespace ccf::cbor } return std::get(value); } + String ValueImpl::as_string() const { if (!std::holds_alternative(value)) @@ -503,6 +437,7 @@ namespace ccf::cbor } return std::get(value); } + Simple ValueImpl::as_simple() const { if (!std::holds_alternative(value)) diff --git a/src/crypto/cbor.h b/src/crypto/cbor.h index a06ee2531b4..c6e15a90eec 100644 --- a/src/crypto/cbor.h +++ b/src/crypto/cbor.h @@ -19,7 +19,6 @@ namespace ccf::cbor struct ValueImpl; using Value = std::unique_ptr; - using Unsigned = uint64_t; using Signed = int64_t; using Bytes = std::span; using String = std::string_view; @@ -52,8 +51,7 @@ namespace ccf::cbor Value item{nullptr}; }; - using Type = - std::variant; + using Type = std::variant; using CBORDecodeError = std::runtime_error; @@ -65,7 +63,6 @@ namespace ccf::cbor [[nodiscard]] const Value& array_at(size_t index) const; [[nodiscard]] const Value& map_at(const Value& key) const; [[nodiscard]] const Value& tag_at(uint64_t tag) const; - [[nodiscard]] Unsigned as_unsigned() const; [[nodiscard]] Signed as_signed() const; [[nodiscard]] Bytes as_bytes() const; [[nodiscard]] String as_string() const; @@ -73,7 +70,6 @@ namespace ccf::cbor [[nodiscard]] size_t size() const; }; - Value make_unsigned(uint64_t value); Value make_signed(int64_t value); Value make_string(std::string_view data); Value make_bytes(std::span data); diff --git a/src/crypto/test/cbor.cpp b/src/crypto/test/cbor.cpp index 938fdcdcd0e..edb344499d8 100644 --- a/src/crypto/test/cbor.cpp +++ b/src/crypto/test/cbor.cpp @@ -12,254 +12,135 @@ using namespace ccf::cbor; -TEST_CASE("CBOR: unsigned integer 0") -{ - auto cbor_bytes = ccf::ds::from_hex("00"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_unsigned() == 0); - - const std::string expected_repr = "Unsigned: 0"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: unsigned integer 42") -{ - auto cbor_bytes = ccf::ds::from_hex("182a"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_unsigned() == 42); - - const std::string expected_repr = "Unsigned: 42"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: unsigned integer 255") -{ - auto cbor_bytes = ccf::ds::from_hex("18ff"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_unsigned() == 255); - - const std::string expected_repr = "Unsigned: 255"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: unsigned integer 65535") -{ - auto cbor_bytes = ccf::ds::from_hex("19ffff"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_unsigned() == 65535); - - const std::string expected_repr = "Unsigned: 65535"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: unsigned integer max uint64") -{ - auto cbor_bytes = ccf::ds::from_hex("1bffffffffffffffff"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_unsigned() == 18446744073709551615ULL); - - const std::string expected_repr = "Unsigned: 18446744073709551615"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: signed integer -1") -{ - auto cbor_bytes = ccf::ds::from_hex("20"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_signed() == -1); - - const std::string expected_repr = "Signed: -1"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: signed integer -42") -{ - auto cbor_bytes = ccf::ds::from_hex("3829"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_signed() == -42); - - const std::string expected_repr = "Signed: -42"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: signed integer -1000") -{ - auto cbor_bytes = ccf::ds::from_hex("3903e7"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_signed() == -1000); - - const std::string expected_repr = "Signed: -1000"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: signed integer min int64") -{ - auto cbor_bytes = ccf::ds::from_hex("3b7fffffffffffffff"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_signed() == INT64_MIN); - - const std::string expected_repr = "Signed: -9223372036854775808"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: empty string") -{ - auto cbor_bytes = ccf::ds::from_hex("60"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_string() == ""); - - const std::string expected_repr = R"(String: "")"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: string 'hello'") -{ - auto cbor_bytes = ccf::ds::from_hex("6568656c6c6f"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_string() == "hello"); - - const std::string expected_repr = R"(String: "hello")"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: string 'Hello, World!'") -{ - auto cbor_bytes = ccf::ds::from_hex("6d48656c6c6f2c20576f726c6421"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_string() == "Hello, World!"); - - const std::string expected_repr = R"(String: "Hello, World!")"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: empty bytes") -{ - auto cbor_bytes = ccf::ds::from_hex("40"); - auto value = parse(cbor_bytes); - - auto bytes = value->as_bytes(); - REQUIRE(bytes.size() == 0); - - const std::string expected_repr = "Bytes[0]:"; - - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: bytes [0,1,2,3]") -{ - auto cbor_bytes = ccf::ds::from_hex("4400010203"); - auto value = parse(cbor_bytes); +TEST_CASE("CBOR: signed integers") +{ + std::vector> + test_cases{ + {"signed integer -1", "20", -1, "Signed: -1"}, + {"signed integer 1", "01", 1, "Signed: 1"}, + {"signed integer -42", "3829", -42, "Signed: -42"}, + {"signed integer 42", "182a", 42, "Signed: 42"}, + {"signed integer -1000", "3903e7", -1000, "Signed: -1000"}, + {"signed integer 1000", "1903e8", 1000, "Signed: 1000"}, + {"signed integer min int64", + "3b7fffffffffffffff", + std::numeric_limits::min(), + "Signed: -9223372036854775808"}, + {"signed integer max int64", + "1B7FFFFFFFFFFFFFFF", + std::numeric_limits::max(), + "Signed: 9223372036854775807"}}; + + for (const auto& [name, hex, expected_value, expected_repr] : test_cases) + { + SUBCASE(name) + { + auto cbor_bytes = ccf::ds::from_hex(hex); + auto value = parse(cbor_bytes); - auto bytes = value->as_bytes(); - std::vector expected{0x00, 0x01, 0x02, 0x03}; - REQUIRE( - std::equal(expected.begin(), expected.end(), bytes.begin(), bytes.end())); + REQUIRE(value->as_signed() == expected_value); - const std::string expected_repr = "Bytes[4]: 00010203"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); + const std::string result = to_string(value); + REQUIRE(result == expected_repr); + } + } } -TEST_CASE("CBOR: bytes deadbeef") +TEST_CASE("CBOR: signed integer overflow") { - auto cbor_bytes = ccf::ds::from_hex("44deadbeef"); - auto value = parse(cbor_bytes); - - auto bytes = value->as_bytes(); - std::vector expected{0xde, 0xad, 0xbe, 0xef}; - REQUIRE( - std::equal(expected.begin(), expected.end(), bytes.begin(), bytes.end())); - - const std::string expected_repr = "Bytes[4]: deadbeef"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); + // 9223372036854775807 + 1 = 9223372036854775808 + auto cbor_bytes = ccf::ds::from_hex("1b8000000000000000"); + REQUIRE_THROWS_AS(parse(cbor_bytes), CBORDecodeError); } -TEST_CASE("CBOR: simple value false") +TEST_CASE("CBOR: strings") { - auto cbor_bytes = ccf::ds::from_hex("f4"); - auto value = parse(cbor_bytes); - - REQUIRE(value->as_simple() == SimpleValue::False); + std::vector< + std::tuple> + test_cases{ + {"empty string", "60", "", R"(String: "")"}, + {"string 'hello'", "6568656c6c6f", "hello", R"(String: "hello")"}, + {"string 'Hello, World!'", + "6d48656c6c6f2c20576f726c6421", + "Hello, World!", + R"(String: "Hello, World!")"}}; - const std::string expected_repr = "Simple: False"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: simple value true") -{ - auto cbor_bytes = ccf::ds::from_hex("f5"); - auto value = parse(cbor_bytes); + for (const auto& [name, hex, expected_value, expected_repr] : test_cases) + { + SUBCASE(name) + { + auto cbor_bytes = ccf::ds::from_hex(hex); + auto value = parse(cbor_bytes); - REQUIRE(value->as_simple() == SimpleValue::True); + REQUIRE(value->as_string() == expected_value); - const std::string expected_repr = "Simple: True"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); + const std::string result = to_string(value); + REQUIRE(result == expected_repr); + } + } } -TEST_CASE("CBOR: simple value null") +TEST_CASE("CBOR: bytes") { - auto cbor_bytes = ccf::ds::from_hex("f6"); - auto value = parse(cbor_bytes); + std::vector< + std::tuple, std::string>> + test_cases{ + {"empty bytes", "40", {}, "Bytes[0]:"}, + {"bytes [0,1,2,3]", + "4400010203", + {0x00, 0x01, 0x02, 0x03}, + "Bytes[4]: 00010203"}, + {"bytes deadbeef", + "44deadbeef", + {0xde, 0xad, 0xbe, 0xef}, + "Bytes[4]: deadbeef"}}; - REQUIRE(value->as_simple() == SimpleValue::Null); - - const std::string expected_repr = "Simple: Null"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); + for (const auto& [name, hex, expected_value, expected_repr] : test_cases) + { + SUBCASE(name) + { + auto cbor_bytes = ccf::ds::from_hex(hex); + auto value = parse(cbor_bytes); + + auto bytes = value->as_bytes(); + REQUIRE(std::equal( + expected_value.begin(), + expected_value.end(), + bytes.begin(), + bytes.end())); + + const std::string result = to_string(value); + REQUIRE(result == expected_repr); + } + } } -TEST_CASE("CBOR: simple value undefined") +TEST_CASE("CBOR: simple values") { - auto cbor_bytes = ccf::ds::from_hex("f7"); - auto value = parse(cbor_bytes); + std::vector< + std::tuple> + test_cases{ + {"simple value false", "f4", SimpleValue::False, "Simple: False"}, + {"simple value true", "f5", SimpleValue::True, "Simple: True"}, + {"simple value null", "f6", SimpleValue::Null, "Simple: Null"}, + {"simple value undefined", + "f7", + SimpleValue::Undefined, + "Simple: Undefined"}}; - REQUIRE(value->as_simple() == SimpleValue::Undefined); - - const std::string expected_repr = "Simple: Undefined"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); -} - -TEST_CASE("CBOR: tagged value Tag(9000) with unsigned 42") -{ - auto cbor_bytes = ccf::ds::from_hex("d92328182a"); - auto value = parse(cbor_bytes); + for (const auto& [name, hex, expected_value, expected_repr] : test_cases) + { + SUBCASE(name) + { + auto cbor_bytes = ccf::ds::from_hex(hex); + auto value = parse(cbor_bytes); - const auto& item = value->tag_at(9000); - REQUIRE(item->as_unsigned() == 42); + REQUIRE(value->as_simple() == expected_value); - const std::string expected_repr = R"(Tagged[9000]: - Unsigned: 42)"; - const std::string result = to_string(value); - REQUIRE(result == expected_repr); + const std::string result = to_string(value); + REQUIRE(result == expected_repr); + } + } } TEST_CASE("CBOR: tagged value Tag(9001) with signed -42") @@ -359,15 +240,15 @@ TEST_CASE("CBOR: array [1, 2, 3, 4, 5]") for (size_t i = 0; i < 5; i++) { - REQUIRE(arr.items[i]->as_unsigned() == i + 1); + REQUIRE(arr.items[i]->as_signed() == i + 1); } const std::string expected_repr = R"(Array[5]: - Unsigned: 1 - Unsigned: 2 - Unsigned: 3 - Unsigned: 4 - Unsigned: 5)"; + Signed: 1 + Signed: 2 + Signed: 3 + Signed: 4 + Signed: 5)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -468,7 +349,7 @@ TEST_CASE("CBOR: array [1, 'two', b'3', True, None]") const auto& arr = std::get(value->value); REQUIRE(arr.items.size() == 5); - REQUIRE(arr.items[0]->as_unsigned() == 1); + REQUIRE(arr.items[0]->as_signed() == 1); REQUIRE(arr.items[1]->as_string() == "two"); @@ -481,7 +362,7 @@ TEST_CASE("CBOR: array [1, 'two', b'3', True, None]") REQUIRE(arr.items[4]->as_simple() == SimpleValue::Null); const std::string expected_repr = R"(Array[5]: - Unsigned: 1 + Signed: 1 String: "two" Bytes[1]: 33 Simple: True @@ -498,22 +379,22 @@ TEST_CASE("CBOR: array [0, -1, 42, -100, 65535]") const auto& arr = std::get(value->value); REQUIRE(arr.items.size() == 5); - REQUIRE(arr.items[0]->as_unsigned() == 0); + REQUIRE(arr.items[0]->as_signed() == 0); REQUIRE(arr.items[1]->as_signed() == -1); - REQUIRE(arr.items[2]->as_unsigned() == 42); + REQUIRE(arr.items[2]->as_signed() == 42); REQUIRE(arr.items[3]->as_signed() == -100); - REQUIRE(arr.items[4]->as_unsigned() == 65535); + REQUIRE(arr.items[4]->as_signed() == 65535); const std::string expected_repr = R"(Array[5]: - Unsigned: 0 + Signed: 0 Signed: -1 - Unsigned: 42 + Signed: 42 Signed: -100 - Unsigned: 65535)"; + Signed: 65535)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -531,20 +412,20 @@ TEST_CASE("CBOR: nested array [[1, 2], [3, 4], [5, 6]]") const auto& inner = std::get(arr.items[i]->value); REQUIRE(inner.items.size() == 2); - REQUIRE(inner.items[0]->as_unsigned() == i * 2 + 1); - REQUIRE(inner.items[1]->as_unsigned() == i * 2 + 2); + REQUIRE(inner.items[0]->as_signed() == i * 2 + 1); + REQUIRE(inner.items[1]->as_signed() == i * 2 + 2); } const std::string expected_repr = R"(Array[3]: Array[2]: - Unsigned: 1 - Unsigned: 2 + Signed: 1 + Signed: 2 Array[2]: - Unsigned: 3 - Unsigned: 4 + Signed: 3 + Signed: 4 Array[2]: - Unsigned: 5 - Unsigned: 6)"; + Signed: 5 + Signed: 6)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -557,35 +438,35 @@ TEST_CASE("CBOR: deeply nested array [1, [2, 3], 4, [5, [6, 7]]]") const auto& arr = std::get(value->value); REQUIRE(arr.items.size() == 4); - REQUIRE(arr.items[0]->as_unsigned() == 1); + REQUIRE(arr.items[0]->as_signed() == 1); const auto& arr1 = std::get(arr.items[1]->value); REQUIRE(arr1.items.size() == 2); - REQUIRE(arr1.items[0]->as_unsigned() == 2); - REQUIRE(arr1.items[1]->as_unsigned() == 3); + REQUIRE(arr1.items[0]->as_signed() == 2); + REQUIRE(arr1.items[1]->as_signed() == 3); - REQUIRE(arr.items[2]->as_unsigned() == 4); + REQUIRE(arr.items[2]->as_signed() == 4); const auto& arr3 = std::get(arr.items[3]->value); REQUIRE(arr3.items.size() == 2); - REQUIRE(arr3.items[0]->as_unsigned() == 5); + REQUIRE(arr3.items[0]->as_signed() == 5); const auto& arr3_1 = std::get(arr3.items[1]->value); REQUIRE(arr3_1.items.size() == 2); - REQUIRE(arr3_1.items[0]->as_unsigned() == 6); - REQUIRE(arr3_1.items[1]->as_unsigned() == 7); + REQUIRE(arr3_1.items[0]->as_signed() == 6); + REQUIRE(arr3_1.items[1]->as_signed() == 7); const std::string expected_repr = R"(Array[4]: - Unsigned: 1 + Signed: 1 Array[2]: - Unsigned: 2 - Unsigned: 3 - Unsigned: 4 + Signed: 2 + Signed: 3 + Signed: 4 Array[2]: - Unsigned: 5 + Signed: 5 Array[2]: - Unsigned: 6 - Unsigned: 7)"; + Signed: 6 + Signed: 7)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -598,15 +479,15 @@ TEST_CASE("CBOR: tagged array Tag(9100) with [1, 2, 3]") const auto& item = value->tag_at(9100); const auto& arr = std::get(item->value); REQUIRE(arr.items.size() == 3); - REQUIRE(arr.items[0]->as_unsigned() == 1); - REQUIRE(arr.items[1]->as_unsigned() == 2); - REQUIRE(arr.items[2]->as_unsigned() == 3); + REQUIRE(arr.items[0]->as_signed() == 1); + REQUIRE(arr.items[1]->as_signed() == 2); + REQUIRE(arr.items[2]->as_signed() == 3); const std::string expected_repr = R"(Tagged[9100]: Array[3]: - Unsigned: 1 - Unsigned: 2 - Unsigned: 3)"; + Signed: 1 + Signed: 2 + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -645,21 +526,21 @@ TEST_CASE("CBOR: map {1: 'one', 2: 'two', 3: 'three'}") REQUIRE(value->size() == 3); - REQUIRE(value->map_at(make_unsigned(1))->as_string() == "one"); - REQUIRE(value->map_at(make_unsigned(2))->as_string() == "two"); - REQUIRE(value->map_at(make_unsigned(3))->as_string() == "three"); + REQUIRE(value->map_at(make_signed(1))->as_string() == "one"); + REQUIRE(value->map_at(make_signed(2))->as_string() == "two"); + REQUIRE(value->map_at(make_signed(3))->as_string() == "three"); const std::string expected_repr = R"(Map[3]: Key: - Unsigned: 1 + Signed: 1 Value: String: "one" Key: - Unsigned: 2 + Signed: 2 Value: String: "two" Key: - Unsigned: 3 + Signed: 3 Value: String: "three")"; const std::string result = to_string(value); @@ -673,23 +554,23 @@ TEST_CASE("CBOR: map {0: 100, 1: 200, 2: 300}") REQUIRE(value->size() == 3); - REQUIRE(value->map_at(make_unsigned(0))->as_unsigned() == 100); - REQUIRE(value->map_at(make_unsigned(1))->as_unsigned() == 200); - REQUIRE(value->map_at(make_unsigned(2))->as_unsigned() == 300); + REQUIRE(value->map_at(make_signed(0))->as_signed() == 100); + REQUIRE(value->map_at(make_signed(1))->as_signed() == 200); + REQUIRE(value->map_at(make_signed(2))->as_signed() == 300); const std::string expected_repr = R"(Map[3]: Key: - Unsigned: 0 + Signed: 0 Value: - Unsigned: 100 + Signed: 100 Key: - Unsigned: 1 + Signed: 1 Value: - Unsigned: 200 + Signed: 200 Key: - Unsigned: 2 + Signed: 2 Value: - Unsigned: 300)"; + Signed: 300)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -703,25 +584,25 @@ TEST_CASE("CBOR: map {'a': 1, 'b': 2, 'c': 3}") REQUIRE(map.items.size() == 3); REQUIRE(map.items[0].first->as_string() == "a"); - REQUIRE(map.items[0].second->as_unsigned() == 1); + REQUIRE(map.items[0].second->as_signed() == 1); REQUIRE(map.items[1].first->as_string() == "b"); - REQUIRE(map.items[1].second->as_unsigned() == 2); + REQUIRE(map.items[1].second->as_signed() == 2); REQUIRE(map.items[2].first->as_string() == "c"); - REQUIRE(map.items[2].second->as_unsigned() == 3); + REQUIRE(map.items[2].second->as_signed() == 3); const std::string expected_repr = R"(Map[3]: Key: String: "a" Value: - Unsigned: 1 + Signed: 1 Key: String: "b" Value: - Unsigned: 2 + Signed: 2 Key: String: "c" Value: - Unsigned: 3)"; + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -816,22 +697,22 @@ TEST_CASE("CBOR: array with map [1, {'a': 2}, 3]") REQUIRE(value->size() == 3); - REQUIRE(value->array_at(0)->as_unsigned() == 1); + REQUIRE(value->array_at(0)->as_signed() == 1); const auto& map = value->array_at(1); REQUIRE(map->size() == 1); - REQUIRE(map->map_at(make_string("a"))->as_unsigned() == 2); + REQUIRE(map->map_at(make_string("a"))->as_signed() == 2); - REQUIRE(value->array_at(2)->as_unsigned() == 3); + REQUIRE(value->array_at(2)->as_signed() == 3); const std::string expected_repr = R"(Array[3]: - Unsigned: 1 + Signed: 1 Map[1]: Key: String: "a" Value: - Unsigned: 2 - Unsigned: 3)"; + Signed: 2 + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -844,30 +725,30 @@ TEST_CASE("CBOR: array of maps [{'x': 1}, {'y': 2}, {'z': 3}]") REQUIRE(value->size() == 3); REQUIRE(value->array_at(0)->size() == 1); - REQUIRE(value->array_at(0)->map_at(make_string("x"))->as_unsigned() == 1); + REQUIRE(value->array_at(0)->map_at(make_string("x"))->as_signed() == 1); REQUIRE(value->array_at(1)->size() == 1); - REQUIRE(value->array_at(1)->map_at(make_string("y"))->as_unsigned() == 2); + REQUIRE(value->array_at(1)->map_at(make_string("y"))->as_signed() == 2); REQUIRE(value->array_at(2)->size() == 1); - REQUIRE(value->array_at(2)->map_at(make_string("z"))->as_unsigned() == 3); + REQUIRE(value->array_at(2)->map_at(make_string("z"))->as_signed() == 3); const std::string expected_repr = R"(Array[3]: Map[1]: Key: String: "x" Value: - Unsigned: 1 + Signed: 1 Map[1]: Key: String: "y" Value: - Unsigned: 2 + Signed: 2 Map[1]: Key: String: "z" Value: - Unsigned: 3)"; + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -881,18 +762,18 @@ TEST_CASE("CBOR: map with array {'items': [1, 2, 3]}") const auto& arr = value->map_at(make_string("items")); REQUIRE(arr->size() == 3); - REQUIRE(arr->array_at(0)->as_unsigned() == 1); - REQUIRE(arr->array_at(1)->as_unsigned() == 2); - REQUIRE(arr->array_at(2)->as_unsigned() == 3); + REQUIRE(arr->array_at(0)->as_signed() == 1); + REQUIRE(arr->array_at(1)->as_signed() == 2); + REQUIRE(arr->array_at(2)->as_signed() == 3); const std::string expected_repr = R"(Map[1]: Key: String: "items" Value: Array[3]: - Unsigned: 1 - Unsigned: 2 - Unsigned: 3)"; + Signed: 1 + Signed: 2 + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -906,38 +787,38 @@ TEST_CASE("CBOR: map with multiple arrays") const auto& arr_a = value->map_at(make_string("a")); REQUIRE(arr_a->size() == 2); - REQUIRE(arr_a->array_at(0)->as_unsigned() == 1); - REQUIRE(arr_a->array_at(1)->as_unsigned() == 2); + REQUIRE(arr_a->array_at(0)->as_signed() == 1); + REQUIRE(arr_a->array_at(1)->as_signed() == 2); const auto& arr_b = value->map_at(make_string("b")); REQUIRE(arr_b->size() == 2); - REQUIRE(arr_b->array_at(0)->as_unsigned() == 3); - REQUIRE(arr_b->array_at(1)->as_unsigned() == 4); + REQUIRE(arr_b->array_at(0)->as_signed() == 3); + REQUIRE(arr_b->array_at(1)->as_signed() == 4); const auto& arr_c = value->map_at(make_string("c")); REQUIRE(arr_c->size() == 2); - REQUIRE(arr_c->array_at(0)->as_unsigned() == 5); - REQUIRE(arr_c->array_at(1)->as_unsigned() == 6); + REQUIRE(arr_c->array_at(0)->as_signed() == 5); + REQUIRE(arr_c->array_at(1)->as_signed() == 6); const std::string expected_repr = R"(Map[3]: Key: String: "a" Value: Array[2]: - Unsigned: 1 - Unsigned: 2 + Signed: 1 + Signed: 2 Key: String: "b" Value: Array[2]: - Unsigned: 3 - Unsigned: 4 + Signed: 3 + Signed: 4 Key: String: "c" Value: Array[2]: - Unsigned: 5 - Unsigned: 6)"; + Signed: 5 + Signed: 6)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -949,19 +830,19 @@ TEST_CASE("CBOR: tagged map Tag(10000) with {'a': 1, 'b': 2}") const auto& item = value->tag_at(10000); REQUIRE(item->size() == 2); - REQUIRE(item->map_at(make_string("a"))->as_unsigned() == 1); - REQUIRE(item->map_at(make_string("b"))->as_unsigned() == 2); + REQUIRE(item->map_at(make_string("a"))->as_signed() == 1); + REQUIRE(item->map_at(make_string("b"))->as_signed() == 2); const std::string expected_repr = R"(Tagged[10000]: Map[2]: Key: String: "a" Value: - Unsigned: 1 + Signed: 1 Key: String: "b" Value: - Unsigned: 2)"; + Signed: 2)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -979,7 +860,7 @@ TEST_CASE( const auto& map = value->array_at(1); REQUIRE(map->size() == 3); - REQUIRE(map->map_at(make_string("count"))->as_unsigned() == 42); + REQUIRE(map->map_at(make_string("count"))->as_signed() == 42); REQUIRE(map->map_at(make_string("label"))->as_string() == "items"); REQUIRE(map->map_at(make_string("active"))->as_simple() == SimpleValue::True); @@ -991,7 +872,7 @@ TEST_CASE( Key: String: "count" Value: - Unsigned: 42 + Signed: 42 Key: String: "label" Value: @@ -1017,7 +898,7 @@ TEST_CASE("CBOR: array ['header', {'id': 123, 'name': 'test'}, 'footer']") const auto& map = value->array_at(1); REQUIRE(map->size() == 2); - REQUIRE(map->map_at(make_string("id"))->as_unsigned() == 123); + REQUIRE(map->map_at(make_string("id"))->as_signed() == 123); REQUIRE(map->map_at(make_string("name"))->as_string() == "test"); REQUIRE(value->array_at(2)->as_string() == "footer"); @@ -1028,7 +909,7 @@ TEST_CASE("CBOR: array ['header', {'id': 123, 'name': 'test'}, 'footer']") Key: String: "id" Value: - Unsigned: 123 + Signed: 123 Key: String: "name" Value: @@ -1046,8 +927,8 @@ TEST_CASE("CBOR: array [1, 2, {'nested': {'key': 'value'}}, 3]") REQUIRE(value->size() == 4); - REQUIRE(value->array_at(0)->as_unsigned() == 1); - REQUIRE(value->array_at(1)->as_unsigned() == 2); + REQUIRE(value->array_at(0)->as_signed() == 1); + REQUIRE(value->array_at(1)->as_signed() == 2); const auto& map = value->array_at(2); REQUIRE(map->size() == 1); @@ -1056,11 +937,11 @@ TEST_CASE("CBOR: array [1, 2, {'nested': {'key': 'value'}}, 3]") REQUIRE(nested_map->size() == 1); REQUIRE(nested_map->map_at(make_string("key"))->as_string() == "value"); - REQUIRE(value->array_at(3)->as_unsigned() == 3); + REQUIRE(value->array_at(3)->as_signed() == 3); const std::string expected_repr = R"(Array[4]: - Unsigned: 1 - Unsigned: 2 + Signed: 1 + Signed: 2 Map[1]: Key: String: "nested" @@ -1070,7 +951,7 @@ TEST_CASE("CBOR: array [1, 2, {'nested': {'key': 'value'}}, 3]") String: "key" Value: String: "value" - Unsigned: 3)"; + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -1082,18 +963,18 @@ TEST_CASE("CBOR: array [1, Tag(9600, 'tagged'), 3]") REQUIRE(value->size() == 3); - REQUIRE(value->array_at(0)->as_unsigned() == 1); + REQUIRE(value->array_at(0)->as_signed() == 1); const auto& item = value->array_at(1)->tag_at(9600); REQUIRE(item->as_string() == "tagged"); - REQUIRE(value->array_at(2)->as_unsigned() == 3); + REQUIRE(value->array_at(2)->as_signed() == 3); const std::string expected_repr = R"(Array[3]: - Unsigned: 1 + Signed: 1 Tagged[9600]: String: "tagged" - Unsigned: 3)"; + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -1123,8 +1004,8 @@ TEST_CASE("CBOR: map with mixed key types") REQUIRE(value->size() == 3); - REQUIRE(value->map_at(make_unsigned(1))->as_string() == "num"); - REQUIRE(value->map_at(make_string("str"))->as_unsigned() == 2); + REQUIRE(value->map_at(make_signed(1))->as_string() == "num"); + REQUIRE(value->map_at(make_string("str"))->as_signed() == 2); const auto bytes_key = ccf::ds::from_hex("6279746573"); REQUIRE( @@ -1132,13 +1013,13 @@ TEST_CASE("CBOR: map with mixed key types") const std::string expected_repr = R"(Map[3]: Key: - Unsigned: 1 + Signed: 1 Value: String: "num" Key: String: "str" Value: - Unsigned: 2 + Signed: 2 Key: Bytes[5]: 6279746573 Value: @@ -1154,7 +1035,7 @@ TEST_CASE("CBOR: map {'a': 1, 'b': 'two', 'c': b'3', 'd': False}") REQUIRE(value->size() == 4); - REQUIRE(value->map_at(make_string("a"))->as_unsigned() == 1); + REQUIRE(value->map_at(make_string("a"))->as_signed() == 1); REQUIRE(value->map_at(make_string("b"))->as_string() == "two"); const auto byte_value = value->map_at(make_string("c"))->as_bytes(); @@ -1167,7 +1048,7 @@ TEST_CASE("CBOR: map {'a': 1, 'b': 'two', 'c': b'3', 'd': False}") Key: String: "a" Value: - Unsigned: 1 + Signed: 1 Key: String: "b" Value: @@ -1218,36 +1099,36 @@ TEST_CASE("CBOR: map {1: [10, 20], 2: ['a', 'b'], 3: [b'x', b'y']}") REQUIRE(value->size() == 3); - const auto& arr0 = value->map_at(make_unsigned(1)); + const auto& arr0 = value->map_at(make_signed(1)); REQUIRE(arr0->size() == 2); - REQUIRE(arr0->array_at(0)->as_unsigned() == 10); - REQUIRE(arr0->array_at(1)->as_unsigned() == 20); + REQUIRE(arr0->array_at(0)->as_signed() == 10); + REQUIRE(arr0->array_at(1)->as_signed() == 20); - const auto& arr1 = value->map_at(make_unsigned(2)); + const auto& arr1 = value->map_at(make_signed(2)); REQUIRE(arr1->size() == 2); REQUIRE(arr1->array_at(0)->as_string() == "a"); REQUIRE(arr1->array_at(1)->as_string() == "b"); - const auto& arr2 = value->map_at(make_unsigned(3)); + const auto& arr2 = value->map_at(make_signed(3)); REQUIRE(arr2->size() == 2); REQUIRE(arr2->array_at(0)->as_bytes()[0] == 'x'); REQUIRE(arr2->array_at(1)->as_bytes()[0] == 'y'); const std::string expected_repr = R"(Map[3]: Key: - Unsigned: 1 + Signed: 1 Value: Array[2]: - Unsigned: 10 - Unsigned: 20 + Signed: 10 + Signed: 20 Key: - Unsigned: 2 + Signed: 2 Value: Array[2]: String: "a" String: "b" Key: - Unsigned: 3 + Signed: 3 Value: Array[2]: Bytes[1]: 78 @@ -1263,19 +1144,19 @@ TEST_CASE("CBOR: map {1: b'data1', 2: b'data2'}") REQUIRE(value->size() == 2); - const auto data1 = value->map_at(make_unsigned(1))->as_bytes(); + const auto data1 = value->map_at(make_signed(1))->as_bytes(); REQUIRE(data1.size() == 5); - const auto data2 = value->map_at(make_unsigned(2))->as_bytes(); + const auto data2 = value->map_at(make_signed(2))->as_bytes(); REQUIRE(data2.size() == 5); const std::string expected_repr = R"(Map[2]: Key: - Unsigned: 1 + Signed: 1 Value: Bytes[5]: 6461746131 Key: - Unsigned: 2 + Signed: 2 Value: Bytes[5]: 6461746132)"; const std::string result = to_string(value); @@ -1292,25 +1173,25 @@ TEST_CASE("CBOR: map {'nested': [1, [2, 3], 4]}") const auto& arr = value->map_at(make_string("nested")); REQUIRE(arr->size() == 3); - REQUIRE(arr->array_at(0)->as_unsigned() == 1); + REQUIRE(arr->array_at(0)->as_signed() == 1); const auto& nested = arr->array_at(1); REQUIRE(nested->size() == 2); - REQUIRE(nested->array_at(0)->as_unsigned() == 2); - REQUIRE(nested->array_at(1)->as_unsigned() == 3); + REQUIRE(nested->array_at(0)->as_signed() == 2); + REQUIRE(nested->array_at(1)->as_signed() == 3); - REQUIRE(arr->array_at(2)->as_unsigned() == 4); + REQUIRE(arr->array_at(2)->as_signed() == 4); const std::string expected_repr = R"(Map[1]: Key: String: "nested" Value: Array[3]: - Unsigned: 1 + Signed: 1 Array[2]: - Unsigned: 2 - Unsigned: 3 - Unsigned: 4)"; + Signed: 2 + Signed: 3 + Signed: 4)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -1348,9 +1229,9 @@ TEST_CASE( const auto& nums = value->map_at(make_string("numbers")); REQUIRE(nums->size() == 3); - REQUIRE(nums->array_at(0)->as_unsigned() == 1); - REQUIRE(nums->array_at(1)->as_unsigned() == 2); - REQUIRE(nums->array_at(2)->as_unsigned() == 3); + REQUIRE(nums->array_at(0)->as_signed() == 1); + REQUIRE(nums->array_at(1)->as_signed() == 2); + REQUIRE(nums->array_at(2)->as_signed() == 3); const auto& strs = value->map_at(make_string("strings")); REQUIRE(strs->size() == 2); @@ -1367,9 +1248,9 @@ TEST_CASE( String: "numbers" Value: Array[3]: - Unsigned: 1 - Unsigned: 2 - Unsigned: 3 + Signed: 1 + Signed: 2 + Signed: 3 Key: String: "strings" Value: @@ -1399,13 +1280,13 @@ TEST_CASE("CBOR: map {'empty': [], 'single': [42], 'multiple': [1, 2, 3]}") const auto& single = value->map_at(make_string("single")); REQUIRE(single->size() == 1); - REQUIRE(single->array_at(0)->as_unsigned() == 42); + REQUIRE(single->array_at(0)->as_signed() == 42); const auto& multiple = value->map_at(make_string("multiple")); REQUIRE(multiple->size() == 3); - REQUIRE(multiple->array_at(0)->as_unsigned() == 1); - REQUIRE(multiple->array_at(1)->as_unsigned() == 2); - REQUIRE(multiple->array_at(2)->as_unsigned() == 3); + REQUIRE(multiple->array_at(0)->as_signed() == 1); + REQUIRE(multiple->array_at(1)->as_signed() == 2); + REQUIRE(multiple->array_at(2)->as_signed() == 3); const std::string expected_repr = R"(Map[3]: Key: @@ -1416,14 +1297,14 @@ TEST_CASE("CBOR: map {'empty': [], 'single': [42], 'multiple': [1, 2, 3]}") String: "single" Value: Array[1]: - Unsigned: 42 + Signed: 42 Key: String: "multiple" Value: Array[3]: - Unsigned: 1 - Unsigned: 2 - Unsigned: 3)"; + Signed: 1 + Signed: 2 + Signed: 3)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -1476,13 +1357,13 @@ TEST_CASE("CBOR: tagged array Tag(9400, [1, 'two', b'3'])") const auto& item = value->tag_at(9400); REQUIRE(item->size() == 3); - REQUIRE(item->array_at(0)->as_unsigned() == 1); + REQUIRE(item->array_at(0)->as_signed() == 1); REQUIRE(item->array_at(1)->as_string() == "two"); REQUIRE(item->array_at(2)->as_bytes()[0] == 0x33); const std::string expected_repr = R"(Tagged[9400]: Array[3]: - Unsigned: 1 + Signed: 1 String: "two" Bytes[1]: 33)"; const std::string result = to_string(value); @@ -1504,22 +1385,22 @@ TEST_CASE("CBOR: tagged array Tag(20000, [{'x': 1}, {'y': 2}])") Key: String: "x" Value: - Unsigned: 1 + Signed: 1 Map[1]: Key: String: "y" Value: - Unsigned: 2)"; + Signed: 2)"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } -TEST_CASE("CBOR: helper function make_unsigned") +TEST_CASE("CBOR: helper function make_signed") { - auto value = make_unsigned(42); + auto value = make_signed(42); REQUIRE(value != nullptr); - REQUIRE(value->as_unsigned() == 42); + REQUIRE(value->as_signed() == 42); - const std::string expected_repr = "Unsigned: 42"; + const std::string expected_repr = "Signed: 42"; const std::string result = to_string(value); REQUIRE(result == expected_repr); } @@ -1567,63 +1448,6 @@ TEST_CASE("CBOR: error - unexpected tag") REQUIRE_THROWS_AS((void)value->tag_at(9004), CBORDecodeError); } -TEST_CASE("CBOR: back and forth (un)signed") -{ - auto cbor_bytes = ccf::ds::from_hex("820120"); - auto value = parse(cbor_bytes); - - { - auto s = make_signed(64); - REQUIRE(s->as_signed() == 64); - REQUIRE(s->as_unsigned() == 64); - } - - { - auto u = make_unsigned(64); - REQUIRE(u->as_unsigned() == 64); - REQUIRE(u->as_signed() == 64); - } - - { - auto s = make_signed(-64); - REQUIRE(s->as_signed() == -64); - REQUIRE_THROWS_AS((void)s->as_unsigned(), CBORDecodeError); - } - - { - const auto value = 1ull + std::numeric_limits::max(); - auto u = make_unsigned(value); - REQUIRE(u->as_unsigned() == value); - REQUIRE_THROWS_AS((void)u->as_signed(), CBORDecodeError); - } -} - -TEST_CASE("CBOR: back and forth (un)signed") -{ - auto cbor_bytes = ccf::ds::from_hex( - "A320622D311B7FFFFFFFFFFFFFFF73393232333337323033363835343737353830371B80" - "00" - "0000000000007339323233333732303336383534373735383038"); - auto value = parse(cbor_bytes); - - // Signed only - REQUIRE(value->map_at(make_signed(-1))->as_string() == "-1"); - - // Both have to match - REQUIRE( - value->map_at(make_signed(9223372036854775807ll))->as_string() == - "9223372036854775807"); - - REQUIRE( - value->map_at(make_unsigned(9223372036854775807ull))->as_string() == - "9223372036854775807"); - - // Unsigned only - REQUIRE( - value->map_at(make_unsigned(9223372036854775808ull))->as_string() == - "9223372036854775808"); -} - TEST_CASE("CBOR: throw with context") { auto v = make_signed(105); diff --git a/src/node/uvm_endorsements.cpp b/src/node/uvm_endorsements.cpp index 22eb55598f9..7af55ce3baa 100644 --- a/src/node/uvm_endorsements.cpp +++ b/src/node/uvm_endorsements.cpp @@ -99,7 +99,7 @@ namespace ccf result.alg = ccf::cbor::rethrow_with_msg( [&]() { return parsed_phdr - ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) + ->map_at(ccf::cbor::make_signed(headers::PARAM_ALG)) ->as_signed(); }, fmt::format( @@ -110,7 +110,7 @@ namespace ccf [&]() { return std::string( parsed_phdr - ->map_at(ccf::cbor::make_unsigned(headers::PARAM_CONTENT_TYPE)) + ->map_at(ccf::cbor::make_signed(headers::PARAM_CONTENT_TYPE)) ->as_string()); }, fmt::format( @@ -120,7 +120,7 @@ namespace ccf result.x5_chain = ccf::cbor::rethrow_with_msg( [&]() { return parse_x5chain(parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); + ccf::cbor::make_signed(headers::PARAM_X5CHAIN))); }, fmt::format( "Parse x5chain ({}) in protected header in UVM endorsements", @@ -172,7 +172,7 @@ namespace ccf result.alg = ccf::cbor::rethrow_with_msg( [&]() { return parsed_phdr - ->map_at(ccf::cbor::make_unsigned(headers::PARAM_ALG)) + ->map_at(ccf::cbor::make_signed(headers::PARAM_ALG)) ->as_signed(); }, fmt::format( @@ -182,7 +182,7 @@ namespace ccf result.content_type = ccf::cbor::rethrow_with_msg( [&]() { return std::string(parsed_phdr - ->map_at(ccf::cbor::make_unsigned( + ->map_at(ccf::cbor::make_signed( headers::PARAM_CONTENT_TYPE_HASH_ENVELOPE)) ->as_string()); }, @@ -193,7 +193,7 @@ namespace ccf result.x5_chain = ccf::cbor::rethrow_with_msg( [&]() { return parse_x5chain(parsed_phdr->map_at( - ccf::cbor::make_unsigned(headers::PARAM_X5CHAIN))); + ccf::cbor::make_signed(headers::PARAM_X5CHAIN))); }, fmt::format( "Parse x5chain ({}) in protected header in UVM endorsements", @@ -202,7 +202,7 @@ namespace ccf const ccf::cbor::Value& cwt_claims = ccf::cbor::rethrow_with_msg( [&]() -> const ccf::cbor::Value& { return parsed_phdr->map_at( - ccf::cbor::make_unsigned(ccf::crypto::COSE_PHEADER_KEY_CWT)); + ccf::cbor::make_signed(ccf::crypto::COSE_PHEADER_KEY_CWT)); }, fmt::format( "Parse CWT claims ({}) in protected header in UVM endorsements", @@ -211,7 +211,7 @@ namespace ccf result.iss = ccf::cbor::rethrow_with_msg( [&]() { return std::string(cwt_claims - ->map_at(ccf::cbor::make_unsigned( + ->map_at(ccf::cbor::make_signed( ccf::crypto::COSE_PHEADER_KEY_ISS)) ->as_string()); }, @@ -222,7 +222,7 @@ namespace ccf result.feed = ccf::cbor::rethrow_with_msg( [&]() { return std::string(cwt_claims - ->map_at(ccf::cbor::make_unsigned( + ->map_at(ccf::cbor::make_signed( ccf::crypto::COSE_PHEADER_KEY_SUB)) ->as_string()); }, @@ -235,7 +235,7 @@ namespace ccf return cwt_claims ->map_at( ccf::cbor::make_string(ccf::crypto::COSE_PHEADER_UVM_SVN)) - ->as_unsigned(); + ->as_signed(); }, fmt::format( "Parse svn ({}) in CWT claims in UVM endorsements",