/* * * 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 #include #include #include 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_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 = SIZE_MAX; 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