blob: d05bd4d9d25a59f0c09b6f9e7094c5e6e7b13e1d [file] [log] [blame]
/*
*
* Copyright (c) 2021 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.
*/
#pragma once
#include <lib/core/CHIPError.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/Span.h>
#include <cstdint>
namespace chip {
namespace Credentials {
struct VendorReservedElement
{
uint16_t vendorId;
uint16_t profileNum;
uint32_t tagNum;
ByteSpan vendorReservedData;
};
// extract elements out of the device attestation bytespan
class DeviceAttestationVendorReservedDeconstructor
{
public:
DeviceAttestationVendorReservedDeconstructor() {}
// Read TLV until first profile tag
CHIP_ERROR PrepareToReadVendorReservedElements(const ByteSpan & attestationElements, size_t count)
{
mIsInitialized = false;
mIsDone = false;
mNumVendorReservedData = count;
mAttestationData = attestationElements;
mTlvReader.Init(mAttestationData);
ReturnErrorOnFailure(mTlvReader.Next(containerType, TLV::AnonymousTag()));
ReturnErrorOnFailure(mTlvReader.EnterContainer(containerType));
// position to first ProfileTag
while (true)
{
CHIP_ERROR err = mTlvReader.Next();
if (err == CHIP_END_OF_TLV)
{
mIsDone = true;
break;
}
ReturnErrorOnFailure(err);
TLV::Tag tag = mTlvReader.GetTag();
if (!TLV::IsContextTag(tag))
break;
}
// positioned to first non-context tag (vendor reserved data)
mIsInitialized = true;
mIsAtFirstToken = true;
return CHIP_NO_ERROR;
}
size_t GetNumberOfElements() const { return mNumVendorReservedData; }
/**
* @brief Return next VendorReserved element. PrepareToReadVendorReservedElements must be called first.
*
* @param[out] element Next vendor Reserved element
*
* @returns CHIP_NO_ERROR on success
* CHIP_ERROR_INCORRECT_STATE if PrepareToReadVendorReservedElements hasn't been called first
* CHIP_ERROR_UNEXPECTED_TLV_ELEMENT if we reach non-profile-specific tags or vendorId is zero
* CHIP_END_OF_TLV if not further entries are present
*/
CHIP_ERROR GetNextVendorReservedElement(struct VendorReservedElement & element)
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_WELL_UNINITIALIZED);
if (mIsDone)
{
return CHIP_END_OF_TLV;
}
if (mIsAtFirstToken)
{
// Already had a Next() done for us by PrepareToReadVendorReservedElements
// so we don't Next() since we should be pointing at a vendor-reserved.
mIsAtFirstToken = false;
}
else
{
CHIP_ERROR error = mTlvReader.Next();
if (error == CHIP_END_OF_TLV)
{
mIsDone = true;
}
ReturnErrorOnFailure(error);
}
TLV::Tag tag = mTlvReader.GetTag();
if (!TLV::IsProfileTag(tag))
{
return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT;
}
// tag is profile tag
element.vendorId = TLV::VendorIdFromTag(tag);
element.profileNum = TLV::ProfileNumFromTag(tag);
element.tagNum = TLV::TagNumFromTag(tag);
ReturnErrorOnFailure(mTlvReader.GetByteView(element.vendorReservedData));
return CHIP_NO_ERROR;
}
private:
size_t mNumVendorReservedData; // number of VendorReserved entries (could be 0)
ByteSpan mAttestationData;
bool mIsInitialized = false;
bool mIsAtFirstToken = false;
bool mIsDone = false;
TLV::ContiguousBufferTLVReader mTlvReader;
TLV::TLVType containerType = TLV::kTLVType_Structure;
};
class DeviceAttestationVendorReservedConstructor
{
public:
DeviceAttestationVendorReservedConstructor(struct VendorReservedElement * array, size_t size) : mElements(array), mMaxSize(size)
{}
typedef const struct VendorReservedElement * const_iterator;
const_iterator Next()
{
VerifyOrReturnError(mCurrentIndex < mNumEntriesUsed, nullptr);
return &mElements[mCurrentIndex++];
}
const_iterator cbegin()
{
// sort the array in place and return the head element.
do_sorting();
mCurrentIndex = 0;
return mElements;
}
CHIP_ERROR addVendorReservedElement(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum, ByteSpan span)
{
if (mNumEntriesUsed == mMaxSize)
return CHIP_ERROR_NO_MEMORY;
mElements[mNumEntriesUsed].tagNum = tagNum;
mElements[mNumEntriesUsed].profileNum = profileNum;
mElements[mNumEntriesUsed].vendorId = vendorId;
mElements[mNumEntriesUsed].vendorReservedData = span;
mNumEntriesUsed++;
return CHIP_NO_ERROR;
}
size_t GetNumberOfElements() const { return mNumEntriesUsed; }
private:
/*
* Sort according to A.2.4 in the spec.
* Mark all sorted entries by setting used flag.
* Order is head to tail, sorted by next
* Executed when entries are about to be read
*/
void do_sorting()
{
size_t starting = 0;
while (starting < mNumEntriesUsed)
{
uint32_t minVendor = UINT32_MAX;
// find lowest vendorId
size_t i;
for (i = starting; i < mNumEntriesUsed; i++)
{
if (mElements[i].vendorId < minVendor)
{
minVendor = mElements[i].vendorId;
}
}
uint32_t minProfile = UINT32_MAX;
// find lowest ProfileNum
for (i = starting; i < mNumEntriesUsed; i++)
{
if (mElements[i].vendorId == minVendor)
{
if (mElements[i].profileNum < minProfile)
minProfile = mElements[i].profileNum;
}
}
// first lowest tagNum for this vendorId/profileNum
uint64_t minTagNum = UINT64_MAX;
size_t lowestIndex;
for (i = starting; i < mNumEntriesUsed; i++)
{
if (mElements[i].vendorId == minVendor && mElements[i].profileNum == minProfile)
{
if (mElements[i].tagNum < minTagNum)
{
minTagNum = mElements[i].tagNum;
lowestIndex = i;
}
}
}
// lowestIndex is the element to move into elements[starting].
if (lowestIndex != starting)
{
//
VendorReservedElement tmpElement;
tmpElement = mElements[starting];
mElements[starting] = mElements[lowestIndex];
mElements[lowestIndex] = tmpElement;
}
starting++;
}
}
VendorReservedElement * mElements;
size_t mMaxSize; // size of elements array
size_t mNumEntriesUsed = 0; // elements used
size_t mCurrentIndex; // iterating from [0...maxSize -1]
};
} // namespace Credentials
} // namespace chip