Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2021 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : #pragma once 18 : 19 : #include <lib/core/CHIPError.h> 20 : #include <lib/core/TLV.h> 21 : #include <lib/support/Span.h> 22 : 23 : #include <cstdint> 24 : 25 : namespace chip { 26 : namespace Credentials { 27 : 28 : struct VendorReservedElement 29 : { 30 : uint16_t vendorId; 31 : uint16_t profileNum; 32 : uint32_t tagNum; 33 : ByteSpan vendorReservedData; 34 : }; 35 : 36 : // extract elements out of the device attestation bytespan 37 : class DeviceAttestationVendorReservedDeconstructor 38 : { 39 : 40 : public: 41 85 : DeviceAttestationVendorReservedDeconstructor() {} 42 : 43 : // Read TLV until first profile tag 44 85 : CHIP_ERROR PrepareToReadVendorReservedElements(const ByteSpan & attestationElements, size_t count) 45 : { 46 85 : mIsInitialized = false; 47 85 : mIsDone = false; 48 85 : mNumVendorReservedData = count; 49 85 : mAttestationData = attestationElements; 50 : 51 85 : mTlvReader.Init(mAttestationData); 52 85 : ReturnErrorOnFailure(mTlvReader.Next(containerType, TLV::AnonymousTag())); 53 85 : ReturnErrorOnFailure(mTlvReader.EnterContainer(containerType)); 54 : 55 : // position to first ProfileTag 56 : while (true) 57 : { 58 340 : CHIP_ERROR err = mTlvReader.Next(); 59 340 : if (err == CHIP_END_OF_TLV) 60 : { 61 84 : mIsDone = true; 62 84 : break; 63 : } 64 : 65 256 : ReturnErrorOnFailure(err); 66 : 67 256 : TLV::Tag tag = mTlvReader.GetTag(); 68 256 : if (!TLV::IsContextTag(tag)) 69 1 : break; 70 255 : } 71 : // positioned to first non-context tag (vendor reserved data) 72 85 : mIsInitialized = true; 73 85 : mIsAtFirstToken = true; 74 85 : return CHIP_NO_ERROR; 75 : } 76 : 77 : size_t GetNumberOfElements() const { return mNumVendorReservedData; } 78 : 79 : /** 80 : * @brief Return next VendorReserved element. PrepareToReadVendorReservedElements must be called first. 81 : * 82 : * @param[out] element Next vendor Reserved element 83 : * 84 : * @returns CHIP_NO_ERROR on success 85 : * CHIP_ERROR_INCORRECT_STATE if PrepareToReadVendorReservedElements hasn't been called first 86 : * CHIP_ERROR_UNEXPECTED_TLV_ELEMENT if we reach non-profile-specific tags or vendorId is zero 87 : * CHIP_END_OF_TLV if not further entries are present 88 : */ 89 : CHIP_ERROR GetNextVendorReservedElement(struct VendorReservedElement & element) 90 : { 91 : VerifyOrReturnError(mIsInitialized, CHIP_ERROR_UNINITIALIZED); 92 : if (mIsDone) 93 : { 94 : return CHIP_END_OF_TLV; 95 : } 96 : 97 : if (mIsAtFirstToken) 98 : { 99 : // Already had a Next() done for us by PrepareToReadVendorReservedElements 100 : // so we don't Next() since we should be pointing at a vendor-reserved. 101 : mIsAtFirstToken = false; 102 : } 103 : else 104 : { 105 : CHIP_ERROR error = mTlvReader.Next(); 106 : if (error == CHIP_END_OF_TLV) 107 : { 108 : mIsDone = true; 109 : } 110 : ReturnErrorOnFailure(error); 111 : } 112 : 113 : TLV::Tag tag = mTlvReader.GetTag(); 114 : if (!TLV::IsProfileTag(tag)) 115 : { 116 : return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; 117 : } 118 : 119 : // tag is profile tag 120 : element.vendorId = TLV::VendorIdFromTag(tag); 121 : element.profileNum = TLV::ProfileNumFromTag(tag); 122 : element.tagNum = TLV::TagNumFromTag(tag); 123 : 124 : ReturnErrorOnFailure(mTlvReader.GetByteView(element.vendorReservedData)); 125 : 126 : return CHIP_NO_ERROR; 127 : } 128 : 129 : private: 130 : size_t mNumVendorReservedData; // number of VendorReserved entries (could be 0) 131 : ByteSpan mAttestationData; 132 : bool mIsInitialized = false; 133 : bool mIsAtFirstToken = false; 134 : bool mIsDone = false; 135 : TLV::ContiguousBufferTLVReader mTlvReader; 136 : TLV::TLVType containerType = TLV::kTLVType_Structure; 137 : }; 138 : 139 : class DeviceAttestationVendorReservedConstructor 140 : { 141 : public: 142 : DeviceAttestationVendorReservedConstructor(struct VendorReservedElement * array, size_t size) : mElements(array), mMaxSize(size) 143 : {} 144 : 145 : typedef const struct VendorReservedElement * const_iterator; 146 : 147 148 : const_iterator Next() 148 : { 149 148 : VerifyOrReturnError(mCurrentIndex < mNumEntriesUsed, nullptr); 150 0 : return &mElements[mCurrentIndex++]; 151 : } 152 : 153 148 : const_iterator cbegin() 154 : { 155 : // sort the array in place and return the head element. 156 148 : do_sorting(); 157 148 : mCurrentIndex = 0; 158 148 : return mElements; 159 : } 160 : 161 : CHIP_ERROR addVendorReservedElement(uint16_t vendorId, uint16_t profileNum, uint32_t tagNum, ByteSpan span) 162 : { 163 : if (mNumEntriesUsed == mMaxSize) 164 : return CHIP_ERROR_NO_MEMORY; 165 : 166 : mElements[mNumEntriesUsed].tagNum = tagNum; 167 : mElements[mNumEntriesUsed].profileNum = profileNum; 168 : mElements[mNumEntriesUsed].vendorId = vendorId; 169 : mElements[mNumEntriesUsed].vendorReservedData = span; 170 : mNumEntriesUsed++; 171 : return CHIP_NO_ERROR; 172 : } 173 : 174 : size_t GetNumberOfElements() const { return mNumEntriesUsed; } 175 : 176 : private: 177 : /* 178 : * Sort according to A.2.4 in the spec. 179 : * Mark all sorted entries by setting used flag. 180 : * Order is head to tail, sorted by next 181 : * Executed when entries are about to be read 182 : */ 183 148 : void do_sorting() 184 : { 185 148 : size_t starting = 0; 186 : 187 148 : while (starting < mNumEntriesUsed) 188 : { 189 0 : uint32_t minVendor = UINT32_MAX; 190 : 191 : // find lowest vendorId 192 : size_t i; 193 0 : for (i = starting; i < mNumEntriesUsed; i++) 194 : { 195 0 : if (mElements[i].vendorId < minVendor) 196 : { 197 0 : minVendor = mElements[i].vendorId; 198 : } 199 : } 200 : 201 0 : uint32_t minProfile = UINT32_MAX; 202 : // find lowest ProfileNum 203 0 : for (i = starting; i < mNumEntriesUsed; i++) 204 : { 205 0 : if (mElements[i].vendorId == minVendor) 206 : { 207 0 : if (mElements[i].profileNum < minProfile) 208 0 : minProfile = mElements[i].profileNum; 209 : } 210 : } 211 : 212 : // first lowest tagNum for this vendorId/profileNum 213 0 : uint64_t minTagNum = UINT64_MAX; 214 0 : size_t lowestIndex = SIZE_MAX; 215 0 : for (i = starting; i < mNumEntriesUsed; i++) 216 : { 217 0 : if (mElements[i].vendorId == minVendor && mElements[i].profileNum == minProfile) 218 : { 219 0 : if (mElements[i].tagNum < minTagNum) 220 : { 221 0 : minTagNum = mElements[i].tagNum; 222 0 : lowestIndex = i; 223 : } 224 : } 225 : } 226 : 227 : // lowestIndex is the element to move into elements[starting]. 228 0 : if (lowestIndex != starting) 229 : { 230 : // 231 0 : VendorReservedElement tmpElement; 232 : 233 0 : tmpElement = mElements[starting]; 234 0 : mElements[starting] = mElements[lowestIndex]; 235 0 : mElements[lowestIndex] = tmpElement; 236 : } 237 0 : starting++; 238 : } 239 148 : } 240 : 241 : VendorReservedElement * mElements; 242 : size_t mMaxSize; // size of elements array 243 : size_t mNumEntriesUsed = 0; // elements used 244 : size_t mCurrentIndex; // iterating from [0...maxSize -1] 245 : }; 246 : 247 : } // namespace Credentials 248 : } // namespace chip