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 87 : DeviceAttestationVendorReservedDeconstructor() {}
42 :
43 : // Read TLV until first profile tag
44 87 : CHIP_ERROR PrepareToReadVendorReservedElements(const ByteSpan & attestationElements, size_t count)
45 : {
46 87 : mIsInitialized = false;
47 87 : mIsDone = false;
48 87 : mNumVendorReservedData = count;
49 87 : mAttestationData = attestationElements;
50 :
51 87 : mTlvReader.Init(mAttestationData);
52 87 : ReturnErrorOnFailure(mTlvReader.Next(containerType, TLV::AnonymousTag()));
53 87 : ReturnErrorOnFailure(mTlvReader.EnterContainer(containerType));
54 :
55 : // position to first ProfileTag
56 : while (true)
57 : {
58 348 : CHIP_ERROR err = mTlvReader.Next();
59 348 : if (err == CHIP_END_OF_TLV)
60 : {
61 86 : mIsDone = true;
62 86 : break;
63 : }
64 :
65 262 : ReturnErrorOnFailure(err);
66 :
67 262 : TLV::Tag tag = mTlvReader.GetTag();
68 262 : if (!TLV::IsContextTag(tag))
69 1 : break;
70 261 : }
71 : // positioned to first non-context tag (vendor reserved data)
72 87 : mIsInitialized = true;
73 87 : mIsAtFirstToken = true;
74 87 : 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 152 : const_iterator Next()
148 : {
149 152 : VerifyOrReturnError(mCurrentIndex < mNumEntriesUsed, nullptr);
150 0 : return &mElements[mCurrentIndex++];
151 : }
152 :
153 152 : const_iterator cbegin()
154 : {
155 : // sort the array in place and return the head element.
156 152 : do_sorting();
157 152 : mCurrentIndex = 0;
158 152 : 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 152 : void do_sorting()
184 : {
185 152 : size_t starting = 0;
186 :
187 152 : 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 152 : }
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
|