blob: 685dc5b702107f0c1361154559f084900e863910 [file]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* 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.
*/
/**
* @file
* This file implements a process to effect a functional test for
* the CHIP Abstract Syntax Notifcation One (ASN1) encode and
* decode interfaces.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <pw_unit_test/framework.h>
#include <lib/asn1/ASN1.h>
#include <lib/asn1/ASN1Macros.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/core/TLV.h>
using namespace chip;
using namespace chip::ASN1;
using namespace chip::TLV;
enum
{
kTestVal_01_Bool = false,
kTestVal_02_Bool = true,
kTestVal_03_BitString = 0x0,
kTestVal_04_BitString = 0x1,
kTestVal_05_BitString = 0x3,
kTestVal_06_BitString = 0x17,
kTestVal_07_BitString = 0x37,
kTestVal_08_BitString = 0x187,
kTestVal_09_BitString = 0x3E7,
kTestVal_10_Int = 0,
kTestVal_11_Int = 1,
kTestVal_12_Int = -1,
kTestVal_13_Int = 0xFF00FF,
kTestVal_14_Int = -0xFF00FF,
kTestVal_15_Int = INT32_MAX,
kTestVal_16_Int = INT32_MIN,
kTestVal_17_Int = INT64_MAX,
kTestVal_18_Int = INT64_MIN,
kTestVal_19_OID = kOID_AttributeType_OrganizationName,
kTestVal_23_OID = kOID_AttributeType_CommonName,
kTestVal_24_Int = 42,
};
// clang-format off
static uint8_t kTestVal_09_BitString_AsOctetString[] = { 0xE7, 0xC0 };
static uint8_t kTestVal_20_OctetString[] = { 0x01, 0x03, 0x05, 0x07, 0x10, 0x30, 0x50, 0x70, 0x00 };
static const char kTestVal_21_PrintableString[] = "Sudden death in Venice";
static const char kTestVal_22_UTFString[] = "Ond bra\xCC\x8A""d do\xCC\x88""d i Venedig";
// clang-format on
// Manually copied from ASN1OID.h for testing.
static const uint8_t sOID_AttributeType_ChipNodeId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x01, 0x01 };
static const uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 };
static const uint8_t sOID_EllipticCurve_prime256v1[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 };
static const uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 };
static const uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 };
static const uint8_t sOID_KeyPurpose_ServerAuth[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01 };
uint8_t TestASN1_EncodedData[] = {
0x30, 0x81, 0xBA, 0x01, 0x01, 0x00, 0x01, 0x01, 0xFF, 0x31, 0x00, 0x03, 0x01, 0x00, 0x03, 0x02, 0x07, 0x80, 0x03, 0x02, 0x06,
0xC0, 0x30, 0x16, 0x30, 0x0F, 0x30, 0x08, 0x03, 0x02, 0x03, 0xE8, 0x03, 0x02, 0x02, 0xEC, 0x03, 0x03, 0x07, 0xE1, 0x80, 0x03,
0x03, 0x06, 0xE7, 0xC0, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0xFF, 0x02, 0x04, 0x00, 0xFF, 0x00, 0xFF, 0x02, 0x04,
0xFF, 0x00, 0xFF, 0x01, 0x02, 0x04, 0x7F, 0xFF, 0xFF, 0xFF, 0x02, 0x04, 0x80, 0x00, 0x00, 0x00, 0x02, 0x08, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x04,
0x09, 0x01, 0x03, 0x05, 0x07, 0x10, 0x30, 0x50, 0x70, 0x00, 0x04, 0x01, 0x01, 0x04, 0x00, 0x1B, 0x00, 0x13, 0x16, 0x53, 0x75,
0x64, 0x64, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x61, 0x74, 0x68, 0x20, 0x69, 0x6E, 0x20, 0x56, 0x65, 0x6E, 0x69, 0x63, 0x65, 0x0C,
0x1A, 0x4F, 0x6E, 0x64, 0x20, 0x62, 0x72, 0x61, 0xCC, 0x8A, 0x64, 0x20, 0x64, 0x6F, 0xCC, 0x88, 0x64, 0x20, 0x69, 0x20, 0x56,
0x65, 0x6E, 0x65, 0x64, 0x69, 0x67, 0x04, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x03, 0x04, 0x00, 0x02, 0x01, 0x2A
};
static CHIP_ERROR EncodeASN1TestData(ASN1Writer & writer)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ASN1_START_SEQUENCE
{
ASN1_ENCODE_BOOLEAN(kTestVal_01_Bool);
ASN1_ENCODE_BOOLEAN(kTestVal_02_Bool);
ASN1_START_SET {}
ASN1_END_SET;
ASN1_ENCODE_BIT_STRING(kTestVal_03_BitString);
ASN1_ENCODE_BIT_STRING(kTestVal_04_BitString);
ASN1_ENCODE_BIT_STRING(kTestVal_05_BitString);
ASN1_START_SEQUENCE
{
ASN1_START_SEQUENCE
{
ASN1_START_SEQUENCE
{
ASN1_ENCODE_BIT_STRING(kTestVal_06_BitString);
ASN1_ENCODE_BIT_STRING(kTestVal_07_BitString);
}
ASN1_END_SEQUENCE;
ASN1_ENCODE_BIT_STRING(kTestVal_08_BitString);
}
ASN1_END_SEQUENCE;
ASN1_ENCODE_BIT_STRING(kTestVal_09_BitString);
}
ASN1_END_SEQUENCE;
ASN1_ENCODE_INTEGER(kTestVal_10_Int);
ASN1_ENCODE_INTEGER(kTestVal_11_Int);
ASN1_ENCODE_INTEGER(kTestVal_12_Int);
ASN1_ENCODE_INTEGER(kTestVal_13_Int);
ASN1_ENCODE_INTEGER(kTestVal_14_Int);
ASN1_ENCODE_INTEGER(kTestVal_15_Int);
ASN1_ENCODE_INTEGER(kTestVal_16_Int);
ASN1_ENCODE_INTEGER(kTestVal_17_Int);
ASN1_ENCODE_INTEGER(kTestVal_18_Int);
ASN1_ENCODE_OBJECT_ID(kTestVal_19_OID);
ASN1_ENCODE_OCTET_STRING(kTestVal_20_OctetString, sizeof(kTestVal_20_OctetString));
ASN1_ENCODE_OCTET_STRING(kTestVal_20_OctetString, 1);
ASN1_ENCODE_OCTET_STRING(kTestVal_20_OctetString, 0);
ASN1_ENCODE_STRING(kASN1UniversalTag_GeneralString, "", 0);
ASN1_ENCODE_STRING(kASN1UniversalTag_PrintableString, kTestVal_21_PrintableString,
static_cast<uint16_t>(strlen(kTestVal_21_PrintableString)));
ASN1_ENCODE_STRING(kASN1UniversalTag_UTF8String, kTestVal_22_UTFString,
static_cast<uint16_t>(strlen(kTestVal_22_UTFString)));
ASN1_START_OCTET_STRING_ENCAPSULATED
{
ASN1_START_SEQUENCE
{
ASN1_ENCODE_OBJECT_ID(kTestVal_23_OID);
ASN1_START_BIT_STRING_ENCAPSULATED
{
ASN1_ENCODE_INTEGER(kTestVal_24_Int);
}
ASN1_END_ENCAPSULATED;
}
ASN1_END_SEQUENCE;
}
ASN1_END_ENCAPSULATED;
}
ASN1_END_SEQUENCE;
exit:
return err;
}
TEST(TestASN1, Encode)
{
CHIP_ERROR err;
static uint8_t buf[2048];
ASN1Writer writer;
size_t encodedLen;
writer.Init(buf);
err = EncodeASN1TestData(writer);
EXPECT_EQ(err, CHIP_NO_ERROR);
encodedLen = writer.GetLengthWritten();
EXPECT_EQ(encodedLen, sizeof(TestASN1_EncodedData));
EXPECT_EQ(memcmp(buf, TestASN1_EncodedData, sizeof(TestASN1_EncodedData)), 0);
#define DUMP_HEX 0
#if DUMP_HEX
for (uint16_t i = 0; i < encodedLen; i++)
{
if (i != 0 && i % 16 == 0)
printf("\n");
printf("0x%02X, ", buf[i]);
}
printf("\n");
#endif
}
TEST(TestASN1, Decode)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ASN1Reader reader;
bool boolVal;
uint32_t bitStringVal;
int64_t intVal;
OID oidVal;
reader.Init(TestASN1_EncodedData);
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_BOOLEAN(boolVal);
EXPECT_EQ(boolVal, kTestVal_01_Bool);
ASN1_PARSE_BOOLEAN(boolVal);
EXPECT_EQ(boolVal, kTestVal_02_Bool);
ASN1_PARSE_ENTER_SET {}
ASN1_EXIT_SET;
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_03_BitString);
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_04_BitString);
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_05_BitString);
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_06_BitString);
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_07_BitString);
}
ASN1_EXIT_SEQUENCE;
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_08_BitString);
}
ASN1_EXIT_SEQUENCE;
ASN1_PARSE_BIT_STRING(bitStringVal);
EXPECT_EQ(bitStringVal, kTestVal_09_BitString);
}
ASN1_EXIT_SEQUENCE;
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_10_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_11_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_12_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_13_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_14_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_15_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_16_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_17_Int);
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_18_Int);
ASN1_PARSE_OBJECT_ID(oidVal);
EXPECT_EQ(oidVal, kTestVal_19_OID);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
EXPECT_EQ(reader.GetValueLen(), sizeof(kTestVal_20_OctetString));
EXPECT_EQ(memcmp(reader.GetValue(), kTestVal_20_OctetString, sizeof(kTestVal_20_OctetString)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
EXPECT_EQ(reader.GetValueLen(), 1u);
EXPECT_EQ(reader.GetValue()[0], kTestVal_20_OctetString[0]);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
EXPECT_EQ(reader.GetValueLen(), 0u);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_GeneralString);
EXPECT_EQ(reader.GetValueLen(), 0u);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_PrintableString);
EXPECT_EQ(reader.GetValueLen(), strlen(kTestVal_21_PrintableString));
EXPECT_EQ(memcmp(reader.GetValue(), kTestVal_21_PrintableString, strlen(kTestVal_21_PrintableString)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_UTF8String);
EXPECT_EQ(reader.GetValueLen(), strlen(kTestVal_22_UTFString));
EXPECT_EQ(memcmp(reader.GetValue(), kTestVal_22_UTFString, strlen(kTestVal_22_UTFString)), 0);
ASN1_PARSE_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_OctetString)
{
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_OBJECT_ID(oidVal);
EXPECT_EQ(oidVal, kTestVal_23_OID);
ASN1_PARSE_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_BitString)
{
ASN1_PARSE_INTEGER(intVal);
EXPECT_EQ(intVal, kTestVal_24_Int);
}
ASN1_EXIT_ENCAPSULATED;
}
ASN1_EXIT_SEQUENCE;
}
ASN1_EXIT_ENCAPSULATED;
}
ASN1_EXIT_SEQUENCE;
exit:
EXPECT_EQ(err, CHIP_NO_ERROR);
}
TEST(TestASN1, NullWriter)
{
CHIP_ERROR err;
ASN1Writer writer;
size_t encodedLen;
writer.InitNullWriter();
err = EncodeASN1TestData(writer);
EXPECT_EQ(err, CHIP_NO_ERROR);
encodedLen = writer.GetLengthWritten();
EXPECT_EQ(encodedLen, 0u);
// Methods that take a reader should still read from it,
// even if the output is suppressed by the null writer.
TLVReader emptyTlvReader;
emptyTlvReader.Init(ByteSpan());
err = writer.PutBitString(0, emptyTlvReader);
EXPECT_EQ(err, CHIP_ERROR_WRONG_TLV_TYPE);
emptyTlvReader.Init(ByteSpan());
err = writer.PutOctetString(kASN1TagClass_ContextSpecific, 123, emptyTlvReader);
EXPECT_EQ(err, CHIP_ERROR_WRONG_TLV_TYPE);
}
TEST(TestASN1, ASN1UniversalTime)
{
struct ASN1TimeTestCase
{
ASN1UniversalTime asn1Time;
const char * asn1TimeStr;
};
struct ASN1TimeErrorTestCase
{
const char * asn1TimeStr;
CHIP_ERROR mExpectedResult;
};
// clang-format off
static ASN1TimeTestCase sASN1TimeTestCases[] = {
// ASN1 Universal Time ASN1_TIME String
// ====================================================
{ { 2020, 10, 15, 14, 23, 43 }, "201015142343Z" },
{ { 2020, 12, 1, 2, 34, 0 }, "201201023400Z" },
{ { 1979, 1, 30, 12, 0, 0 }, "790130120000Z" },
{ { 2079, 1, 30, 12, 0, 0 }, "20790130120000Z" },
{ { 2049, 3, 31, 23, 59, 59 }, "490331235959Z" },
{ { 1949, 3, 31, 23, 59, 59 }, "19490331235959Z" },
{ { 1950, 3, 31, 23, 59, 59 }, "500331235959Z" },
};
// clang-format on
// clang-format off
static ASN1TimeErrorTestCase sASN1TimeErrorTestCases[] = {
// ASN1_TIME String Expected Result
// =======================================================
{ "201015142343z", ASN1_ERROR_UNSUPPORTED_ENCODING },
{ "20105142343Z", ASN1_ERROR_UNSUPPORTED_ENCODING },
{ "2010115142343Z", ASN1_ERROR_UNSUPPORTED_ENCODING },
{ "201014415142343Z", ASN1_ERROR_UNSUPPORTED_ENCODING },
{ "201O15142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201" "\xe9" "15142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "200015142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201315142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201000142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201032142343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201015242343Z", ASN1_ERROR_INVALID_ENCODING },
{ "201015146043Z", ASN1_ERROR_INVALID_ENCODING },
{ "201015142360Z", ASN1_ERROR_INVALID_ENCODING },
};
// clang-format on
for (auto & testCase : sASN1TimeTestCases)
{
CharSpan testStr = CharSpan(testCase.asn1TimeStr, strlen(testCase.asn1TimeStr));
ASN1UniversalTime result;
EXPECT_EQ(result.ImportFrom_ASN1_TIME_string(testStr), CHIP_NO_ERROR);
EXPECT_EQ(result.Year, testCase.asn1Time.Year);
EXPECT_EQ(result.Month, testCase.asn1Time.Month);
EXPECT_EQ(result.Day, testCase.asn1Time.Day);
EXPECT_EQ(result.Hour, testCase.asn1Time.Hour);
EXPECT_EQ(result.Minute, testCase.asn1Time.Minute);
EXPECT_EQ(result.Second, testCase.asn1Time.Second);
char buf[ASN1UniversalTime::kASN1TimeStringMaxLength];
MutableCharSpan resultTimeStr(buf);
EXPECT_EQ(result.ExportTo_ASN1_TIME_string(resultTimeStr), CHIP_NO_ERROR);
EXPECT_TRUE(resultTimeStr.data_equal(testStr));
}
for (auto & testCase : sASN1TimeErrorTestCases)
{
CharSpan testStr = CharSpan(testCase.asn1TimeStr, strlen(testCase.asn1TimeStr));
ASN1UniversalTime result;
EXPECT_EQ(result.ImportFrom_ASN1_TIME_string(testStr), testCase.mExpectedResult);
}
}
TEST(TestASN1, ObjectID)
{
CHIP_ERROR err;
static uint8_t buf[2048];
ASN1Writer writer;
ASN1Reader reader;
size_t encodedLen;
writer.Init(buf, sizeof(buf));
ASN1_START_SEQUENCE
{
ASN1_ENCODE_OBJECT_ID(kOID_AttributeType_MatterNodeId);
ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
ASN1_ENCODE_OBJECT_ID(kOID_EllipticCurve_prime256v1);
ASN1_ENCODE_OBJECT_ID(kOID_Extension_AuthorityKeyIdentifier);
ASN1_ENCODE_OBJECT_ID(kOID_Extension_BasicConstraints);
ASN1_ENCODE_OBJECT_ID(kOID_KeyPurpose_ServerAuth);
}
ASN1_END_SEQUENCE;
encodedLen = writer.GetLengthWritten();
EXPECT_GT(encodedLen, 0u);
reader.Init(buf, encodedLen);
// Parse and check OIDs as actual ASN1 encoded values.
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_AttributeType_ChipNodeId));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_AttributeType_ChipNodeId, sizeof(sOID_AttributeType_ChipNodeId)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_SigAlgo_ECDSAWithSHA256));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_SigAlgo_ECDSAWithSHA256, sizeof(sOID_SigAlgo_ECDSAWithSHA256)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_EllipticCurve_prime256v1));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_EllipticCurve_prime256v1, sizeof(sOID_EllipticCurve_prime256v1)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_Extension_AuthorityKeyIdentifier));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_Extension_AuthorityKeyIdentifier, sizeof(sOID_Extension_AuthorityKeyIdentifier)),
0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_Extension_BasicConstraints));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_Extension_BasicConstraints, sizeof(sOID_Extension_BasicConstraints)), 0);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
EXPECT_EQ(reader.GetValueLen(), sizeof(sOID_KeyPurpose_ServerAuth));
EXPECT_EQ(memcmp(reader.GetValue(), sOID_KeyPurpose_ServerAuth, sizeof(sOID_KeyPurpose_ServerAuth)), 0);
}
ASN1_EXIT_SEQUENCE;
exit:
EXPECT_EQ(err, CHIP_NO_ERROR);
}
TEST(TestASN1, FromTLVReader)
{
CHIP_ERROR err;
static uint8_t tlvBuf[128];
static uint8_t asn1Buf1[128];
static uint8_t asn1Buf2[128];
TLVWriter tlvWriter;
TLVReader tlvReader;
ASN1Writer writer;
ASN1Reader reader;
MutableByteSpan tlvEncodedData(tlvBuf);
MutableByteSpan asn1EncodedData1(asn1Buf1);
MutableByteSpan asn1EncodedData2(asn1Buf2);
TLVType outerContainerType;
// Construct TLV Encoded Structure.
{
tlvWriter.Init(tlvEncodedData);
err = tlvWriter.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainerType);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvWriter.PutBytes(TLV::ContextTag(1), kTestVal_20_OctetString, sizeof(kTestVal_20_OctetString));
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvWriter.PutBytes(TLV::ContextTag(2), kTestVal_09_BitString_AsOctetString,
sizeof(kTestVal_09_BitString_AsOctetString));
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvWriter.PutString(TLV::ContextTag(3), kTestVal_21_PrintableString);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvWriter.EndContainer(outerContainerType);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvWriter.Finalize();
EXPECT_EQ(err, CHIP_NO_ERROR);
}
// Construct first ASN1 SEQUESNCE using values.
writer.Init(asn1EncodedData1);
ASN1_START_SEQUENCE
{
ASN1_ENCODE_OCTET_STRING(kTestVal_20_OctetString, sizeof(kTestVal_20_OctetString));
ASN1_ENCODE_BIT_STRING(kTestVal_09_BitString);
err = writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_PrintableString, false,
reinterpret_cast<const uint8_t *>(kTestVal_21_PrintableString),
static_cast<uint16_t>(strlen(kTestVal_21_PrintableString)));
EXPECT_EQ(err, CHIP_NO_ERROR);
}
ASN1_END_SEQUENCE;
asn1EncodedData1.reduce_size(writer.GetLengthWritten());
// Construct second ASN1 SEQUENCE from TLVReader.
tlvReader.Init(tlvEncodedData);
writer.Init(asn1EncodedData2);
ASN1_START_SEQUENCE
{
err = tlvReader.Next(kTLVType_Structure, AnonymousTag());
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvReader.EnterContainer(outerContainerType);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvReader.Next(kTLVType_ByteString, ContextTag(1));
EXPECT_EQ(err, CHIP_NO_ERROR);
err = writer.PutOctetString(kASN1TagClass_Universal, kASN1UniversalTag_OctetString, tlvReader);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvReader.Next(kTLVType_ByteString, ContextTag(2));
EXPECT_EQ(err, CHIP_NO_ERROR);
err = writer.PutBitString(6, tlvReader);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvReader.Next(kTLVType_UTF8String, ContextTag(3));
EXPECT_EQ(err, CHIP_NO_ERROR);
err = writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_PrintableString, false, tlvReader);
EXPECT_EQ(err, CHIP_NO_ERROR);
err = tlvReader.ExitContainer(outerContainerType);
EXPECT_EQ(err, CHIP_NO_ERROR);
}
ASN1_END_SEQUENCE;
asn1EncodedData2.reduce_size(writer.GetLengthWritten());
// Compare two ASN1 SEQUENCEs.
EXPECT_TRUE(asn1EncodedData2.data_equal(asn1EncodedData1));
// Initialize ASN1Reader and test data.
reader.Init(asn1EncodedData2);
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
ASSERT_NE(reader.GetValue(), nullptr);
EXPECT_EQ(reader.GetValueLen(), sizeof(kTestVal_20_OctetString));
EXPECT_EQ(memcmp(reader.GetValue(), kTestVal_20_OctetString, sizeof(kTestVal_20_OctetString)), 0);
uint32_t val;
ASN1_PARSE_BIT_STRING(val);
EXPECT_EQ(val, kTestVal_09_BitString);
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_PrintableString);
ASSERT_NE(reader.GetValue(), nullptr);
EXPECT_EQ(reader.GetValueLen(), strlen(kTestVal_21_PrintableString));
EXPECT_EQ(memcmp(reader.GetValue(), kTestVal_21_PrintableString, strlen(kTestVal_21_PrintableString)), 0);
}
ASN1_EXIT_SEQUENCE;
exit:
EXPECT_EQ(err, CHIP_NO_ERROR);
}
// ---------------------------------------------------------------------------
// Regression coverage for the behavior change in this PR.
//
// The only externally observable contract change is in ASN1Reader::Next() and
// ASN1Reader::ExitContainer(): an inner element whose declared length overruns
// its parent container previously caused the reader to advance past
// mContainerEnd and report ASN1_END (which the ASN1_EXIT_* macros and
// DumpASN1-style loops treat as a clean end-of-stream, silently swallowing the
// malformed encoding). It now returns ASN1_ERROR_INVALID_ENCODING.
//
// The remaining production changes (the uint32 addition-overflow guards in
// Next()/ExitContainer()/GetConstructedType()/Get*(), the mContainerEnd >=
// mElemStart consistency guards, and the EnterContainer/ExitContainer
// peek-then-commit reordering) are defense-in-depth: DecodeHead already caps
// ValueLen at the remaining buffer, so mHeadLen + ValueLen cannot wrap a
// uint32 from any input that reaches these methods through the public API.
// Those branches are intentionally NOT exercised by fabricated private-state
// tests -- doing so would only prove the guard fires on states the parser
// cannot produce. They are exercised indirectly: every positive test below
// passes through the new guards on its way to a correct result.
//
// The GetBitString() signed-shift fix is a separate, real correctness fix and
// is proven by dedicated tests below (catchable under -fsanitize=shift).
// ---------------------------------------------------------------------------
// Positive round-trip: build a nested SEQUENCE-in-SEQUENCE, then assert that
// GetConstructedType reports valLen = HeadLen + ValueLen exactly equal to the
// byte span of the encoded element in the source buffer. This drives a valid
// element through the new GetConstructedType() overflow/consistency guards and
// confirms they do not reject well-formed input.
TEST(TestASN1, GetConstructedType_RoundTripValidNested)
{
// Hand-rolled DER: SEQUENCE { SEQUENCE { INTEGER 0x42, INTEGER 0x7FFFFFFF } }.
// 30 0B -- outer SEQUENCE, length 11 (entire inner SEQUENCE)
// 30 09 -- inner SEQUENCE, length 9 (two INTEGERs follow)
// 02 01 42 -- INTEGER 0x42
// 02 04 7F FF FF FF -- INTEGER 0x7FFFFFFF
static const uint8_t kEncoded[] = { 0x30, 0x0B, 0x30, 0x09, 0x02, 0x01, 0x42, 0x02, 0x04, 0x7F, 0xFF, 0xFF, 0xFF };
ASN1Reader reader;
reader.Init(kEncoded, sizeof(kEncoded));
// Position at outer SEQUENCE.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
// Capture pointer + length of the OUTER element via GetConstructedType.
const uint8_t * outerVal = nullptr;
uint32_t outerLen = 0;
EXPECT_EQ(reader.GetConstructedType(outerVal, outerLen), CHIP_NO_ERROR);
EXPECT_EQ(outerVal, &kEncoded[0]);
EXPECT_EQ(outerLen, static_cast<uint32_t>(sizeof(kEncoded)));
// Descend, position on inner SEQUENCE, capture its span.
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
const uint8_t * innerVal = nullptr;
uint32_t innerLen = 0;
EXPECT_EQ(reader.GetConstructedType(innerVal, innerLen), CHIP_NO_ERROR);
ASSERT_NE(innerVal, nullptr);
// Inner element must be wholly contained inside the outer element's reported
// span, and its first byte must be the SEQUENCE tag (0x30).
EXPECT_GE(innerVal, outerVal);
EXPECT_LE(innerVal + innerLen, outerVal + outerLen);
EXPECT_EQ(innerVal[0], 0x30u);
// GetConstructedType on a primitive element must fail with INVALID_STATE.
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR); // INTEGER 0x42
EXPECT_FALSE(reader.IsConstructed());
const uint8_t * v = nullptr;
uint32_t vLen = 0;
EXPECT_EQ(reader.GetConstructedType(v, vLen), ASN1_ERROR_INVALID_STATE);
}
// Edge case: a tag with a 0-byte length (ASN.1 NULL { 0x05, 0x00 }) should be
// readable, and Next() / EnterContainer / ExitContainer must all advance past it
// correctly without confusing the reader's position.
TEST(TestASN1, ASN1Reader_ZeroLengthElements)
{
// SEQUENCE { NULL, NULL, INTEGER 0x2A }
// 30 07 -- SEQUENCE, length 7
// 05 00 -- NULL
// 05 00 -- NULL
// 02 01 2A -- INTEGER 42
static const uint8_t kEncoded[] = { 0x30, 0x07, 0x05, 0x00, 0x05, 0x00, 0x02, 0x01, 0x2A };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Sequence);
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
// First NULL: tag = 0x05, value length = 0.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_FALSE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Null);
EXPECT_EQ(reader.GetValueLen(), 0u);
// Second NULL: same tag, same zero length -- reader must not stall on a 0-length value.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Null);
EXPECT_EQ(reader.GetValueLen(), 0u);
// INTEGER 42 follows immediately after the two zero-length NULLs.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Integer);
int64_t intVal = 0;
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 42);
EXPECT_EQ(reader.Next(), ASN1_END);
EXPECT_EQ(reader.ExitConstructedType(), CHIP_NO_ERROR);
}
// Build a deeply nested SEQUENCE-of-SEQUENCE structure (>5 levels), enter all
// the way down, then exit all the way back up, asserting CHIP_NO_ERROR at each
// step. Each ExitContainer passes through the new peek-then-commit unwind.
TEST(TestASN1, ASN1Reader_DeeplyNestedContainer)
{
// Hand-rolled DER: 6 nested SEQUENCEs wrapping INTEGER 0x55.
static const uint8_t kEncoded[] = {
0x30, 0x0D, 0x30, 0x0B, 0x30, 0x09, 0x30, 0x07, 0x30, 0x05, 0x30, 0x03, 0x02, 0x01, 0x55,
};
ASN1Reader reader;
reader.Init(kEncoded, sizeof(kEncoded));
constexpr int kDepth = 6; // matches the 6 nested SEQUENCE wrappers above.
for (int i = 0; i < kDepth; ++i)
{
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR) << "Next() failed at depth " << i;
EXPECT_TRUE(reader.IsConstructed()) << "Element at depth " << i << " should be constructed";
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR) << "EnterConstructedType failed at depth " << i;
}
// Innermost element is the INTEGER.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_FALSE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Integer);
int64_t intVal = 0;
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 0x55);
// Climb back out -- every ExitConstructedType must succeed.
for (int i = 0; i < kDepth; ++i)
{
EXPECT_EQ(reader.Next(), ASN1_END) << "Next() at end of depth " << (kDepth - i);
EXPECT_EQ(reader.ExitConstructedType(), CHIP_NO_ERROR) << "ExitConstructedType failed at depth " << (kDepth - i);
}
}
// When two elements abut and the second begins exactly at container_end, calling
// Next() after consuming the last in-range element must report ASN1_END rather
// than reading past the boundary (exercises the `mElemStart == mContainerEnd`
// early return; confirms the new bounds guard does NOT misclassify a clean end
// of container as INVALID_ENCODING).
TEST(TestASN1, ASN1Reader_AdjacentElementsAtBoundary)
{
// 30 06 SEQUENCE, length 6
// 02 01 01 INTEGER 1
// 02 01 02 INTEGER 2
// 02 01 03 INTEGER 3 <-- starts exactly at the byte after the SEQUENCE
static const uint8_t kEncoded[] = { 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03 };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Sequence);
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
int64_t intVal = 0;
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 1);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 2);
// Next element would start exactly at mContainerEnd -- must be reported as
// ASN1_END (clean end of container), NOT INVALID_ENCODING, and must NOT
// surface the trailing INTEGER 3.
EXPECT_EQ(reader.Next(), ASN1_END);
// After ExitConstructedType we should see the trailing INTEGER 3.
EXPECT_EQ(reader.ExitConstructedType(), CHIP_NO_ERROR);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Integer);
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 3);
EXPECT_EQ(reader.Next(), ASN1_END);
}
// Zero-length elements positioned at container boundaries: a NULL at the very
// FIRST slot of a SEQUENCE, a NULL at the very LAST slot, and an entirely-empty
// SEQUENCE {}. Exercises the boundary positions (mElemStart at the start of the
// container's value bytes; header ending exactly at mContainerEnd; empty
// container) against the bounds guard.
TEST(TestASN1, ASN1Reader_ZeroLengthElementsAtContainerBoundaries)
{
// SEQUENCE { NULL, INTEGER 7, NULL }
static const uint8_t kEncoded[] = {
0x30, 0x07, 0x05, 0x00, 0x02, 0x01, 0x07, 0x05, 0x00,
};
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Sequence);
EXPECT_EQ(reader.GetValueLen(), 7u);
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
// FIRST slot: zero-length NULL at the very start of the container.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_FALSE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Null);
EXPECT_EQ(reader.GetValueLen(), 0u);
// Middle: INTEGER 7.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_FALSE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Integer);
int64_t intVal = 0;
EXPECT_EQ(reader.GetInteger(intVal), CHIP_NO_ERROR);
EXPECT_EQ(intVal, 7);
// LAST slot: zero-length NULL whose 2-byte header ends exactly at the
// SEQUENCE's mContainerEnd.
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Null);
EXPECT_EQ(reader.GetValueLen(), 0u);
EXPECT_EQ(reader.Next(), ASN1_END);
EXPECT_EQ(reader.ExitConstructedType(), CHIP_NO_ERROR);
// Boundary case: SEQUENCE {} with no elements at all.
static const uint8_t kEmptySeq[] = { 0x30, 0x00 };
ASN1Reader emptyReader;
emptyReader.Init(kEmptySeq);
EXPECT_EQ(emptyReader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(emptyReader.IsConstructed());
EXPECT_EQ(emptyReader.GetValueLen(), 0u);
EXPECT_EQ(emptyReader.EnterConstructedType(), CHIP_NO_ERROR);
EXPECT_EQ(emptyReader.Next(), ASN1_END);
EXPECT_EQ(emptyReader.ExitConstructedType(), CHIP_NO_ERROR);
}
// Boundary regression for ExitContainer's "sum > ContainerEnd" check: when the
// saved context's ElemStart + HeadLen + ValueLen equals ContainerEnd EXACTLY,
// ExitContainer must succeed (not return an error). This pins the off-by-one the
// `<=` comparator must not regress to `<`.
TEST(TestASN1, ExitContainer_BoundarySumEqualsContainerEnd_Succeeds)
{
// SEQUENCE { OCTET STRING (3 bytes "abc") }
// 30 05 -- SEQUENCE, length 5
// 04 03 61 62 63 -- OCTET STRING "abc"
static const uint8_t kEncoded[] = { 0x30, 0x05, 0x04, 0x03, 0x61, 0x62, 0x63 };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_OctetString);
EXPECT_EQ(reader.GetValueLen(), 3u);
EXPECT_EQ(reader.Next(), ASN1_END);
// The regression assertion: ExitContainer must NOT error on the exact-boundary path.
EXPECT_EQ(reader.ExitConstructedType(), CHIP_NO_ERROR);
}
// A SEQUENCE whose declared length overshoots the buffer must be rejected.
// DecodeHead's `static_cast<uint32_t>(mBufEnd - p) >= ValueLen` bounds check
// surfaces this through Next() as ASN1_ERROR_VALUE_OVERFLOW before the parser
// ever enters the container. This is the existing master behavior; the test
// documents that the new EnterContainer reordering does not regress it.
TEST(TestASN1, EnterConstructedType_DeclaredLengthExceedsBuffer)
{
// 30 05 02 01 2A -- SEQUENCE length=5, but body is only 3 bytes.
static const uint8_t kEncoded[] = { 0x30, 0x05, 0x02, 0x01, 0x2A };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), ASN1_ERROR_VALUE_OVERFLOW);
}
// Indefinite-length elements are not supported by ASN1Reader::Next() (DER only).
// The second Next() must return ASN1_ERROR_UNSUPPORTED_ENCODING and must NOT
// advance based on the indefinite-length state.
TEST(TestASN1, Next_IndefiniteLengthRejectedOnSecondCall)
{
// 30 80 ... 00 00 -- SEQUENCE, indefinite length (BER, not DER)
static const uint8_t kEncoded[] = { 0x30, 0x80, 0x02, 0x01, 0x2A, 0x00, 0x00 };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_TRUE(reader.IsIndefiniteLen());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_Sequence);
// The IndefiniteLen guard at the top of Next() must fire before the
// overflow/bounds checks, returning UNSUPPORTED_ENCODING.
EXPECT_EQ(reader.Next(), ASN1_ERROR_UNSUPPORTED_ENCODING);
}
// ---------------------------------------------------------------------------
// Proving tests for the ASN1_END -> ASN1_ERROR_INVALID_ENCODING contract change.
//
// These build REAL DER blobs and FAIL on pre-fix master (where the reader bleeds
// past mContainerEnd and returns ASN1_END, which callers map to CHIP_NO_ERROR)
// and PASS post-fix (where Next() returns ASN1_ERROR_INVALID_ENCODING).
// ---------------------------------------------------------------------------
// DER blob where an inner element declares ValueLen larger than the parent
// SEQUENCE's remaining bytes WITHOUT wrapping uint32. Pre-fix Next() returned
// ASN1_END (silently swallowed as clean EOF); post-fix it returns
// ASN1_ERROR_INVALID_ENCODING.
TEST(TestASN1, Next_BoundsExceededReturnsErrorNotEnd)
{
// 30 04 -- outer SEQUENCE, ValueLen = 4
// 04 0A 00 ... (10 bytes) -- inner OCTET STRING, ValueLen = 10 > 2 spare
// DecodeHead accepts the inner head because mBufEnd is far enough out; the
// bounds check `2 + 10 <= 4 - 2` then fails inside Next().
static const uint8_t kEncoded[] = {
0x30, 0x04, // outer SEQUENCE, ValueLen = 4
0x04, 0x0A, // inner OCTET STRING, ValueLen = 10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 fill bytes so DecodeHead's mBufEnd guard succeeds
0x00, 0x00, // 2 more fill bytes
};
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_TRUE(reader.IsConstructed());
EXPECT_EQ(reader.GetValueLen(), 4u);
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
// First Next() decodes the inner OCTET STRING head (HeadLen=2, ValueLen=10).
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_FALSE(reader.IsConstructed());
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_OctetString);
EXPECT_EQ(reader.GetValueLen(), 10u);
// Second Next() now sees mHeadLen=2, ValueLen=10 and must reject the advance:
// 2 + 10 = 12 > (mContainerEnd - mElemStart) = 4 - 2 = 2
// Pre-fix this bled past mContainerEnd and returned ASN1_END (which the
// ASN1_EXIT_* macros map to CHIP_NO_ERROR, silently swallowing corruption);
// post-fix it surfaces as the malformed-encoding error code.
CHIP_ERROR err = reader.Next();
EXPECT_EQ(err, ASN1_ERROR_INVALID_ENCODING) << "Next() must surface inner-overruns-parent as INVALID_ENCODING, not ASN1_END "
<< "(got " << err.Format() << ")";
EXPECT_NE(err, ASN1_END);
EXPECT_NE(err, ASN1_ERROR_LENGTH_OVERFLOW);
}
// DumpASN1-style top-level loop must surface a length-overrun child as a hard
// parse error (INVALID_ENCODING), not silently terminate via the
// ASN1_END -> CHIP_NO_ERROR mapping the ASN1_EXIT_* macros perform.
TEST(TestASN1, DumpLikeLoop_RejectsBoundsOverrun)
{
// Same DER layout as Next_BoundsExceededReturnsErrorNotEnd.
static const uint8_t kEncoded[] = {
0x30, 0x04, // outer SEQUENCE, ValueLen = 4
0x04, 0x0A, // inner OCTET STRING, ValueLen = 10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 fill bytes for DecodeHead's mBufEnd guard
0x00, 0x00, // 2 more fill bytes
};
ASN1Reader reader;
reader.Init(kEncoded);
CHIP_ERROR err = CHIP_NO_ERROR;
int nestLevel = 0;
bool sawOverflow = false;
while (true)
{
err = reader.Next();
if (err != CHIP_NO_ERROR)
{
// Pre-fix: err == ASN1_END here, the loop would break and the
// caller would treat it as clean EOF (corruption swallowed).
// Post-fix: err == ASN1_ERROR_INVALID_ENCODING, a hard error.
sawOverflow = (err == ASN1_ERROR_INVALID_ENCODING);
break;
}
if (reader.IsConstructed())
{
EXPECT_EQ(reader.EnterConstructedType(), CHIP_NO_ERROR);
nestLevel++;
}
}
EXPECT_TRUE(sawOverflow) << "DumpASN1-style loop must surface inner-overruns-parent as INVALID_ENCODING, "
<< "not silently terminate via ASN1_END (got " << err.Format() << ")";
EXPECT_NE(err, ASN1_END);
EXPECT_NE(err, CHIP_NO_ERROR);
EXPECT_EQ(nestLevel, 1) << "Should have descended into the outer SEQUENCE before the inner element tripped the guard";
}
// ---------------------------------------------------------------------------
// Proving tests for the GetBitString() signed-shift undefined-behavior fix.
//
// Pre-fix the inner expression was
// static_cast<uint32_t>(ReverseBits(Value[i]) << shift)
// where the uint8_t result of ReverseBits is integer-promoted to (signed) int
// BEFORE the shift; a value with bit 7 set, shifted left by 24, sets the sign
// bit of an int -- undefined behavior, caught by -fsanitize=shift / UBSan.
//
// The fix moves the cast inside:
// (static_cast<uint32_t>(ReverseBits(Value[i])) << shift)
// so the shift operates on a uint32_t. These tests use real DER BIT STRINGs and
// pin the exact bit pattern across every shift offset (8, 16, 24).
// ---------------------------------------------------------------------------
TEST(TestASN1, GetBitString_HighBitAtMaxShift_NoUndefinedBehavior)
{
// 03 05 00 80 00 00 01 -- BIT STRING, 0 unused bits, 4 data bytes.
// ReverseBits(0x80)=0x01 at shift 0; ReverseBits(0x01)=0x80 at shift 24.
// Expected: 0x80 << 24 | 0x01 == 0x80000001 (the shift-24 high-bit UB case).
static const uint8_t kBitStringHighBit[] = { 0x03, 0x05, 0x00, 0x80, 0x00, 0x00, 0x01 };
ASN1Reader reader;
reader.Init(kBitStringHighBit);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_BitString);
uint32_t bits = 0;
EXPECT_EQ(reader.GetBitString(bits), CHIP_NO_ERROR);
EXPECT_EQ(bits, 0x80000001u);
}
TEST(TestASN1, GetBitString_AllBytesHighBit_ExactPattern)
{
// 03 05 00 01 01 01 01 -- ReverseBits(0x01)=0x80, so each of the 4 data
// bytes contributes 0x80 at shifts 0, 8, 16, 24 => outVal = 0x80808080.
static const uint8_t kEncoded[] = { 0x03, 0x05, 0x00, 0x01, 0x01, 0x01, 0x01 };
ASN1Reader reader;
reader.Init(kEncoded);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_BitString);
uint32_t bits = 0;
EXPECT_EQ(reader.GetBitString(bits), CHIP_NO_ERROR);
EXPECT_EQ(bits, 0x80808080u);
}
// Edge case: ValueLen == 1 means only the "unused bits" byte is present and
// there are zero data bytes. The short-circuit branch must return 0 without
// reading past Value[0].
TEST(TestASN1, GetBitString_ZeroDataBytes_ReturnsZero)
{
// 03 01 00 -- BIT STRING, length 1, 0 unused bits, no data.
static const uint8_t kEmptyBitString[] = { 0x03, 0x01, 0x00 };
ASN1Reader reader;
reader.Init(kEmptyBitString);
EXPECT_EQ(reader.Next(), CHIP_NO_ERROR);
EXPECT_EQ(reader.GetTag(), kASN1UniversalTag_BitString);
EXPECT_EQ(reader.GetValueLen(), 1u);
uint32_t bits = 0xDEADBEEF; // pre-populate to catch "never assigned"
EXPECT_EQ(reader.GetBitString(bits), CHIP_NO_ERROR);
EXPECT_EQ(bits, 0u);
}