diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6d17202..7623aed10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,8 +171,8 @@ file(GLOB AWS_CRT_ENDPOINT_HEADERS "include/aws/crt/endpoints/*.h" ) -file(GLOB AWS_CRT_EXTERNAL_HEADERS - "include/aws/crt/external/*.h" +file(GLOB AWS_CRT_CBOR_HEADERS + "include/aws/crt/cbor/*.h" ) file(GLOB AWS_CRT_PUBLIC_HEADERS @@ -184,6 +184,7 @@ file(GLOB AWS_CRT_PUBLIC_HEADERS ${AWS_CRT_MQTT_HEADERS} ${AWS_CRT_HTTP_HEADERS} ${AWS_CRT_ENDPOINT_HEADERS} + ${AWS_CRT_CBOR_HEADERS} ) if(BUILD_DEPS) @@ -228,8 +229,8 @@ file(GLOB AWS_CRT_ENDPOINTS_SRC "source/endpoints/*.cpp" ) -file(GLOB AWS_CRT_EXTERNAL_SRC - "source/external/*.cpp" +file(GLOB AWS_CRT_CBOR_SRC + "source/cbor/*.cpp" ) file(GLOB AWS_CRT_CPP_SRC @@ -241,7 +242,7 @@ file(GLOB AWS_CRT_CPP_SRC ${AWS_CRT_MQTT_SRC} ${AWS_CRT_HTTP_SRC} ${AWS_CRT_ENDPOINTS_SRC} - ${AWS_CRT_EXTERNAL_SRC} + ${AWS_CRT_CBOR_SRC} ) if(WIN32) @@ -254,6 +255,7 @@ if(WIN32) source_group("Header Files\\aws\\crt\\mqtt" FILES ${AWS_CRT_MQTT_HEADERS}) source_group("Header Files\\aws\\crt\\http" FILES ${AWS_CRT_HTTP_HEADERS}) source_group("Header Files\\aws\\crt\\endpoints" FILES ${AWS_CRT_ENDPOINT_HEADERS}) + source_group("Header Files\\aws\\crt\\cbor" FILES ${AWS_CRT_CBOR_HEADERS}) source_group("Source Files" FILES ${AWS_CRT_SRC}) source_group("Source Files\\auth" FILES ${AWS_CRT_AUTH_SRC}) @@ -263,6 +265,7 @@ if(WIN32) source_group("Source Files\\mqtt" FILES ${AWS_CRT_MQTT_SRC}) source_group("Source Files\\http" FILES ${AWS_CRT_HTTP_SRC}) source_group("Source Files\\endpoints" FILES ${AWS_CRT_ENDPOINTS_SRC}) + source_group("Source Files\\cbor" FILES ${AWS_CRT_CBOR_SRC}) endif() endif() @@ -332,6 +335,7 @@ install(FILES ${AWS_CRT_IOT_HEADERS} DESTINATION "include/aws/iot" COMPONENT Dev install(FILES ${AWS_CRT_MQTT_HEADERS} DESTINATION "include/aws/crt/mqtt" COMPONENT Development) install(FILES ${AWS_CRT_HTTP_HEADERS} DESTINATION "include/aws/crt/http" COMPONENT Development) install(FILES ${AWS_CRT_ENDPOINT_HEADERS} DESTINATION "include/aws/crt/endpoints" COMPONENT Development) +install(FILES ${AWS_CRT_CBOR_HEADERS} DESTINATION "include/aws/crt/cbor" COMPONENT Development) install( TARGETS ${PROJECT_NAME} diff --git a/crt/aws-c-common b/crt/aws-c-common index ae7b067d9..7210cf204 160000 --- a/crt/aws-c-common +++ b/crt/aws-c-common @@ -1 +1 @@ -Subproject commit ae7b067d9274d2d3faa1d3ae42d489a6986661f7 +Subproject commit 7210cf20440182d10b8cf58d876f1ab7dfe8d165 diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h new file mode 100644 index 000000000..dcaab7ee0 --- /dev/null +++ b/include/aws/crt/cbor/Cbor.h @@ -0,0 +1,364 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include + +namespace Aws +{ + namespace Crt + { + namespace Cbor + { + /** + * The types used by APIs, not 1:1 with major types. + * It's an extension for CBOR major type in RFC8949 section 3.1. + * Major type 0 - Uint + * Major type 1 - NegInt + * Major type 2 - Bytes / IndefBytesStart + * Major type 3 - Text / IndefTextStart + * Major type 4 - ArrayStart / IndefArrayStart + * Major type 5 - MapStart / IndefMapStart + * Major type 6 - Tag + * Major type 7: + * - 20/21 - Bool + * - 22 - Null + * - 23 - Undefined + * - 25/26/27 - Float + * - 31 - Break + * - Rest of the values are not supported. + */ + enum class CborType + { + Unknown = AWS_CBOR_TYPE_UNKNOWN, + Uint = AWS_CBOR_TYPE_UINT, + NegInt = AWS_CBOR_TYPE_NEGINT, + Float = AWS_CBOR_TYPE_FLOAT, + Bytes = AWS_CBOR_TYPE_BYTES, + Text = AWS_CBOR_TYPE_TEXT, + ArrayStart = AWS_CBOR_TYPE_ARRAY_START, + MapStart = AWS_CBOR_TYPE_MAP_START, + Tag = AWS_CBOR_TYPE_TAG, + Bool = AWS_CBOR_TYPE_BOOL, + Null = AWS_CBOR_TYPE_NULL, + Undefined = AWS_CBOR_TYPE_UNDEFINED, + Break = AWS_CBOR_TYPE_BREAK, + IndefBytesStart = AWS_CBOR_TYPE_INDEF_BYTES_START, + IndefTextStart = AWS_CBOR_TYPE_INDEF_TEXT_START, + IndefArrayStart = AWS_CBOR_TYPE_INDEF_ARRAY_START, + IndefMapStart = AWS_CBOR_TYPE_INDEF_MAP_START, + }; + + class AWS_CRT_CPP_API CborEncoder + { + public: + CborEncoder(const CborEncoder &) = delete; + CborEncoder(CborEncoder &&) = delete; + CborEncoder &operator=(const CborEncoder &) = delete; + CborEncoder &operator=(CborEncoder &&) = delete; + + CborEncoder(Allocator *allocator) noexcept; + ~CborEncoder() noexcept; + + /** + * Get the current encoded data from encoder. The encoded data has the same lifetime as the encoder, + * and once any other function call invoked for the encoder, the encoded data is no longer valid. + * + * @return the current encoded data + */ + ByteCursor GetEncodedData() noexcept; + + /** + * Clear the current encoded buffer from encoder. + */ + void Reset() noexcept; + + /** + * Encode a AWS_CBOR_TYPE_UINT value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * + * @param value value to encode. + */ + void WriteUint(uint64_t value) noexcept; + + /** + * Encode a AWS_CBOR_TYPE_NEGINT value to "smallest possible" in encoder's buffer. + * It represents (-1 - value). + * Referring to RFC8949 section 4.2.1 + * + * @param value value to encode, which is (-1 - represented value) + */ + void WriteNegInt(uint64_t value) noexcept; + + /** + * Encode a AWS_CBOR_TYPE_FLOAT value to "smallest possible", but will not be encoded into + * half-precision float, as it's not well supported cross languages. + * + * To be more specific, it will be encoded into integer/negative/float + * (Order with priority) when the conversion will not cause precision loss. + * + * @param value value to encode. + */ + void WriteFloat(double value) noexcept; + + /** + * Encode a Bytes value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "value" will be encoded first and then the value of + * "value" will be followed. + * + * @param value value to encode. + */ + void WriteBytes(ByteCursor value) noexcept; + + /** + * Encode a Text value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "value" will be encoded first and then the value of + * "value" will be followed. + * + * @param value value to encode. + */ + void WriteText(ByteCursor value) noexcept; + + /** + * Encode an ArrayStart value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * Notes: it's user's responsibility to keep the integrity of the array to be encoded. + * + * @param number_entries the number of CBOR data items to be followed as the content of the array. + */ + void WriteArrayStart(size_t number_entries) noexcept; + + /** + * Encode a MapStart value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * + * Notes: it's user's responsibility to keep the integrity of the map to be encoded. + * + * @param number_entries the number of pair of CBOR data items as key and value to be followed as + * the content of the map. + */ + void WriteMapStart(size_t number_entries) noexcept; + + /** + * Encode a Tag value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * The following CBOR data item will be the content of the tagged value. + * Notes: it's user's responsibility to keep the integrity of the tagged value to follow the RFC8949 + * section 3.4 + * + * @param tag_number The tag value to encode. + */ + void WriteTag(uint64_t tag_number) noexcept; + + /** + * Encode a simple value Null + */ + void WriteNull() noexcept; + + /** + * Encode a simple value Undefined + */ + void WriteUndefined() noexcept; + + /** + * Encode a simple value Bool + */ + void WriteBool(bool value) noexcept; + + /** + * Encode a simple value Break + * Notes: no error checking, it's user's responsibility to track the break to close the corresponding + * indef_start + */ + void WriteBreak() noexcept; + + /** + * Encode an IndefBytesStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefBytesStart() noexcept; + + /** + * Encode an IndefTextStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefTextStart() noexcept; + + /** + * Encode an IndefArrayStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefArrayStart() noexcept; + + /** + * Encode an IndefMapStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefMapStart() noexcept; + + private: + struct aws_cbor_encoder *m_encoder; + }; + + class AWS_CRT_CPP_API CborDecoder + { + + public: + CborDecoder(const CborDecoder &) = delete; + CborDecoder(CborDecoder &&) = delete; + CborDecoder &operator=(const CborDecoder &) = delete; + CborDecoder &operator=(CborDecoder &&) = delete; + + /** + * Construct a new Cbor Decoder object + * + * @param allocator + * @param src The src data to decode from. + */ + CborDecoder(Allocator *allocator, ByteCursor src) noexcept; + ~CborDecoder() noexcept; + + /** + * Get the length of the remaining bytes of the source. Once the source was decoded, it will be + * consumed, and result in decrease of the remaining length of bytes. + * + * @return The length of bytes remaining of the decoder source. + */ + size_t GetRemainingLength() noexcept; + + /** + * Decode the next element and store it in the decoder cache if there was no element cached. + * If there was an element cached, just return the type of the cached element. + * + * @param out_type + * @return success/failure + */ + bool PeekType(CborType &out_type) noexcept; + + /** + * Consume the next data item, includes all the content within the data item. + * + * As an example for the following CBOR, this function will consume all the data + * as it's only one CBOR data item, an indefinite map with 2 key, value pair: + * 0xbf6346756ef563416d7421ff + * BF -- Start indefinite-length map + * 63 -- First key, UTF-8 string length 3 + * 46756e -- "Fun" + * F5 -- First value, true + * 63 -- Second key, UTF-8 string length 3 + * 416d74 -- "Amt" + * 21 -- Second value, -2 + * FF -- "break" + * + * Notes: this function will not ensure the data item is well-formed. + * + * @return success/failure + */ + bool ConsumeNextWholeDataItem() noexcept; + + /** + * Consume the next single element, without the content followed by the element. + * + * As an example for the following CBOR, this function will only consume the + * 0xBF, "Start indefinite-length map", not any content of the map represented. + * The next element to decode will start from 0x63. + * 0xbf6346756ef563416d7421ff + * BF -- Start indefinite-length map + * 63 -- First key, UTF-8 string length 3 + * 46756e -- "Fun" + * F5 -- First value, true + * 63 -- Second key, UTF-8 string length 3 + * 416d74 -- "Amt" + * 21 -- Second value, -2 + * FF -- "break" + * + * @return success/failure + */ + bool ConsumeNextSingleElement() noexcept; + + /** + * Get the next element based on the type. If the next element doesn't match the expected type, an error + * will be raised. If the next element has already been cached, it will consume the cached item when no + * error was returned. Specifically: + * - Uint - PopNextUnsignedIntVal + * - NegInt - PopNextNegativeIntVal, it represents (-1 - &out) + * - Float - PopNextFloatVal + * - Bytes - PopNextBytesVal + * - Text - PopNextTextVal + * + * @param out + * @return success/failure + */ + bool PopNextUnsignedIntVal(uint64_t &out) noexcept; + bool PopNextNegativeIntVal(uint64_t &out) noexcept; + bool PopNextFloatVal(double &out) noexcept; + bool PopNextBooleanVal(bool &out) noexcept; + bool PopNextBytesVal(ByteCursor &out) noexcept; + bool PopNextTextVal(ByteCursor &out) noexcept; + + /** + * Get the next ArrayStart element. Only consume the ArrayStart element and set the size of array to + * &out_size, not the content of the array. The next &out_size CBOR data items will be the content of + * the array for a valid CBOR data. + * + * Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The + * designed way to handle indefinite-length is: + * - Get IndefArrayStart from PeekType + * - Call ConsumeNextSingleElement to pop the indefinite-length start. + * - Decode the next data item until Break is read. + * + * @param out_size Store the size of array if succeeded. + * @return success/failure + */ + bool PopNextArrayStart(uint64_t &out_size) noexcept; + + /** + * Get the next MapStart element. Only consume the MapStart element and set the size of array to + * &out_size, not the content of the map. The next &out_size pair of CBOR data items as key and value + * will be the content of the array for a valid CBOR data. + * + * Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The + * designed way to handle indefinite-length is: + * - Get IndefMapStart from PeekType + * - Call ConsumeNextSingleElement to pop the indefinite-length start. + * - Decode the next data item until Break is read. + * + * @param out_size Store the size of map if succeeded. + * @return success/failure + */ + bool PopNextMapStart(uint64_t &out_size) noexcept; + + /** + * Get the next Tag element. Only consume the Tag element and set the tag value to out_tag_val, + * not the content of the tagged value. The next CBOR data item will be the content of the tagged value + * for a valid CBOR data. + * + * @param out_tag_val Store the tag value if succeeded. + * @return success/failure + */ + bool PopNextTagVal(uint64_t &out_tag_val) noexcept; + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } + + private: + Allocator *m_allocator; + struct aws_cbor_decoder *m_decoder; + /* Error */ + int m_lastError; + }; + } // namespace Cbor + + } // namespace Crt +} // namespace Aws diff --git a/source/cbor/Cbor.cpp b/source/cbor/Cbor.cpp new file mode 100644 index 000000000..50f5ee12c --- /dev/null +++ b/source/cbor/Cbor.cpp @@ -0,0 +1,209 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include + +namespace Aws +{ + namespace Crt + { + namespace Cbor + { + /***************************************************** + * + * CborEncoder + * + *****************************************************/ + CborEncoder::CborEncoder(Crt::Allocator *allocator) noexcept + { + m_encoder = aws_cbor_encoder_new(allocator); + } + + CborEncoder::~CborEncoder() noexcept { aws_cbor_encoder_destroy(m_encoder); } + + ByteCursor CborEncoder::GetEncodedData() noexcept { return aws_cbor_encoder_get_encoded_data(m_encoder); } + void CborEncoder::Reset() noexcept { aws_cbor_encoder_reset(m_encoder); } + + void CborEncoder::WriteUint(uint64_t value) noexcept { aws_cbor_encoder_write_uint(m_encoder, value); } + void CborEncoder::WriteNegInt(uint64_t value) noexcept { aws_cbor_encoder_write_negint(m_encoder, value); } + + void CborEncoder::WriteFloat(double value) noexcept { aws_cbor_encoder_write_float(m_encoder, value); } + + void CborEncoder::WriteBytes(ByteCursor value) noexcept { aws_cbor_encoder_write_bytes(m_encoder, value); } + + void CborEncoder::WriteText(ByteCursor value) noexcept { aws_cbor_encoder_write_text(m_encoder, value); } + + void CborEncoder::WriteArrayStart(size_t number_entries) noexcept + { + aws_cbor_encoder_write_array_start(m_encoder, number_entries); + } + + void CborEncoder::WriteMapStart(size_t number_entries) noexcept + { + aws_cbor_encoder_write_map_start(m_encoder, number_entries); + } + + void CborEncoder::WriteTag(uint64_t tag_number) noexcept + { + aws_cbor_encoder_write_tag(m_encoder, tag_number); + } + + void CborEncoder::WriteNull() noexcept { aws_cbor_encoder_write_null(m_encoder); } + + void CborEncoder::WriteUndefined() noexcept { aws_cbor_encoder_write_undefined(m_encoder); } + + void CborEncoder::WriteBool(bool value) noexcept { aws_cbor_encoder_write_bool(m_encoder, value); } + + void CborEncoder::WriteBreak() noexcept { aws_cbor_encoder_write_break(m_encoder); } + + void CborEncoder::WriteIndefBytesStart() noexcept { aws_cbor_encoder_write_indef_bytes_start(m_encoder); } + + void CborEncoder::WriteIndefTextStart() noexcept { aws_cbor_encoder_write_indef_text_start(m_encoder); } + + void CborEncoder::WriteIndefArrayStart() noexcept { aws_cbor_encoder_write_indef_array_start(m_encoder); } + + void CborEncoder::WriteIndefMapStart() noexcept { aws_cbor_encoder_write_indef_map_start(m_encoder); } + + /***************************************************** + * + * CborDecoder + * + *****************************************************/ + CborDecoder::CborDecoder(Crt::Allocator *allocator, ByteCursor src) noexcept + { + m_decoder = aws_cbor_decoder_new(allocator, src); + } + + CborDecoder::~CborDecoder() noexcept { aws_cbor_decoder_destroy(m_decoder); } + + size_t CborDecoder::GetRemainingLength() noexcept + { + return aws_cbor_decoder_get_remaining_length(m_decoder); + } + + bool CborDecoder::PeekType(CborType &out_type) noexcept + { + enum aws_cbor_type out_type_c = AWS_CBOR_TYPE_UNKNOWN; + if (aws_cbor_decoder_peek_type(m_decoder, &out_type_c) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + out_type = (CborType)out_type_c; + return true; + } + bool CborDecoder::ConsumeNextWholeDataItem() noexcept + { + if (aws_cbor_decoder_consume_next_whole_data_item(m_decoder) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::ConsumeNextSingleElement() noexcept + { + if (aws_cbor_decoder_consume_next_single_element(m_decoder) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextUnsignedIntVal(uint64_t &out) noexcept + { + if (aws_cbor_decoder_pop_next_unsigned_int_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextNegativeIntVal(uint64_t &out) noexcept + { + if (aws_cbor_decoder_pop_next_negative_int_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextFloatVal(double &out) noexcept + { + if (aws_cbor_decoder_pop_next_float_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextBooleanVal(bool &out) noexcept + { + if (aws_cbor_decoder_pop_next_boolean_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextBytesVal(ByteCursor &out) noexcept + { + if (aws_cbor_decoder_pop_next_bytes_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextTextVal(ByteCursor &out) noexcept + { + if (aws_cbor_decoder_pop_next_text_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextArrayStart(uint64_t &out_size) noexcept + { + if (aws_cbor_decoder_pop_next_array_start(m_decoder, &out_size) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextMapStart(uint64_t &out_size) noexcept + { + if (aws_cbor_decoder_pop_next_map_start(m_decoder, &out_size) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextTagVal(uint64_t &out_tag_val) noexcept + { + if (aws_cbor_decoder_pop_next_tag_val(m_decoder, &out_tag_val) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + } // namespace Cbor + } // namespace Crt +} // namespace Aws diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d0576813b..e78c7d974 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -274,6 +274,8 @@ if(AWS_HAS_CI_ENVIRONMENT AND NOT BYO_CRYPTO) endif() endif() +add_test_case(CborSanityTest) + generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp new file mode 100644 index 000000000..f85659d40 --- /dev/null +++ b/tests/CborTest.cpp @@ -0,0 +1,36 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include + +using namespace Aws::Crt; + +static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + { + ApiHandle apiHandle(allocator); + Cbor::CborEncoder encoder(allocator); + uint64_t expected_val = 100; + encoder.WriteUint(expected_val); + ByteCursor cursor = encoder.GetEncodedData(); + + Cbor::CborDecoder decoder(allocator, cursor); + Cbor::CborType out_type = Cbor::CborType::Unknown; + ASSERT_TRUE(decoder.PeekType(out_type)); + ASSERT_UINT_EQUALS((int)out_type, (int)Cbor::CborType::Uint); + uint64_t out_val = 0; + ASSERT_TRUE(decoder.PopNextUnsignedIntVal(out_val)); + ASSERT_UINT_EQUALS(expected_val, out_val); + + auto length = decoder.GetRemainingLength(); + ASSERT_UINT_EQUALS(0, length); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(CborSanityTest, s_CborSanityTest)