Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file implements data types, objects and APIs for
22 : * working with Certification Declaration elements.
23 : */
24 :
25 : #include <algorithm>
26 : #include <cinttypes>
27 : #include <cstddef>
28 :
29 : #include <credentials/CertificationDeclaration.h>
30 : #include <crypto/CHIPCryptoPAL.h>
31 : #include <lib/core/CHIPCore.h>
32 : #include <lib/core/CHIPSafeCasts.h>
33 : #include <lib/core/TLV.h>
34 : #include <lib/support/SafeInt.h>
35 :
36 : namespace chip {
37 : namespace Credentials {
38 :
39 : using namespace chip::ASN1;
40 : using namespace chip::TLV;
41 : using namespace chip::Crypto;
42 :
43 : static constexpr uint8_t sOID_ContentType_PKCS7Data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
44 : static constexpr uint8_t sOID_ContentType_PKCS7SignedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
45 : static constexpr uint8_t sOID_DigestAlgo_SHA256[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };
46 : static constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 };
47 :
48 : /** Certification Declaration Element TLV Tags
49 : */
50 : enum
51 : {
52 : kTag_FormatVersion = 0, /**< [ unsigned int ] Format version. */
53 : kTag_VendorId = 1, /**< [ unsigned int ] Vedor identifier. */
54 : kTag_ProductIdArray = 2, /**< [ array ] Product identifiers (each is unsigned int). */
55 : kTag_DeviceTypeId = 3, /**< [ unsigned int ] Device Type identifier. */
56 : kTag_CertificateId = 4, /**< [ UTF-8 string, length 19 ] Certificate identifier. */
57 : kTag_SecurityLevel = 5, /**< [ unsigned int ] Security level. */
58 : kTag_SecurityInformation = 6, /**< [ unsigned int ] Security information. */
59 : kTag_VersionNumber = 7, /**< [ unsigned int ] Version number. */
60 : kTag_CertificationType = 8, /**< [ unsigned int ] Certification Type. */
61 : kTag_DACOriginVendorId = 9, /**< [ unsigned int, optional ] DAC origin vendor identifier. */
62 : kTag_DACOriginProductId = 10, /**< [ unsigned int, optional ] DAC origin product identifier. */
63 : kTag_AuthorizedPAAList = 11, /**< [ array, optional ] Authorized PAA List. */
64 : };
65 :
66 7 : CHIP_ERROR EncodeCertificationElements(const CertificationElements & certElements, MutableByteSpan & encodedCertElements)
67 : {
68 7 : TLVWriter writer;
69 : TLVType outerContainer1, outerContainer2;
70 :
71 7 : writer.Init(encodedCertElements);
72 :
73 7 : ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainer1));
74 :
75 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_FormatVersion), certElements.FormatVersion));
76 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VendorId), certElements.VendorId));
77 :
78 7 : VerifyOrReturnError(certElements.ProductIdsCount > 0, CHIP_ERROR_INVALID_ARGUMENT);
79 7 : VerifyOrReturnError(certElements.ProductIdsCount <= kMaxProductIdsCount, CHIP_ERROR_INVALID_ARGUMENT);
80 :
81 7 : ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ProductIdArray), kTLVType_Array, outerContainer2));
82 115 : for (uint8_t i = 0; i < certElements.ProductIdsCount; i++)
83 : {
84 108 : ReturnErrorOnFailure(writer.Put(AnonymousTag(), certElements.ProductIds[i]));
85 : }
86 7 : ReturnErrorOnFailure(writer.EndContainer(outerContainer2));
87 :
88 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DeviceTypeId), certElements.DeviceTypeId));
89 7 : ReturnErrorOnFailure(writer.PutString(ContextTag(kTag_CertificateId), certElements.CertificateId));
90 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityLevel), certElements.SecurityLevel));
91 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityInformation), certElements.SecurityInformation));
92 7 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VersionNumber), certElements.VersionNumber));
93 6 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_CertificationType), certElements.CertificationType));
94 6 : if (certElements.DACOriginVIDandPIDPresent)
95 : {
96 2 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginVendorId), certElements.DACOriginVendorId));
97 2 : ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginProductId), certElements.DACOriginProductId));
98 : }
99 6 : if (certElements.AuthorizedPAAListCount > 0)
100 : {
101 1 : VerifyOrReturnError(certElements.AuthorizedPAAListCount <= kMaxAuthorizedPAAListCount, CHIP_ERROR_INVALID_ARGUMENT);
102 :
103 1 : ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_AuthorizedPAAList), kTLVType_Array, outerContainer2));
104 11 : for (uint8_t i = 0; i < certElements.AuthorizedPAAListCount; i++)
105 : {
106 10 : ReturnErrorOnFailure(writer.Put(AnonymousTag(), ByteSpan(certElements.AuthorizedPAAList[i])));
107 : }
108 1 : ReturnErrorOnFailure(writer.EndContainer(outerContainer2));
109 : }
110 :
111 6 : ReturnErrorOnFailure(writer.EndContainer(outerContainer1));
112 :
113 6 : ReturnErrorOnFailure(writer.Finalize());
114 :
115 6 : encodedCertElements.reduce_size(writer.GetLengthWritten());
116 :
117 6 : return CHIP_NO_ERROR;
118 : }
119 :
120 5 : CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElements & certElements)
121 : {
122 : CHIP_ERROR err;
123 : TLVReader reader;
124 : TLVType outerContainer1, outerContainer2;
125 :
126 5 : VerifyOrReturnError(encodedCertElements.size() <= kMaxCMSSignedCDMessage, CHIP_ERROR_INVALID_ARGUMENT);
127 :
128 5 : reader.Init(encodedCertElements);
129 :
130 5 : ReturnErrorOnFailure(reader.Next(kTLVType_Structure, AnonymousTag()));
131 :
132 5 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer1));
133 :
134 5 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_FormatVersion)));
135 5 : ReturnErrorOnFailure(reader.Get(certElements.FormatVersion));
136 :
137 5 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VendorId)));
138 5 : ReturnErrorOnFailure(reader.Get(certElements.VendorId));
139 :
140 4 : ReturnErrorOnFailure(reader.Next(kTLVType_Array, ContextTag(kTag_ProductIdArray)));
141 3 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer2));
142 :
143 3 : certElements.ProductIdsCount = 0;
144 106 : while ((err = reader.Next(AnonymousTag())) == CHIP_NO_ERROR)
145 : {
146 103 : VerifyOrReturnError(certElements.ProductIdsCount < kMaxProductIdsCount, CHIP_ERROR_INVALID_ARGUMENT);
147 103 : ReturnErrorOnFailure(reader.Get(certElements.ProductIds[certElements.ProductIdsCount++]));
148 : }
149 3 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
150 3 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer2));
151 :
152 3 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DeviceTypeId)));
153 3 : ReturnErrorOnFailure(reader.Get(certElements.DeviceTypeId));
154 :
155 3 : ReturnErrorOnFailure(reader.Next(kTLVType_UTF8String, ContextTag(kTag_CertificateId)));
156 3 : ReturnErrorOnFailure(reader.GetString(certElements.CertificateId, sizeof(certElements.CertificateId)));
157 3 : VerifyOrReturnError(strlen(certElements.CertificateId) == kCertificateIdLength, CHIP_ERROR_INVALID_TLV_ELEMENT);
158 :
159 3 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityLevel)));
160 3 : ReturnErrorOnFailure(reader.Get(certElements.SecurityLevel));
161 :
162 3 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityInformation)));
163 3 : ReturnErrorOnFailure(reader.Get(certElements.SecurityInformation));
164 :
165 3 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VersionNumber)));
166 3 : ReturnErrorOnFailure(reader.Get(certElements.VersionNumber));
167 :
168 3 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_CertificationType)));
169 3 : ReturnErrorOnFailure(reader.Get(certElements.CertificationType));
170 :
171 3 : certElements.DACOriginVIDandPIDPresent = false;
172 :
173 : // If kTag_DACOriginVendorId present then kTag_DACOriginProductId must be present.
174 3 : if ((err = reader.Next(ContextTag(kTag_DACOriginVendorId))) == CHIP_NO_ERROR)
175 : {
176 1 : ReturnErrorOnFailure(reader.Get(certElements.DACOriginVendorId));
177 :
178 1 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DACOriginProductId)));
179 1 : ReturnErrorOnFailure(reader.Get(certElements.DACOriginProductId));
180 :
181 1 : certElements.DACOriginVIDandPIDPresent = true;
182 :
183 1 : err = reader.Next();
184 : }
185 3 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err);
186 3 : VerifyOrReturnError(reader.GetTag() != TLV::ContextTag(kTag_DACOriginProductId), CHIP_ERROR_INVALID_TLV_ELEMENT);
187 :
188 3 : if (err != CHIP_END_OF_TLV && reader.GetTag() == ContextTag(kTag_AuthorizedPAAList))
189 : {
190 1 : VerifyOrReturnError(reader.GetType() == kTLVType_Array, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
191 :
192 1 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer2));
193 :
194 1 : certElements.AuthorizedPAAListCount = 0;
195 11 : while ((err = reader.Next(kTLVType_ByteString, AnonymousTag())) == CHIP_NO_ERROR)
196 : {
197 10 : VerifyOrReturnError(reader.GetLength() == kKeyIdentifierLength, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
198 10 : VerifyOrReturnError(certElements.AuthorizedPAAListCount < kMaxAuthorizedPAAListCount, CHIP_ERROR_INVALID_ARGUMENT);
199 :
200 10 : ReturnErrorOnFailure(
201 : reader.GetBytes(certElements.AuthorizedPAAList[certElements.AuthorizedPAAListCount++], kKeyIdentifierLength));
202 : }
203 1 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
204 1 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer2));
205 :
206 1 : err = reader.Next();
207 : }
208 3 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err);
209 :
210 3 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer1));
211 :
212 3 : ReturnErrorOnFailure(reader.VerifyEndOfContainer());
213 :
214 3 : return CHIP_NO_ERROR;
215 : }
216 :
217 81 : CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElementsWithoutPIDs & certDeclContent)
218 : {
219 : CHIP_ERROR err;
220 : TLVReader reader;
221 : TLVType outerContainer;
222 : TLVType outerContainer2;
223 :
224 81 : VerifyOrReturnError(encodedCertElements.size() <= kMaxCMSSignedCDMessage, CHIP_ERROR_INVALID_ARGUMENT);
225 :
226 81 : reader.Init(encodedCertElements);
227 :
228 81 : ReturnErrorOnFailure(reader.Next(kTLVType_Structure, AnonymousTag()));
229 :
230 81 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer));
231 :
232 81 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_FormatVersion)));
233 80 : ReturnErrorOnFailure(reader.Get(certDeclContent.formatVersion));
234 :
235 80 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VendorId)));
236 79 : ReturnErrorOnFailure(reader.Get(certDeclContent.vendorId));
237 :
238 79 : ReturnErrorOnFailure(reader.Next(kTLVType_Array, ContextTag(kTag_ProductIdArray)));
239 78 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer2));
240 :
241 471 : while ((err = reader.Next(kTLVType_UnsignedInteger, AnonymousTag())) == CHIP_NO_ERROR)
242 : {
243 : // Verifies that the TLV structure of PID Array is correct
244 : // but skip the values
245 : }
246 78 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
247 78 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer2));
248 :
249 78 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DeviceTypeId)));
250 77 : ReturnErrorOnFailure(reader.Get(certDeclContent.deviceTypeId));
251 :
252 77 : ReturnErrorOnFailure(reader.Next(kTLVType_UTF8String, ContextTag(kTag_CertificateId)));
253 76 : ReturnErrorOnFailure(reader.GetString(certDeclContent.certificateId, sizeof(certDeclContent.certificateId)));
254 75 : VerifyOrReturnError(strlen(certDeclContent.certificateId) == kCertificateIdLength, CHIP_ERROR_INVALID_TLV_ELEMENT);
255 :
256 75 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityLevel)));
257 74 : ReturnErrorOnFailure(reader.Get(certDeclContent.securityLevel));
258 :
259 74 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityInformation)));
260 73 : ReturnErrorOnFailure(reader.Get(certDeclContent.securityInformation));
261 :
262 73 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VersionNumber)));
263 72 : ReturnErrorOnFailure(reader.Get(certDeclContent.versionNumber));
264 :
265 72 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_CertificationType)));
266 71 : ReturnErrorOnFailure(reader.Get(certDeclContent.certificationType));
267 :
268 71 : certDeclContent.dacOriginVIDandPIDPresent = false;
269 :
270 : // If kTag_DACOriginVendorId present then kTag_DACOriginProductId must be present.
271 71 : if ((err = reader.Next(ContextTag(kTag_DACOriginVendorId))) == CHIP_NO_ERROR)
272 : {
273 6 : ReturnErrorOnFailure(reader.Get(certDeclContent.dacOriginVendorId));
274 :
275 6 : ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DACOriginProductId)));
276 5 : ReturnErrorOnFailure(reader.Get(certDeclContent.dacOriginProductId));
277 :
278 5 : certDeclContent.dacOriginVIDandPIDPresent = true;
279 :
280 5 : err = reader.Next();
281 : }
282 70 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err);
283 70 : VerifyOrReturnError(reader.GetTag() != TLV::ContextTag(kTag_DACOriginProductId), CHIP_ERROR_INVALID_TLV_ELEMENT);
284 :
285 69 : if (err != CHIP_END_OF_TLV && reader.GetTag() == ContextTag(kTag_AuthorizedPAAList))
286 : {
287 7 : VerifyOrReturnError(reader.GetType() == kTLVType_Array, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
288 :
289 7 : ReturnErrorOnFailure(reader.EnterContainer(outerContainer2));
290 :
291 43 : while ((err = reader.Next(kTLVType_ByteString, AnonymousTag())) == CHIP_NO_ERROR)
292 : {
293 36 : VerifyOrReturnError(reader.GetLength() == kKeyIdentifierLength, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
294 : // Verifies that the TLV structure of the Authorized PAA List is correct
295 : // but skip the values
296 : }
297 7 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
298 :
299 7 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer2));
300 :
301 7 : certDeclContent.authorizedPAAListPresent = true;
302 :
303 7 : err = reader.Next();
304 : }
305 69 : VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err);
306 :
307 69 : ReturnErrorOnFailure(reader.ExitContainer(outerContainer));
308 :
309 69 : ReturnErrorOnFailure(reader.VerifyEndOfContainer());
310 :
311 69 : return CHIP_NO_ERROR;
312 : }
313 :
314 362 : bool CertificationElementsDecoder::IsProductIdIn(const ByteSpan & encodedCertElements, uint16_t productId)
315 : {
316 362 : VerifyOrReturnError(FindAndEnterArray(encodedCertElements, ContextTag(kTag_ProductIdArray)) == CHIP_NO_ERROR, false);
317 :
318 362 : uint16_t cdProductId = 0;
319 15431 : while (GetNextProductId(cdProductId) == CHIP_NO_ERROR)
320 : {
321 15322 : if (productId == cdProductId)
322 : {
323 253 : return true;
324 : }
325 : }
326 109 : return false;
327 : }
328 :
329 388 : CHIP_ERROR CertificationElementsDecoder::FindAndEnterArray(const ByteSpan & encodedCertElements, Tag arrayTag)
330 : {
331 : TLVType outerContainerType1;
332 : TLVType outerContainerType2;
333 :
334 388 : mReader.Init(encodedCertElements);
335 388 : ReturnErrorOnFailure(mReader.Next(kTLVType_Structure, AnonymousTag()));
336 388 : ReturnErrorOnFailure(mReader.EnterContainer(outerContainerType1));
337 :
338 : // position to arrayTag Array
339 : do
340 : {
341 1346 : ReturnErrorOnFailure(mReader.Next());
342 1346 : } while (mReader.Expect(kTLVType_Array, arrayTag) != CHIP_NO_ERROR);
343 :
344 388 : ReturnErrorOnFailure(mReader.EnterContainer(outerContainerType2));
345 :
346 388 : return CHIP_NO_ERROR;
347 : }
348 :
349 15431 : CHIP_ERROR CertificationElementsDecoder::GetNextProductId(uint16_t & productId)
350 : {
351 15431 : ReturnErrorOnFailure(mReader.Next(AnonymousTag()));
352 15322 : ReturnErrorOnFailure(mReader.Get(productId));
353 15322 : return CHIP_NO_ERROR;
354 : }
355 :
356 26 : bool CertificationElementsDecoder::HasAuthorizedPAA(const ByteSpan & encodedCertElements, const ByteSpan & authorizedPAA)
357 : {
358 26 : VerifyOrReturnError(FindAndEnterArray(encodedCertElements, ContextTag(kTag_AuthorizedPAAList)) == CHIP_NO_ERROR, false);
359 :
360 26 : ByteSpan cdAuthorizedPAA;
361 184 : while (GetNextAuthorizedPAA(cdAuthorizedPAA) == CHIP_NO_ERROR)
362 : {
363 171 : if (authorizedPAA.data_equal(cdAuthorizedPAA))
364 : {
365 13 : return true;
366 : }
367 : }
368 13 : return false;
369 : }
370 :
371 184 : CHIP_ERROR CertificationElementsDecoder::GetNextAuthorizedPAA(ByteSpan & authorizedPAA)
372 : {
373 184 : ReturnErrorOnFailure(mReader.Next(AnonymousTag()));
374 171 : ReturnErrorOnFailure(mReader.Get(authorizedPAA));
375 171 : return CHIP_NO_ERROR;
376 : }
377 :
378 : namespace {
379 :
380 2 : CHIP_ERROR EncodeEncapsulatedContent(const ByteSpan & cdContent, ASN1Writer & writer)
381 : {
382 : /**
383 : * EncapsulatedContentInfo ::= SEQUENCE {
384 : * eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1),
385 : * eContent [0] EXPLICIT OCTET STRING cd_content }
386 : */
387 2 : CHIP_ERROR err = CHIP_NO_ERROR;
388 :
389 2 : ASN1_START_SEQUENCE
390 : {
391 : // eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1)
392 2 : ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7Data, sizeof(sOID_ContentType_PKCS7Data)));
393 :
394 : // eContent [0] EXPLICIT OCTET STRING cd_content
395 2 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
396 : {
397 : // OCTET STRING cd_content
398 2 : ReturnErrorOnFailure(writer.PutOctetString(cdContent.data(), static_cast<uint16_t>(cdContent.size())));
399 : }
400 2 : ASN1_END_CONSTRUCTED;
401 : }
402 2 : ASN1_END_SEQUENCE;
403 :
404 2 : exit:
405 2 : return err;
406 : }
407 :
408 90 : CHIP_ERROR DecodeEncapsulatedContent(ASN1Reader & reader, ByteSpan & cdContent)
409 : {
410 : /**
411 : * EncapsulatedContentInfo ::= SEQUENCE {
412 : * eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1),
413 : * eContent [0] EXPLICIT OCTET STRING cd_content }
414 : */
415 90 : CHIP_ERROR err = CHIP_NO_ERROR;
416 :
417 90 : ASN1_PARSE_ENTER_SEQUENCE
418 : {
419 : // eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1)
420 90 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
421 90 : VerifyOrReturnError(ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_ContentType_PKCS7Data)),
422 : ASN1_ERROR_UNSUPPORTED_ENCODING);
423 :
424 : // eContent [0] EXPLICIT OCTET STRING cd_content
425 89 : ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
426 : {
427 : // OCTET STRING cd_content
428 89 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
429 89 : cdContent = ByteSpan(reader.GetValue(), reader.GetValueLen());
430 : }
431 89 : ASN1_EXIT_CONSTRUCTED;
432 : }
433 89 : ASN1_EXIT_SEQUENCE;
434 :
435 89 : exit:
436 89 : return err;
437 : }
438 :
439 2 : CHIP_ERROR EncodeSignerInfo(const ByteSpan & signerKeyId, const P256ECDSASignature & signature, ASN1Writer & writer)
440 : {
441 : /**
442 : * SignerInfo ::= SEQUENCE {
443 : * version INTEGER ( v3(3) ),
444 : * subjectKeyIdentifier OCTET STRING,
445 : * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1),
446 : * signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2),
447 : * signature OCTET STRING }
448 : */
449 2 : CHIP_ERROR err = CHIP_NO_ERROR;
450 :
451 2 : ASN1_START_SET
452 : {
453 2 : ASN1_START_SEQUENCE
454 : {
455 : // version INTEGER ( v3(3) )
456 2 : ASN1_ENCODE_INTEGER(3);
457 :
458 : // subjectKeyIdentifier OCTET STRING
459 2 : ReturnErrorOnFailure(writer.PutOctetString(kASN1TagClass_ContextSpecific, 0, signerKeyId.data(),
460 : static_cast<uint16_t>(signerKeyId.size())));
461 :
462 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
463 2 : ASN1_START_SEQUENCE
464 : {
465 2 : ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256)));
466 : }
467 2 : ASN1_END_SEQUENCE;
468 :
469 : // signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2)
470 2 : ASN1_START_SEQUENCE
471 : {
472 2 : ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
473 : }
474 2 : ASN1_END_SEQUENCE;
475 :
476 : // signature OCTET STRING
477 2 : ASN1_START_OCTET_STRING_ENCAPSULATED
478 : {
479 2 : ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan(signature.ConstBytes()), writer));
480 : }
481 2 : ASN1_END_ENCAPSULATED;
482 : }
483 2 : ASN1_END_SEQUENCE;
484 : }
485 2 : ASN1_END_SET;
486 :
487 2 : exit:
488 2 : return err;
489 : }
490 :
491 178 : CHIP_ERROR DecodeSignerInfo(ASN1Reader & reader, ByteSpan & signerKeyId, P256ECDSASignature & signature)
492 : {
493 : /**
494 : * SignerInfo ::= SEQUENCE {
495 : * version INTEGER ( v3(3) ),
496 : * subjectKeyIdentifier OCTET STRING,
497 : * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1),
498 : * signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2),
499 : * signature OCTET STRING }
500 : */
501 178 : CHIP_ERROR err = CHIP_NO_ERROR;
502 :
503 178 : ASN1_PARSE_ENTER_SET
504 : {
505 178 : ASN1_PARSE_ENTER_SEQUENCE
506 : {
507 : // version INTEGER ( v3(3) )
508 : {
509 : int64_t version;
510 179 : ASN1_PARSE_INTEGER(version);
511 :
512 : // Verify that the CMS version is v3
513 178 : VerifyOrExit(version == 3, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
514 : }
515 :
516 : // subjectKeyIdentifier OCTET STRING
517 177 : ASN1_PARSE_ELEMENT(kASN1TagClass_ContextSpecific, 0);
518 177 : signerKeyId = ByteSpan(reader.GetValue(), reader.GetValueLen());
519 :
520 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
521 177 : ASN1_PARSE_ENTER_SEQUENCE
522 : {
523 177 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
524 178 : VerifyOrReturnError(ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_DigestAlgo_SHA256)),
525 : ASN1_ERROR_UNSUPPORTED_ENCODING);
526 : }
527 176 : ASN1_EXIT_SEQUENCE;
528 :
529 : // signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2)
530 176 : ASN1_PARSE_ENTER_SEQUENCE
531 : {
532 176 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
533 176 : VerifyOrReturnError(
534 : ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_SigAlgo_ECDSAWithSHA256)),
535 : ASN1_ERROR_UNSUPPORTED_ENCODING);
536 : }
537 175 : ASN1_EXIT_SEQUENCE;
538 :
539 : // signature OCTET STRING
540 175 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
541 :
542 175 : MutableByteSpan signatureSpan(signature.Bytes(), signature.Capacity());
543 175 : ReturnErrorOnFailure(
544 : EcdsaAsn1SignatureToRaw(kP256_FE_Length, ByteSpan(reader.GetValue(), reader.GetValueLen()), signatureSpan));
545 175 : ReturnErrorOnFailure(signature.SetLength(signatureSpan.size()));
546 : }
547 175 : ASN1_EXIT_SEQUENCE;
548 : }
549 175 : ASN1_EXIT_SET;
550 :
551 176 : exit:
552 176 : return err;
553 : }
554 :
555 : } // namespace
556 :
557 2 : CHIP_ERROR CMS_Sign(const ByteSpan & cdContent, const ByteSpan & signerKeyId, Crypto::P256Keypair & signerKeypair,
558 : MutableByteSpan & signedMessage)
559 : {
560 : /**
561 : * CertificationDeclaration ::= SEQUENCE {
562 : * version INTEGER ( v3(3) ),
563 : * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1),
564 : * encapContentInfo EncapsulatedContentInfo,
565 : * signerInfo SignerInfo }
566 : */
567 2 : CHIP_ERROR err = CHIP_NO_ERROR;
568 : ASN1Writer writer;
569 2 : uint32_t size = static_cast<uint32_t>(std::min(static_cast<size_t>(UINT32_MAX), signedMessage.size()));
570 :
571 2 : writer.Init(signedMessage.data(), size);
572 :
573 2 : ASN1_START_SEQUENCE
574 : {
575 : // OID identifies the CMS signed-data content type
576 2 : ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7SignedData, sizeof(sOID_ContentType_PKCS7SignedData)));
577 :
578 2 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
579 : {
580 2 : ASN1_START_SEQUENCE
581 : {
582 : // version INTEGER ( v3(3) )
583 2 : ASN1_ENCODE_INTEGER(3);
584 :
585 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
586 2 : ASN1_START_SET
587 : {
588 2 : ASN1_START_SEQUENCE
589 : {
590 2 : ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256)));
591 : }
592 2 : ASN1_END_SEQUENCE;
593 : }
594 2 : ASN1_END_SET;
595 :
596 : // encapContentInfo EncapsulatedContentInfo
597 2 : ReturnErrorOnFailure(EncodeEncapsulatedContent(cdContent, writer));
598 :
599 2 : Crypto::P256ECDSASignature signature;
600 2 : ReturnErrorOnFailure(signerKeypair.ECDSA_sign_msg(cdContent.data(), cdContent.size(), signature));
601 :
602 : // signerInfo SignerInfo
603 2 : ReturnErrorOnFailure(EncodeSignerInfo(signerKeyId, signature, writer));
604 2 : }
605 2 : ASN1_END_SEQUENCE;
606 : }
607 2 : ASN1_END_CONSTRUCTED;
608 : }
609 2 : ASN1_END_SEQUENCE;
610 :
611 2 : signedMessage.reduce_size(writer.GetLengthWritten());
612 :
613 2 : exit:
614 2 : return err;
615 : }
616 :
617 2 : CHIP_ERROR CMS_Verify(const ByteSpan & signedMessage, const ByteSpan & signerX509Cert, ByteSpan & cdContent)
618 : {
619 2 : P256PublicKey signerPubkey;
620 :
621 2 : ReturnErrorOnFailure(ExtractPubkeyFromX509Cert(signerX509Cert, signerPubkey));
622 :
623 2 : return CMS_Verify(signedMessage, signerPubkey, cdContent);
624 2 : }
625 :
626 90 : CHIP_ERROR CMS_Verify(const ByteSpan & signedMessage, const Crypto::P256PublicKey & signerPubkey, ByteSpan & cdContent)
627 : {
628 90 : CHIP_ERROR err = CHIP_NO_ERROR;
629 : ASN1Reader reader;
630 90 : uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(signedMessage.size());
631 :
632 90 : reader.Init(signedMessage.data(), size);
633 :
634 : // SignedData ::= SEQUENCE
635 90 : ASN1_PARSE_ENTER_SEQUENCE
636 : {
637 90 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
638 :
639 : // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
640 : // OID identifies the CMS signed-data content type
641 90 : VerifyOrReturnError(
642 : ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_ContentType_PKCS7SignedData)),
643 : ASN1_ERROR_UNSUPPORTED_ENCODING);
644 :
645 : // version [0] EXPLICIT Version DEFAULT v3
646 90 : ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
647 : {
648 90 : ASN1_PARSE_ENTER_SEQUENCE
649 : {
650 : // Version ::= INTEGER { v3(3) }
651 : int64_t version;
652 91 : ASN1_PARSE_INTEGER(version);
653 :
654 : // Verify that the CMS version is v3
655 90 : VerifyOrExit(version == 3, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
656 :
657 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
658 89 : ASN1_PARSE_ENTER_SET
659 : {
660 89 : ASN1_PARSE_ENTER_SEQUENCE
661 : {
662 89 : ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
663 91 : VerifyOrReturnError(
664 : ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_DigestAlgo_SHA256)),
665 : ASN1_ERROR_UNSUPPORTED_ENCODING);
666 : }
667 88 : ASN1_EXIT_SEQUENCE;
668 : }
669 88 : ASN1_EXIT_SET;
670 :
671 : // encapContentInfo EncapsulatedContentInfo
672 88 : ReturnErrorOnFailure(DecodeEncapsulatedContent(reader, cdContent));
673 :
674 : // signerInfo SignerInfo
675 87 : ByteSpan signerKeyId;
676 87 : P256ECDSASignature signature;
677 87 : ReturnErrorOnFailure(DecodeSignerInfo(reader, signerKeyId, signature));
678 :
679 : // Validate CD Signature
680 87 : ReturnErrorOnFailure(signerPubkey.ECDSA_validate_msg_signature(cdContent.data(), cdContent.size(), signature));
681 87 : }
682 86 : ASN1_EXIT_SEQUENCE;
683 : }
684 86 : ASN1_EXIT_CONSTRUCTED;
685 : }
686 86 : ASN1_EXIT_SEQUENCE;
687 :
688 87 : exit:
689 87 : return err;
690 : }
691 :
692 91 : CHIP_ERROR CMS_ExtractKeyId(const ByteSpan & signedMessage, ByteSpan & signerKeyId)
693 : {
694 91 : CHIP_ERROR err = CHIP_NO_ERROR;
695 : ASN1Reader reader;
696 91 : uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(signedMessage.size());
697 :
698 91 : reader.Init(signedMessage.data(), size);
699 :
700 : // SignedData ::= SEQUENCE
701 91 : ASN1_PARSE_ENTER_SEQUENCE
702 : {
703 : // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
704 : // OID identifies the CMS signed-data content type
705 91 : ASN1_PARSE_ANY;
706 :
707 : // version [0] EXPLICIT Version DEFAULT v3
708 91 : ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
709 : {
710 91 : ASN1_PARSE_ENTER_SEQUENCE
711 : {
712 : // Version ::= INTEGER { v3(3) }
713 91 : ASN1_PARSE_ANY;
714 :
715 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
716 91 : ASN1_PARSE_ANY;
717 :
718 : // encapContentInfo EncapsulatedContentInfo
719 91 : ASN1_PARSE_ANY;
720 :
721 : // signerInfo SignerInfo
722 91 : P256ECDSASignature signature;
723 91 : ReturnErrorOnFailure(DecodeSignerInfo(reader, signerKeyId, signature));
724 91 : }
725 88 : ASN1_EXIT_SEQUENCE;
726 : }
727 88 : ASN1_EXIT_CONSTRUCTED;
728 : }
729 88 : ASN1_EXIT_SEQUENCE;
730 :
731 88 : exit:
732 88 : return err;
733 : }
734 :
735 2 : CHIP_ERROR CMS_ExtractCDContent(const ByteSpan & signedMessage, ByteSpan & cdContent)
736 : {
737 2 : CHIP_ERROR err = CHIP_NO_ERROR;
738 : ASN1Reader reader;
739 2 : uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(signedMessage.size());
740 :
741 2 : reader.Init(signedMessage.data(), size);
742 :
743 : // SignedData ::= SEQUENCE
744 2 : ASN1_PARSE_ENTER_SEQUENCE
745 : {
746 : // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
747 : // OID identifies the CMS signed-data content type
748 2 : ASN1_PARSE_ANY;
749 :
750 : // version [0] EXPLICIT Version DEFAULT v3
751 2 : ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
752 : {
753 2 : ASN1_PARSE_ENTER_SEQUENCE
754 : {
755 : // Version ::= INTEGER { v3(3) }
756 2 : ASN1_PARSE_ANY;
757 :
758 : // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1)
759 2 : ASN1_PARSE_ANY;
760 :
761 : // encapContentInfo EncapsulatedContentInfo
762 2 : ReturnErrorOnFailure(DecodeEncapsulatedContent(reader, cdContent));
763 :
764 : // signerInfo SignerInfo
765 2 : ASN1_PARSE_ANY;
766 : }
767 2 : ASN1_EXIT_SEQUENCE;
768 : }
769 2 : ASN1_EXIT_CONSTRUCTED;
770 : }
771 2 : ASN1_EXIT_SEQUENCE;
772 :
773 2 : exit:
774 2 : return err;
775 : }
776 :
777 : } // namespace Credentials
778 : } // namespace chip
|