/* * * Copyright (c) 2023 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "sample_data.h" namespace { using namespace chip::Decoders; using namespace chip::FlatTree; using namespace chip::TLV; using namespace chip::TLVMeta; using namespace chip::TestData; // size 1 to avoid compilers complaining about empty arrays // (not allowed by ISO 9899:2011 6.7.6.2: // If the expression is a constant expression, it shall have a value greater // than zero. // ). We still claim its size is 0 in empty_meta though. const Entry _empty_item[1] = {}; const std::array, 1> empty_meta = { { { 0, _empty_item } } }; const Entry _FakeProtocolData[] = { { { AttributeTag(5), "proto5", ItemType::kDefault }, kInvalidNodeIndex }, { { AttributeTag(16), "proto16", ItemType::kDefault }, kInvalidNodeIndex }, }; const Entry _FakeProtocols[] = { { { ClusterTag(0xFFFF0000), "FakeSC", ItemType::kDefault }, 1 }, { { ClusterTag(0xFFFF0001), "FakeIM", ItemType::kDefault }, 1 }, }; const std::array, 53 + 2> fake_protocols_meta = { { { 2, _FakeProtocols }, { 2, _FakeProtocolData }, } }; void TestSampleData(const PayloadDecoderInitParams & params, const SamplePayload & data, const char * expectation) { chip::Decoders::PayloadDecoder<64, 128> decoder( PayloadDecoderInitParams(params).SetProtocol(data.protocolId).SetMessageType(data.messageType)); decoder.StartDecoding(data.payload); chip::StringBuilder<4096> output_builder; PayloadEntry entry; int nesting = 0; while (decoder.Next(entry)) { switch (entry.GetType()) { case PayloadEntry::IMPayloadType::kNestingExit: nesting--; continue; case PayloadEntry::IMPayloadType::kAttribute: output_builder.AddFormat("%*sATTRIBUTE: %" PRIi32 "/%" PRIi32 "\n", nesting * 2, "", entry.GetClusterId(), entry.GetAttributeId()); continue; case PayloadEntry::IMPayloadType::kCommand: output_builder.AddFormat("%*sCOMMAND: %" PRIi32 "/%" PRIi32 "\n", nesting * 2, "", entry.GetClusterId(), entry.GetCommandId()); continue; case PayloadEntry::IMPayloadType::kEvent: output_builder.AddFormat("%*sEVENT: %" PRIi32 "/%" PRIi32 "\n", nesting * 2, "", entry.GetClusterId(), entry.GetEventId()); continue; default: break; } output_builder.AddFormat("%*s%s", nesting * 2, "", entry.GetName()); if (entry.GetType() == PayloadEntry::IMPayloadType::kNestingEnter) { nesting++; } else if (entry.GetValueText()[0] != '\0') { output_builder.AddFormat(": %s", entry.GetValueText()); } else { output_builder.Add(": EMPTY"); } output_builder.AddFormat("\n"); } output_builder.AddMarkerIfOverflow(); if (strcmp(output_builder.c_str(), expectation) != 0) { printf("!!!!!!!!!!!!!!!!!!! EXPECTED OUTPUT !!!!!!!!!!!!!!!!!\n"); printf("%s\n", expectation); printf("!!!!!!!!!!!!!!!!!!! ACTUAL OUTPUT !!!!!!!!!!!!!!!!!\n"); printf("%s\n", output_builder.c_str()); unsigned idx = 0; while (expectation[idx] == output_builder.c_str()[idx]) { idx++; } printf("!!!!!!!!!!!!!!!!!!! DIFF LOCATION !!!!!!!!!!!!!!!!!\n"); printf("First diff at index %u\n", idx); chip::StringBuilder<31> partial; printf("EXPECT: '%s'\n", partial.Reset().Add(expectation + idx).AddMarkerIfOverflow().c_str()); printf("ACTUAL: '%s'\n", partial.Reset().Add(output_builder.c_str() + idx).AddMarkerIfOverflow().c_str()); } EXPECT_STREQ(output_builder.c_str(), expectation); } TEST(TestDecoding, TestFullDataDecoding) { PayloadDecoderInitParams params; params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta).SetClusterDecodeTree(chip::TLVMeta::clusters_meta); TestSampleData(params, secure_channel_mrp_ack, "mrp_ack: EMPTY\n"); TestSampleData(params, secure_channel_pkbdf_param_request, "pbkdf_param_request\n" " initiator_random: hex:7C8698755B8E9866BB4FFDC27B733F3B6EF7F83D43FBE0CA6AD2B8C52C8F4236\n" " initiator_session_id: 37677\n" " passcode_id: 0\n" " has_pbkdf_parameters: false\n"); TestSampleData(params, secure_channel_pkbdf_param_response, "pbkdf_param_response\n" " initiator_random: hex:7C8698755B8E9866BB4FFDC27B733F3B6EF7F83D43FBE0CA6AD2B8C52C8F4236\n" " responder_random: hex:A44EB3E1A751A88A32BAB59EF16EB9764C20E1A9DDBEF6EFE3F588C943C58424\n" " responder_session_id: 40168\n" " pbkdf_parameters\n" " iterations: 1000\n" " salt: hex:E8FC1E6FD0023422B3CA7ECEDD344444551C814D3D0B0EB9C096F00E8A8051B2\n"); TestSampleData(params, secure_channel_pase_pake1, // clang-format off "pase_pake1\n" " pA: hex:0422ABC7A84352850456BD4A510905FE6BB782A0863A9382550E1228020801B22EEC4102C60F80082842B9739705FCD37F134651442A41E3723DFFE0...\n" // clang-format on ); TestSampleData(params, secure_channel_pase_pake2, // clang-format off "pase_pake2\n" " pB: hex:04B6A44A3347C6B77900A3674CA19F40F25F056F8CB344EC1B4FA7888B9E6B570B7010431C5D0BE4021FE74A96C40721765FDA6802BE8DFDF5624332...\n" " cB: hex:40E7452275E38AEBAF0E0F6FAB33A1B0CB5AEB5E824230DD40D0071DC7E55C87\n" // clang-format on ); TestSampleData(params, secure_channel_pase_pake3, "pase_pake3\n" " cA: hex:6008C72EDEC9D25D4A36522F0BF23058F9378EFE38CBBCCE8C6853900169BC38\n"); TestSampleData(params, secure_channel_status_report, "status_report: BINARY DATA\n"); TestSampleData(params, im_protocol_read_request, "read_request\n" " attribute_requests\n" " Anonymous<>\n" " cluster_id: 49 == 'NetworkCommissioning'\n" " attribute_id: 65532 == 'featureMap'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 0 == 'breadcrumb'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 1 == 'basicCommissioningInfo'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 2 == 'regulatoryConfig'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 3 == 'locationCapability'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 40 == 'BasicInformation'\n" " attribute_id: 2 == 'vendorID'\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 40 == 'BasicInformation'\n" " attribute_id: 4 == 'productID'\n" " Anonymous<>\n" " cluster_id: 49 == 'NetworkCommissioning'\n" " attribute_id: 3 == 'connectMaxTimeSeconds'\n" " fabric_filtered: false\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_report_data, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 28559721\n" " path\n" " endpoint_id: 0\n" " cluster_id: 49 == 'NetworkCommissioning'\n" " attribute_id: 3 == 'connectMaxTimeSeconds'\n" " NetworkCommissioning::connectMaxTimeSeconds: 0\n" " Anonymous<>\n" " attribute_data\n" " data_version: 664978787\n" " path\n" " endpoint_id: 0\n" " cluster_id: 40 == 'BasicInformation'\n" " attribute_id: 4 == 'productID'\n" " BasicInformation::productID: 32769\n" " Anonymous<>\n" " attribute_data\n" " data_version: 664978787\n" " path\n" " endpoint_id: 0\n" " cluster_id: 40 == 'BasicInformation'\n" " attribute_id: 2 == 'vendorID'\n" " BasicInformation::vendorID: 65521\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 3 == 'locationCapability'\n" " GeneralCommissioning::locationCapability: 2 == kIndoorOutdoor\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 2 == 'regulatoryConfig'\n" " GeneralCommissioning::regulatoryConfig: 0 == kIndoor\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 1 == 'basicCommissioningInfo'\n" " GeneralCommissioning::basicCommissioningInfo\n" " failSafeExpiryLengthSeconds: 60\n" " maxCumulativeFailsafeSeconds: 900\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48 == 'GeneralCommissioning'\n" " attribute_id: 0 == 'breadcrumb'\n" " GeneralCommissioning::breadcrumb: 0\n" " Anonymous<>\n" " attribute_data\n" " data_version: 28559721\n" " path\n" " endpoint_id: 0\n" " cluster_id: 49 == 'NetworkCommissioning'\n" " attribute_id: 65532 == 'featureMap'\n" " NetworkCommissioning::featureMap: 4 == kEthernetNetworkInterface\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); // Different content TestSampleData(params, im_protocol_report_data_acl, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 3420147058\n" " path\n" " endpoint_id: 0\n" " cluster_id: 31 == 'AccessControl'\n" " attribute_id: 0 == 'acl'\n" " AccessControl::acl\n" " Anonymous<>\n" " privilege: 5 == kAdminister\n" " authMode: 2 == kCASE\n" " subjects\n" " Anonymous<>: 112233\n" " targets: NULL\n" " fabricIndex: 1\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); TestSampleData( params, im_protocol_report_data_window_covering, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 2054986218\n" " path\n" " endpoint_id: 1\n" " cluster_id: 258 == 'WindowCovering'\n" " attribute_id: 7 == 'configStatus'\n" " WindowCovering::configStatus: 27 == kOperational | kOnlineReserved | kLiftPositionAware | kTiltPositionAware\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_invoke_request, "invoke_request\n" " suppress_response: false\n" " timed_request: false\n" " invoke_requests\n" " Anonymous<>\n" " path\n" " endpoint_id: 1\n" " cluster_id: 6 == 'OnOff'\n" " command_id: 2 == 'Toggle'\n" " OnOff::Toggle\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_invoke_response, "invoke_response\n" " suppress_response: false\n" " invoke_responses\n" " Anonymous<>\n" " status\n" " path\n" " endpoint_id: 1\n" " cluster_id: 6 == 'OnOff'\n" " command_id: 2 == 'Toggle'\n" " status\n" " status: 0 == kSuccess\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_invoke_request_change_channel, "invoke_request\n" " suppress_response: false\n" " timed_request: false\n" " invoke_requests\n" " Anonymous<>\n" " path\n" " endpoint_id: 1\n" " cluster_id: 1284 == 'Channel'\n" " command_id: 0 == 'ChangeChannel'\n" " Channel::ChangeChannel\n" " match: \"channel name\"\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_event_software_fault, "report_data\n" " event_reports\n" " Anonymous<>\n" " event_data\n" " path\n" " endpoint_id: 0\n" " cluster_id: 52 == 'SoftwareDiagnostics'\n" " event_id: 0 == 'SoftwareFault'\n" " event_number: 196610\n" " priority: 1\n" " epoch_timestamp: 1690566548210\n" " SoftwareDiagnostics::SoftwareFault\n" " id: 2454952\n" " name: \"2454952\"\n" " faultRecording: hex:467269204A756C2032382031333A34393A30382032303233\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_event_multipress, "report_data\n" " event_reports\n" " Anonymous<>\n" " event_data\n" " path\n" " endpoint_id: 0\n" " cluster_id: 59 == 'Switch'\n" " event_id: 6 == 'MultiPressComplete'\n" " event_number: 196611\n" " priority: 1\n" " epoch_timestamp: 1690566820898\n" " Switch::MultiPressComplete\n" " previousPosition: 1\n" " totalNumberOfPressesCounted: 23\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); } TEST(TestDecoding, TestMetaDataOnlyDecoding) { PayloadDecoderInitParams params; // NO CLUSTER DECODE TREE params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta); TestSampleData(params, secure_channel_mrp_ack, "mrp_ack: EMPTY\n"); TestSampleData(params, secure_channel_pkbdf_param_request, "pbkdf_param_request\n" " initiator_random: hex:7C8698755B8E9866BB4FFDC27B733F3B6EF7F83D43FBE0CA6AD2B8C52C8F4236\n" " initiator_session_id: 37677\n" " passcode_id: 0\n" " has_pbkdf_parameters: false\n"); TestSampleData(params, im_protocol_read_request, "read_request\n" " attribute_requests\n" " Anonymous<>\n" " cluster_id: 49\n" " attribute_id: 65532\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 0\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 1\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 2\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 3\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 40\n" " attribute_id: 2\n" " Anonymous<>\n" " endpoint_id: 0\n" " cluster_id: 40\n" " attribute_id: 4\n" " Anonymous<>\n" " cluster_id: 49\n" " attribute_id: 3\n" " fabric_filtered: false\n" " interaction_model_revison: 1\n"); TestSampleData(params, im_protocol_report_data, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 28559721\n" " path\n" " endpoint_id: 0\n" " cluster_id: 49\n" " attribute_id: 3\n" " ATTRIBUTE: 49/3\n" " Anonymous<>\n" " attribute_data\n" " data_version: 664978787\n" " path\n" " endpoint_id: 0\n" " cluster_id: 40\n" " attribute_id: 4\n" " ATTRIBUTE: 40/4\n" " Anonymous<>\n" " attribute_data\n" " data_version: 664978787\n" " path\n" " endpoint_id: 0\n" " cluster_id: 40\n" " attribute_id: 2\n" " ATTRIBUTE: 40/2\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 3\n" " ATTRIBUTE: 48/3\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 2\n" " ATTRIBUTE: 48/2\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 1\n" " ATTRIBUTE: 48/1\n" " Anonymous<>\n" " attribute_data\n" " data_version: 1414030794\n" " path\n" " endpoint_id: 0\n" " cluster_id: 48\n" " attribute_id: 0\n" " ATTRIBUTE: 48/0\n" " Anonymous<>\n" " attribute_data\n" " data_version: 28559721\n" " path\n" " endpoint_id: 0\n" " cluster_id: 49\n" " attribute_id: 65532\n" " ATTRIBUTE: 49/65532\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); // Different content TestSampleData(params, im_protocol_report_data_acl, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 3420147058\n" " path\n" " endpoint_id: 0\n" " cluster_id: 31\n" " attribute_id: 0\n" " ATTRIBUTE: 31/0\n" " suppress_response: true\n" " interaction_model_revison: 1\n"); } TEST(TestDecoding, TestEmptyClusterMetaDataDecode) { PayloadDecoderInitParams params; params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta).SetClusterDecodeTree(empty_meta); TestSampleData(params, secure_channel_mrp_ack, "mrp_ack: EMPTY\n"); TestSampleData(params, im_protocol_report_data_acl, "report_data\n" " attribute_reports\n" " Anonymous<>\n" " attribute_data\n" " data_version: 3420147058\n" " path\n" " endpoint_id: 0\n" " cluster_id: 31\n" " attribute_id: 0\n" " 0x1f::ATTR(0x0)\n" // Cluster 31, attribute 0 " AnonymousTag()\n" // List entry (acl is a list) " ContextTag(0x1): 5\n" // privilege " ContextTag(0x2): 2\n" // authMode " ContextTag(0x3)\n" // subjects " AnonymousTag(): 112233\n" // List entry (subjects is a list) " ContextTag(0x4): NULL\n" // targets " ContextTag(0xFE): 1\n" // fabricIndex " suppress_response: true\n" " interaction_model_revison: 1\n"); } TEST(TestDecoding, TestMissingDecodeData) { PayloadDecoderInitParams params; params.SetProtocolDecodeTree(empty_meta).SetClusterDecodeTree(empty_meta); TestSampleData(params, secure_channel_mrp_ack, "PROTO(0x0, 0x10): UNKNOWN\n"); TestSampleData(params, im_protocol_report_data_acl, "PROTO(0x1, 0x5): UNKNOWN\n"); } TEST(TestDecoding, TestWrongDecodeData) { PayloadDecoderInitParams params; params.SetProtocolDecodeTree(fake_protocols_meta).SetClusterDecodeTree(empty_meta); TestSampleData(params, secure_channel_mrp_ack, "proto16: EMPTY\n"); TestSampleData(params, im_protocol_report_data_acl, "proto5\n" " ContextTag(0x1)\n" " AnonymousTag()\n" " ContextTag(0x1)\n" " ContextTag(0x0): 3420147058\n" " ContextTag(0x1)\n" " ContextTag(0x2): 0\n" " ContextTag(0x3): 31\n" " ContextTag(0x4): 0\n" " ContextTag(0x2)\n" " AnonymousTag()\n" " ContextTag(0x1): 5\n" " ContextTag(0x2): 2\n" " ContextTag(0x3)\n" " AnonymousTag(): 112233\n" " ContextTag(0x4): NULL\n" " ContextTag(0xFE): 1\n" " ContextTag(0x4): true\n" " ContextTag(0xFF): 1\n"); } TEST(TestDecoding, TestNestingOverflow) { PayloadDecoderInitParams params; params.SetProtocolDecodeTree(fake_protocols_meta).SetClusterDecodeTree(empty_meta); uint8_t data_buffer[1024]; chip::TLV::TLVWriter writer; writer.Init(data_buffer, sizeof(data_buffer)); chip::TLV::TLVType unusedType; // Protocols start with an anonymous tagged structure, after which lists can be of any tags EXPECT_EQ(writer.StartContainer(AnonymousTag(), kTLVType_Structure, unusedType), CHIP_NO_ERROR); // nesting overflow here for (uint8_t i = 0; i < 32; i++) { EXPECT_EQ(writer.StartContainer(ContextTag(i), kTLVType_List, unusedType), CHIP_NO_ERROR); } // Go back to 24 (still too much nesting) for (uint8_t i = 0; i < 8; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.StartContainer(ContextTag(static_cast(i + 0x10)), kTLVType_List, unusedType), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } // Go back to 8 for (uint8_t i = 0; i < 16; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.StartContainer(ContextTag(static_cast(i + 0x20)), kTLVType_List, unusedType), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } // Go back to 4 for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.StartContainer(ContextTag(static_cast(i + 0x30)), kTLVType_List, unusedType), CHIP_NO_ERROR); } for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } // close everything for (uint8_t i = 0; i < 4; i++) { EXPECT_EQ(writer.EndContainer(kTLVType_List), CHIP_NO_ERROR); } EXPECT_EQ(writer.EndContainer(kTLVType_Structure), CHIP_NO_ERROR); SamplePayload fake_payload{ chip::Protocols::InteractionModel::Id, 5, chip::ByteSpan(data_buffer, writer.GetLengthWritten()) }; TestSampleData(params, fake_payload, "proto5\n" " ContextTag(0x0)\n" " ContextTag(0x1)\n" " ContextTag(0x2)\n" " ContextTag(0x3)\n" " ContextTag(0x4)\n" " ContextTag(0x5)\n" " ContextTag(0x6)\n" " ContextTag(0x7)\n" " ContextTag(0x8)\n" " ContextTag(0x9)\n" " ContextTag(0xA)\n" " ContextTag(0xB)\n" " ContextTag(0xC)\n" " ContextTag(0xD)\n" " ContextTag(0xE)\n" " ContextTag(0xF): NESTING DEPTH REACHED\n" " ContextTag(0x20)\n" " ContextTag(0x21)\n" " ContextTag(0x22)\n" " ContextTag(0x23)\n" " ContextTag(0x30)\n" " ContextTag(0x31)\n" " ContextTag(0x32)\n" " ContextTag(0x33)\n"); } } // namespace