blob: f03fc1b3cdac27a1f3741d0cc8cdd301dc21dc6f [file] [log] [blame]
/*
*
* Copyright (c) 2020-2023 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* 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 parser for the CHIP TLV (Tag-Length-Value) encoding format.
*
*/
#include <stdlib.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/TLV.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
namespace chip {
namespace TLV {
using namespace chip::Encoding;
static const uint8_t sTagSizes[] = { 0, 1, 2, 4, 2, 4, 6, 8 };
void TLVReader::Init(const uint8_t * data, size_t dataLen)
{
// TODO: Maybe we can just make mMaxLen and mLenRead size_t instead?
uint32_t actualDataLen = dataLen > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(dataLen);
mBackingStore = nullptr;
mReadPoint = data;
mBufEnd = data + actualDataLen;
mLenRead = 0;
mMaxLen = actualDataLen;
ClearElementState();
mContainerType = kTLVType_NotSpecified;
SetContainerOpen(false);
ImplicitProfileId = kProfileIdNotSpecified;
}
CHIP_ERROR TLVReader::Init(TLVBackingStore & backingStore, uint32_t maxLen)
{
mBackingStore = &backingStore;
mReadPoint = nullptr;
uint32_t bufLen = 0;
CHIP_ERROR err = mBackingStore->OnInit(*this, mReadPoint, bufLen);
if (err != CHIP_NO_ERROR)
return err;
mBufEnd = mReadPoint + bufLen;
mLenRead = 0;
mMaxLen = maxLen;
ClearElementState();
mContainerType = kTLVType_NotSpecified;
SetContainerOpen(false);
ImplicitProfileId = kProfileIdNotSpecified;
AppData = nullptr;
return CHIP_NO_ERROR;
}
void TLVReader::Init(const TLVReader & aReader)
{
// Initialize private data members
mElemTag = aReader.mElemTag;
mElemLenOrVal = aReader.mElemLenOrVal;
mBackingStore = aReader.mBackingStore;
mReadPoint = aReader.mReadPoint;
mBufEnd = aReader.mBufEnd;
mLenRead = aReader.mLenRead;
mMaxLen = aReader.mMaxLen;
mControlByte = aReader.mControlByte;
mContainerType = aReader.mContainerType;
SetContainerOpen(aReader.IsContainerOpen());
// Initialize public data members
ImplicitProfileId = aReader.ImplicitProfileId;
AppData = aReader.AppData;
}
TLVType TLVReader::GetType() const
{
TLVElementType elemType = ElementType();
if (elemType == TLVElementType::EndOfContainer)
return kTLVType_NotSpecified;
if (elemType == TLVElementType::FloatingPointNumber32 || elemType == TLVElementType::FloatingPointNumber64)
return kTLVType_FloatingPointNumber;
if (elemType == TLVElementType::NotSpecified || elemType >= TLVElementType::Null)
return static_cast<TLVType>(elemType);
return static_cast<TLVType>(static_cast<uint8_t>(elemType) & ~kTLVTypeSizeMask);
}
uint32_t TLVReader::GetLength() const
{
if (TLVTypeHasLength(ElementType()))
return static_cast<uint32_t>(mElemLenOrVal);
return 0;
}
CHIP_ERROR TLVReader::Get(bool & v)
{
TLVElementType elemType = ElementType();
if (elemType == TLVElementType::BooleanFalse)
v = false;
else if (elemType == TLVElementType::BooleanTrue)
v = true;
else
return CHIP_ERROR_WRONG_TLV_TYPE;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Get(int8_t & v)
{
int64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<int8_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<int8_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(int16_t & v)
{
int64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<int16_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<int16_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(int32_t & v)
{
int64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<int32_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<int32_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(int64_t & v)
{
// Internal callers of this method depend on it not modifying "v" on failure.
switch (ElementType())
{
case TLVElementType::Int8:
v = CastToSigned(static_cast<uint8_t>(mElemLenOrVal));
break;
case TLVElementType::Int16:
v = CastToSigned(static_cast<uint16_t>(mElemLenOrVal));
break;
case TLVElementType::Int32:
v = CastToSigned(static_cast<uint32_t>(mElemLenOrVal));
break;
case TLVElementType::Int64:
v = CastToSigned(mElemLenOrVal);
break;
default:
return CHIP_ERROR_WRONG_TLV_TYPE;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Get(uint8_t & v)
{
uint64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<uint8_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<uint8_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(uint16_t & v)
{
uint64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<uint16_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<uint16_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(uint32_t & v)
{
uint64_t v64 = 0;
CHIP_ERROR err = Get(v64);
if (!CanCastTo<uint32_t>(v64))
{
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
v = static_cast<uint32_t>(v64);
return err;
}
CHIP_ERROR TLVReader::Get(uint64_t & v)
{
// Internal callers of this method depend on it not modifying "v" on failure.
switch (ElementType())
{
case TLVElementType::UInt8:
case TLVElementType::UInt16:
case TLVElementType::UInt32:
case TLVElementType::UInt64:
v = mElemLenOrVal;
break;
default:
return CHIP_ERROR_WRONG_TLV_TYPE;
}
return CHIP_NO_ERROR;
}
namespace {
float BitCastToFloat(const uint64_t elemLenOrVal)
{
float f;
auto unsigned32 = static_cast<uint32_t>(elemLenOrVal);
memcpy(&f, &unsigned32, sizeof(f));
return f;
}
} // namespace
// Note: Unlike the integer Get functions, this code avoids doing conversions
// between float and double wherever possible, because these conversions are
// relatively expensive on platforms that use soft-float instruction sets.
CHIP_ERROR TLVReader::Get(float & v)
{
switch (ElementType())
{
case TLVElementType::FloatingPointNumber32: {
v = BitCastToFloat(mElemLenOrVal);
break;
}
default:
return CHIP_ERROR_WRONG_TLV_TYPE;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Get(double & v)
{
switch (ElementType())
{
case TLVElementType::FloatingPointNumber32: {
v = BitCastToFloat(mElemLenOrVal);
break;
}
case TLVElementType::FloatingPointNumber64: {
double d;
memcpy(&d, &mElemLenOrVal, sizeof(d));
v = d;
break;
}
default:
return CHIP_ERROR_WRONG_TLV_TYPE;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Get(ByteSpan & v)
{
const uint8_t * val;
ReturnErrorOnFailure(GetDataPtr(val));
v = ByteSpan(val, GetLength());
return CHIP_NO_ERROR;
}
namespace {
constexpr int kUnicodeInformationSeparator1 = 0x1F;
constexpr size_t kMaxLocalizedStringIdentifierLen = 2 * sizeof(LocalizedStringIdentifier);
} // namespace
CHIP_ERROR TLVReader::Get(CharSpan & v)
{
if (!TLVTypeIsUTF8String(ElementType()))
{
return CHIP_ERROR_WRONG_TLV_TYPE;
}
const uint8_t * bytes;
ReturnErrorOnFailure(GetDataPtr(bytes)); // Does length sanity checks
if (bytes == nullptr)
{
// Calling memchr further down with bytes == nullptr would have undefined behaviour, exiting early.
return CHIP_NO_ERROR;
}
uint32_t len = GetLength();
// If Unicode Information Separator 1 (0x1f) is present in the string then method returns
// string ending at first appearance of the Information Separator 1.
const uint8_t * infoSeparator = reinterpret_cast<const uint8_t *>(memchr(bytes, kUnicodeInformationSeparator1, len));
if (infoSeparator != nullptr)
{
len = static_cast<uint32_t>(infoSeparator - bytes);
}
v = CharSpan(Uint8::to_const_char(bytes), len);
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Get(Optional<LocalizedStringIdentifier> & lsid)
{
lsid.ClearValue();
VerifyOrReturnError(TLVTypeIsUTF8String(ElementType()), CHIP_ERROR_WRONG_TLV_TYPE);
const uint8_t * bytes;
ReturnErrorOnFailure(GetDataPtr(bytes)); // Does length sanity checks
if (bytes == nullptr)
{
// Calling memchr further down with bytes == nullptr would have undefined behaviour, exiting early.
return CHIP_NO_ERROR;
}
uint32_t len = GetLength();
const uint8_t * infoSeparator1 = static_cast<const uint8_t *>(memchr(bytes, kUnicodeInformationSeparator1, len));
if (infoSeparator1 == nullptr)
{
return CHIP_NO_ERROR;
}
const uint8_t * lsidPtr = infoSeparator1 + 1;
len -= static_cast<uint32_t>(lsidPtr - bytes);
const uint8_t * infoSeparator2 = static_cast<const uint8_t *>(memchr(lsidPtr, kUnicodeInformationSeparator1, len));
if (infoSeparator2 != nullptr)
{
len = static_cast<uint32_t>(infoSeparator2 - lsidPtr);
}
if (len == 0)
{
return CHIP_NO_ERROR;
}
VerifyOrReturnError(len <= kMaxLocalizedStringIdentifierLen, CHIP_ERROR_INVALID_TLV_ELEMENT);
// Leading zeroes are not allowed.
VerifyOrReturnError(static_cast<char>(lsidPtr[0]) != '0', CHIP_ERROR_INVALID_TLV_ELEMENT);
char idStr[kMaxLocalizedStringIdentifierLen] = { '0', '0', '0', '0' };
memcpy(&idStr[kMaxLocalizedStringIdentifierLen - len], lsidPtr, len);
LocalizedStringIdentifier id;
VerifyOrReturnError(Encoding::UppercaseHexToUint16(idStr, sizeof(idStr), id) == sizeof(LocalizedStringIdentifier),
CHIP_ERROR_INVALID_TLV_ELEMENT);
lsid.SetValue(id);
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::GetBytes(uint8_t * buf, size_t bufSize)
{
if (!TLVTypeIsString(ElementType()))
return CHIP_ERROR_WRONG_TLV_TYPE;
if (mElemLenOrVal > bufSize)
return CHIP_ERROR_BUFFER_TOO_SMALL;
CHIP_ERROR err = ReadData(buf, static_cast<uint32_t>(mElemLenOrVal));
if (err != CHIP_NO_ERROR)
return err;
mElemLenOrVal = 0;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::GetString(char * buf, size_t bufSize)
{
if (!TLVTypeIsString(ElementType()))
return CHIP_ERROR_WRONG_TLV_TYPE;
if ((mElemLenOrVal + 1) > bufSize)
return CHIP_ERROR_BUFFER_TOO_SMALL;
buf[mElemLenOrVal] = 0;
return GetBytes(reinterpret_cast<uint8_t *>(buf), bufSize - 1);
}
CHIP_ERROR TLVReader::DupBytes(uint8_t *& buf, uint32_t & dataLen)
{
if (!TLVTypeIsString(ElementType()))
return CHIP_ERROR_WRONG_TLV_TYPE;
buf = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(static_cast<uint32_t>(mElemLenOrVal)));
if (buf == nullptr)
return CHIP_ERROR_NO_MEMORY;
CHIP_ERROR err = ReadData(buf, static_cast<uint32_t>(mElemLenOrVal));
if (err != CHIP_NO_ERROR)
{
chip::Platform::MemoryFree(buf);
buf = nullptr;
return err;
}
dataLen = static_cast<uint32_t>(mElemLenOrVal);
mElemLenOrVal = 0;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::DupString(char *& buf)
{
if (!TLVTypeIsString(ElementType()))
return CHIP_ERROR_WRONG_TLV_TYPE;
if (mElemLenOrVal > UINT32_MAX - 1)
return CHIP_ERROR_NO_MEMORY;
buf = static_cast<char *>(chip::Platform::MemoryAlloc(static_cast<uint32_t>(mElemLenOrVal + 1)));
if (buf == nullptr)
return CHIP_ERROR_NO_MEMORY;
CHIP_ERROR err = ReadData(reinterpret_cast<uint8_t *>(buf), static_cast<uint32_t>(mElemLenOrVal));
if (err != CHIP_NO_ERROR)
{
chip::Platform::MemoryFree(buf);
buf = nullptr;
return err;
}
buf[mElemLenOrVal] = 0;
mElemLenOrVal = 0;
return err;
}
CHIP_ERROR TLVReader::GetDataPtr(const uint8_t *& data)
{
CHIP_ERROR err;
if (!TLVTypeIsString(ElementType()))
return CHIP_ERROR_WRONG_TLV_TYPE;
if (GetLength() == 0)
{
data = nullptr;
return CHIP_NO_ERROR;
}
err = EnsureData(CHIP_ERROR_TLV_UNDERRUN);
if (err != CHIP_NO_ERROR)
return err;
uint32_t remainingLen = static_cast<decltype(mMaxLen)>(mBufEnd - mReadPoint);
// Verify that the entirety of the data is available in the buffer.
// Note that this may not be possible if the reader is reading from a chain of buffers.
if (remainingLen < static_cast<uint32_t>(mElemLenOrVal))
return CHIP_ERROR_TLV_UNDERRUN;
data = mReadPoint;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::OpenContainer(TLVReader & containerReader)
{
TLVElementType elemType = ElementType();
if (!TLVTypeIsContainer(elemType))
return CHIP_ERROR_INCORRECT_STATE;
containerReader.mBackingStore = mBackingStore;
containerReader.mReadPoint = mReadPoint;
containerReader.mBufEnd = mBufEnd;
containerReader.mLenRead = mLenRead;
containerReader.mMaxLen = mMaxLen;
containerReader.ClearElementState();
containerReader.mContainerType = static_cast<TLVType>(elemType);
containerReader.SetContainerOpen(false);
containerReader.ImplicitProfileId = ImplicitProfileId;
containerReader.AppData = AppData;
SetContainerOpen(true);
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::CloseContainer(TLVReader & containerReader)
{
CHIP_ERROR err;
if (!IsContainerOpen())
return CHIP_ERROR_INCORRECT_STATE;
if (static_cast<TLVElementType>(containerReader.mContainerType) != ElementType())
return CHIP_ERROR_INCORRECT_STATE;
err = containerReader.SkipToEndOfContainer();
if (err != CHIP_NO_ERROR)
return err;
mBackingStore = containerReader.mBackingStore;
mReadPoint = containerReader.mReadPoint;
mBufEnd = containerReader.mBufEnd;
mLenRead = containerReader.mLenRead;
mMaxLen = containerReader.mMaxLen;
ClearElementState();
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::EnterContainer(TLVType & outerContainerType)
{
TLVElementType elemType = ElementType();
if (!TLVTypeIsContainer(elemType))
return CHIP_ERROR_INCORRECT_STATE;
outerContainerType = mContainerType;
mContainerType = static_cast<TLVType>(elemType);
ClearElementState();
SetContainerOpen(false);
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::ExitContainer(TLVType outerContainerType)
{
CHIP_ERROR err;
err = SkipToEndOfContainer();
if (err != CHIP_NO_ERROR)
return err;
mContainerType = outerContainerType;
ClearElementState();
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::VerifyEndOfContainer()
{
CHIP_ERROR err = Next();
if (err == CHIP_END_OF_TLV)
return CHIP_NO_ERROR;
if (err == CHIP_NO_ERROR)
return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT;
return err;
}
CHIP_ERROR TLVReader::Next()
{
CHIP_ERROR err;
TLVElementType elemType = ElementType();
err = Skip();
if (err != CHIP_NO_ERROR)
return err;
err = ReadElement();
if (err != CHIP_NO_ERROR)
return err;
elemType = ElementType();
if (elemType == TLVElementType::EndOfContainer)
return CHIP_END_OF_TLV;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Next(Tag expectedTag)
{
CHIP_ERROR err = Next();
if (err != CHIP_NO_ERROR)
return err;
if (mElemTag != expectedTag)
return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Next(TLVType expectedType, Tag expectedTag)
{
CHIP_ERROR err = Next(expectedTag);
if (err != CHIP_NO_ERROR)
return err;
if (GetType() != expectedType)
return CHIP_ERROR_WRONG_TLV_TYPE;
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::Skip()
{
CHIP_ERROR err;
TLVElementType elemType = ElementType();
if (elemType == TLVElementType::EndOfContainer)
return CHIP_END_OF_TLV;
if (TLVTypeIsContainer(elemType))
{
TLVType outerContainerType;
err = EnterContainer(outerContainerType);
if (err != CHIP_NO_ERROR)
return err;
err = ExitContainer(outerContainerType);
if (err != CHIP_NO_ERROR)
return err;
}
else
{
err = SkipData();
if (err != CHIP_NO_ERROR)
return err;
ClearElementState();
}
return CHIP_NO_ERROR;
}
/**
* Clear the state of the TLVReader.
* This method is used to position the reader before the first TLV,
* between TLVs or after the last TLV.
*/
void TLVReader::ClearElementState()
{
mElemTag = AnonymousTag();
mControlByte = kTLVControlByte_NotSpecified;
mElemLenOrVal = 0;
}
/**
* Skip any data contained in the current TLV by reading over it without
* a destination buffer.
*
* @retval #CHIP_NO_ERROR If the reader was successfully positioned at the end of the
* data.
* @retval other Other CHIP or platform error codes returned by the configured
* TLVBackingStore.
*/
CHIP_ERROR TLVReader::SkipData()
{
CHIP_ERROR err = CHIP_NO_ERROR;
TLVElementType elemType = ElementType();
if (TLVTypeHasLength(elemType))
{
err = ReadData(nullptr, static_cast<uint32_t>(mElemLenOrVal));
if (err != CHIP_NO_ERROR)
return err;
}
return err;
}
CHIP_ERROR TLVReader::SkipToEndOfContainer()
{
CHIP_ERROR err;
TLVType outerContainerType = mContainerType;
uint32_t nestLevel = 0;
// If the user calls Next() after having called OpenContainer() but before calling
// CloseContainer() they're effectively doing a close container by skipping over
// the container element. So reset the 'container open' flag here to prevent them
// from calling CloseContainer() with the now orphaned container reader.
SetContainerOpen(false);
while (true)
{
TLVElementType elemType = ElementType();
if (elemType == TLVElementType::EndOfContainer)
{
if (nestLevel == 0)
return CHIP_NO_ERROR;
nestLevel--;
mContainerType = (nestLevel == 0) ? outerContainerType : kTLVType_UnknownContainer;
}
else if (TLVTypeIsContainer(elemType))
{
nestLevel++;
mContainerType = static_cast<TLVType>(elemType);
}
err = SkipData();
if (err != CHIP_NO_ERROR)
return err;
err = ReadElement();
if (err != CHIP_NO_ERROR)
return err;
}
}
CHIP_ERROR TLVReader::ReadElement()
{
CHIP_ERROR err;
uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes
const uint8_t * p;
TLVElementType elemType;
// Make sure we have input data. Return CHIP_END_OF_TLV if no more data is available.
err = EnsureData(CHIP_END_OF_TLV);
if (err != CHIP_NO_ERROR)
return err;
if (mReadPoint == nullptr)
{
return CHIP_ERROR_INVALID_TLV_ELEMENT;
}
// Get the element's control byte.
mControlByte = *mReadPoint;
// Extract the element type from the control byte. Fail if it's invalid.
elemType = ElementType();
if (!IsValidTLVType(elemType))
return CHIP_ERROR_INVALID_TLV_ELEMENT;
// Extract the tag control from the control byte.
TLVTagControl tagControl = static_cast<TLVTagControl>(mControlByte & kTLVTagControlMask);
// Determine the number of bytes in the element's tag, if any.
uint8_t tagBytes = sTagSizes[tagControl >> kTLVTagControlShift];
// Extract the size of length/value field from the control byte.
TLVFieldSize lenOrValFieldSize = GetTLVFieldSize(elemType);
// Determine the number of bytes in the length/value field.
uint8_t valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize);
// Determine the number of bytes in the element's 'head'. This includes: the control byte, the tag bytes (if present), the
// length bytes (if present), and for elements that don't have a length (e.g. integers), the value bytes.
uint8_t elemHeadBytes = static_cast<uint8_t>(1 + tagBytes + valOrLenBytes);
// If the head of the element overlaps the end of the input buffer, read the bytes into the staging buffer
// and arrange to parse them from there. Otherwise read them directly from the input buffer.
if (elemHeadBytes > (mBufEnd - mReadPoint))
{
err = ReadData(stagingBuf, elemHeadBytes);
if (err != CHIP_NO_ERROR)
return err;
p = stagingBuf;
}
else
{
p = mReadPoint;
mReadPoint += elemHeadBytes;
mLenRead += elemHeadBytes;
}
// Skip over the control byte.
p++;
// Read the tag field, if present.
mElemTag = ReadTag(tagControl, p);
// Read the length/value field, if present.
switch (lenOrValFieldSize)
{
case kTLVFieldSize_0Byte:
mElemLenOrVal = 0;
break;
case kTLVFieldSize_1Byte:
mElemLenOrVal = Read8(p);
break;
case kTLVFieldSize_2Byte:
mElemLenOrVal = LittleEndian::Read16(p);
break;
case kTLVFieldSize_4Byte:
mElemLenOrVal = LittleEndian::Read32(p);
break;
case kTLVFieldSize_8Byte:
mElemLenOrVal = LittleEndian::Read64(p);
VerifyOrReturnError(!TLVTypeHasLength(elemType) || (mElemLenOrVal <= UINT32_MAX), CHIP_ERROR_NOT_IMPLEMENTED);
break;
}
return VerifyElement();
}
CHIP_ERROR TLVReader::VerifyElement()
{
if (ElementType() == TLVElementType::EndOfContainer)
{
if (mContainerType == kTLVType_NotSpecified)
return CHIP_ERROR_INVALID_TLV_ELEMENT;
if (mElemTag != AnonymousTag())
return CHIP_ERROR_INVALID_TLV_TAG;
}
else
{
if (mElemTag == UnknownImplicitTag())
return CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG;
switch (mContainerType)
{
case kTLVType_NotSpecified:
if (IsContextTag(mElemTag))
return CHIP_ERROR_INVALID_TLV_TAG;
break;
case kTLVType_Structure:
if (mElemTag == AnonymousTag())
return CHIP_ERROR_INVALID_TLV_TAG;
break;
case kTLVType_Array:
if (mElemTag != AnonymousTag())
return CHIP_ERROR_INVALID_TLV_TAG;
break;
case kTLVType_UnknownContainer:
case kTLVType_List:
break;
default:
return CHIP_ERROR_INCORRECT_STATE;
}
}
// If the current element encodes a specific length (e.g. a UTF8 string or a byte string), verify
// that the purported length fits within the remaining bytes of the encoding (as delineated by mMaxLen).
//
// Note that this check is not strictly necessary to prevent runtime errors, as any attempt to access
// the data of an element with an invalid length will result in an error. However checking the length
// here catches the error earlier, and ensures that the application will never see the erroneous length
// value.
//
if (TLVTypeHasLength(ElementType()))
{
uint32_t overallLenRemaining = mMaxLen - mLenRead;
if (overallLenRemaining < static_cast<uint32_t>(mElemLenOrVal))
return CHIP_ERROR_TLV_UNDERRUN;
}
return CHIP_NO_ERROR;
}
Tag TLVReader::ReadTag(TLVTagControl tagControl, const uint8_t *& p) const
{
uint16_t vendorId;
uint16_t profileNum;
switch (tagControl)
{
case TLVTagControl::ContextSpecific:
return ContextTag(Read8(p));
case TLVTagControl::CommonProfile_2Bytes:
return CommonTag(LittleEndian::Read16(p));
case TLVTagControl::CommonProfile_4Bytes:
return CommonTag(LittleEndian::Read32(p));
case TLVTagControl::ImplicitProfile_2Bytes:
if (ImplicitProfileId == kProfileIdNotSpecified)
return UnknownImplicitTag();
return ProfileTag(ImplicitProfileId, LittleEndian::Read16(p));
case TLVTagControl::ImplicitProfile_4Bytes:
if (ImplicitProfileId == kProfileIdNotSpecified)
return UnknownImplicitTag();
return ProfileTag(ImplicitProfileId, LittleEndian::Read32(p));
case TLVTagControl::FullyQualified_6Bytes:
vendorId = LittleEndian::Read16(p);
profileNum = LittleEndian::Read16(p);
return ProfileTag(vendorId, profileNum, LittleEndian::Read16(p));
case TLVTagControl::FullyQualified_8Bytes:
vendorId = LittleEndian::Read16(p);
profileNum = LittleEndian::Read16(p);
return ProfileTag(vendorId, profileNum, LittleEndian::Read32(p));
case TLVTagControl::Anonymous:
default:
return AnonymousTag();
}
}
CHIP_ERROR TLVReader::ReadData(uint8_t * buf, uint32_t len)
{
CHIP_ERROR err;
while (len > 0)
{
err = EnsureData(CHIP_ERROR_TLV_UNDERRUN);
if (err != CHIP_NO_ERROR)
return err;
uint32_t remainingLen = static_cast<decltype(mMaxLen)>(mBufEnd - mReadPoint);
uint32_t readLen = len;
if (readLen > remainingLen)
readLen = remainingLen;
if (buf != nullptr)
{
memcpy(buf, mReadPoint, readLen);
buf += readLen;
}
mReadPoint += readLen;
mLenRead += readLen;
len -= readLen;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR TLVReader::EnsureData(CHIP_ERROR noDataErr)
{
CHIP_ERROR err;
if (mReadPoint == mBufEnd)
{
if (mLenRead == mMaxLen)
return noDataErr;
if (mBackingStore == nullptr)
return noDataErr;
uint32_t bufLen;
err = mBackingStore->GetNextBuffer(*this, mReadPoint, bufLen);
if (err != CHIP_NO_ERROR)
return err;
if (bufLen == 0)
return noDataErr;
// Cap mBufEnd so that we don't read beyond the user's specified maximum length, even
// if the underlying buffer is larger.
uint32_t overallLenRemaining = mMaxLen - mLenRead;
if (overallLenRemaining < bufLen)
bufLen = overallLenRemaining;
mBufEnd = mReadPoint + bufLen;
}
return CHIP_NO_ERROR;
}
/**
* This is a private method used to compute the length of a TLV element head.
*/
CHIP_ERROR TLVReader::GetElementHeadLength(uint8_t & elemHeadBytes) const
{
uint8_t tagBytes;
uint8_t valOrLenBytes;
TLVTagControl tagControl;
TLVFieldSize lenOrValFieldSize;
TLVElementType elemType = ElementType();
// Verify element is of valid TLVType.
VerifyOrReturnError(IsValidTLVType(elemType), CHIP_ERROR_INVALID_TLV_ELEMENT);
// Extract the tag control from the control byte.
tagControl = static_cast<TLVTagControl>(mControlByte & kTLVTagControlMask);
// Determine the number of bytes in the element's tag, if any.
tagBytes = sTagSizes[tagControl >> kTLVTagControlShift];
// Extract the size of length/value field from the control byte.
lenOrValFieldSize = GetTLVFieldSize(elemType);
// Determine the number of bytes in the length/value field.
valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize);
// Determine the number of bytes in the element's 'head'. This includes: the
// control byte, the tag bytes (if present), the length bytes (if present),
// and for elements that don't have a length (e.g. integers), the value
// bytes.
VerifyOrReturnError(CanCastTo<uint8_t>(1 + tagBytes + valOrLenBytes), CHIP_ERROR_INTERNAL);
elemHeadBytes = static_cast<uint8_t>(1 + tagBytes + valOrLenBytes);
return CHIP_NO_ERROR;
}
/**
* This is a private method that returns the TLVElementType from mControlByte
*/
TLVElementType TLVReader::ElementType() const
{
if (mControlByte == static_cast<uint16_t>(kTLVControlByte_NotSpecified))
return TLVElementType::NotSpecified;
return static_cast<TLVElementType>(mControlByte & kTLVTypeMask);
}
CHIP_ERROR TLVReader::FindElementWithTag(Tag tag, TLVReader & destReader) const
{
CHIP_ERROR err = CHIP_NO_ERROR;
chip::TLV::TLVReader reader;
reader.Init(*this);
while (CHIP_NO_ERROR == (err = reader.Next()))
{
VerifyOrExit(chip::TLV::kTLVType_NotSpecified != reader.GetType(), err = CHIP_ERROR_INVALID_TLV_ELEMENT);
if (tag == reader.GetTag())
{
destReader.Init(reader);
break;
}
}
exit:
ChipLogIfFalse((CHIP_NO_ERROR == err) || (CHIP_END_OF_TLV == err));
return err;
}
CHIP_ERROR TLVReader::CountRemainingInContainer(size_t * size) const
{
if (mContainerType == kTLVType_NotSpecified)
{
return CHIP_ERROR_INCORRECT_STATE;
}
TLVReader tempReader(*this);
size_t count = 0;
CHIP_ERROR err;
while ((err = tempReader.Next()) == CHIP_NO_ERROR)
{
++count;
};
if (err == CHIP_END_OF_TLV)
{
*size = count;
return CHIP_NO_ERROR;
}
return err;
}
CHIP_ERROR ContiguousBufferTLVReader::OpenContainer(ContiguousBufferTLVReader & containerReader)
{
// We are going to initialize containerReader by calling our superclass
// OpenContainer method. The superclass only knows how to initialize
// members the superclass knows about, so we assert that we don't have any
// extra members that need initializing. If such members ever get added,
// they would need to be initialized in this method.
static_assert(sizeof(ContiguousBufferTLVReader) == sizeof(TLVReader), "We have state the superclass is not initializing?");
return TLVReader::OpenContainer(containerReader);
}
CHIP_ERROR ContiguousBufferTLVReader::GetStringView(Span<const char> & data)
{
return Get(data);
}
CHIP_ERROR ContiguousBufferTLVReader::GetByteView(ByteSpan & data)
{
if (!TLVTypeIsByteString(ElementType()))
{
return CHIP_ERROR_WRONG_TLV_TYPE;
}
return Get(data);
}
} // namespace TLV
} // namespace chip