/* * * Copyright (c) 2021-2022 Project CHIP Authors * All rights reserved. * * 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. */ /** * @file * This file implements data types, objects and APIs for * working with Certification Declaration elements. */ #include #include #include #include #include #include #include #include #include namespace chip { namespace Credentials { using namespace chip::ASN1; using namespace chip::TLV; using namespace chip::Crypto; static constexpr uint8_t sOID_ContentType_PKCS7Data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; static constexpr uint8_t sOID_ContentType_PKCS7SignedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; static constexpr uint8_t sOID_DigestAlgo_SHA256[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }; static constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; /** Certification Declaration Element TLV Tags */ enum { kTag_FormatVersion = 0, /**< [ unsigned int ] Format version. */ kTag_VendorId = 1, /**< [ unsigned int ] Vedor identifier. */ kTag_ProductIdArray = 2, /**< [ array ] Product identifiers (each is unsigned int). */ kTag_DeviceTypeId = 3, /**< [ unsigned int ] Device Type identifier. */ kTag_CertificateId = 4, /**< [ UTF-8 string, length 19 ] Certificate identifier. */ kTag_SecurityLevel = 5, /**< [ unsigned int ] Security level. */ kTag_SecurityInformation = 6, /**< [ unsigned int ] Security information. */ kTag_VersionNumber = 7, /**< [ unsigned int ] Version number. */ kTag_CertificationType = 8, /**< [ unsigned int ] Certification Type. */ kTag_DACOriginVendorId = 9, /**< [ unsigned int, optional ] DAC origin vendor identifier. */ kTag_DACOriginProductId = 10, /**< [ unsigned int, optional ] DAC origin product identifier. */ kTag_AuthorizedPAAList = 11, /**< [ array, optional ] Authorized PAA List. */ }; CHIP_ERROR EncodeCertificationElements(const CertificationElements & certElements, MutableByteSpan & encodedCertElements) { TLVWriter writer; TLVType outerContainer1, outerContainer2; writer.Init(encodedCertElements); ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainer1)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_FormatVersion), certElements.FormatVersion)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VendorId), certElements.VendorId)); VerifyOrReturnError(certElements.ProductIdsCount > 0, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(certElements.ProductIdsCount <= kMaxProductIdsCount, CHIP_ERROR_INVALID_ARGUMENT); ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ProductIdArray), kTLVType_Array, outerContainer2)); for (uint8_t i = 0; i < certElements.ProductIdsCount; i++) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), certElements.ProductIds[i])); } ReturnErrorOnFailure(writer.EndContainer(outerContainer2)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DeviceTypeId), certElements.DeviceTypeId)); ReturnErrorOnFailure(writer.PutString(ContextTag(kTag_CertificateId), certElements.CertificateId)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityLevel), certElements.SecurityLevel)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityInformation), certElements.SecurityInformation)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VersionNumber), certElements.VersionNumber)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_CertificationType), certElements.CertificationType)); if (certElements.DACOriginVIDandPIDPresent) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginVendorId), certElements.DACOriginVendorId)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginProductId), certElements.DACOriginProductId)); } if (certElements.AuthorizedPAAListCount > 0) { VerifyOrReturnError(certElements.AuthorizedPAAListCount <= kMaxAuthorizedPAAListCount, CHIP_ERROR_INVALID_ARGUMENT); ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_AuthorizedPAAList), kTLVType_Array, outerContainer2)); for (uint8_t i = 0; i < certElements.AuthorizedPAAListCount; i++) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), ByteSpan(certElements.AuthorizedPAAList[i]))); } ReturnErrorOnFailure(writer.EndContainer(outerContainer2)); } ReturnErrorOnFailure(writer.EndContainer(outerContainer1)); ReturnErrorOnFailure(writer.Finalize()); encodedCertElements.reduce_size(writer.GetLengthWritten()); return CHIP_NO_ERROR; } CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElements & certElements) { CHIP_ERROR err; TLVReader reader; TLVType outerContainer1, outerContainer2; VerifyOrReturnError(encodedCertElements.size() <= kMaxCMSSignedCDMessage, CHIP_ERROR_INVALID_ARGUMENT); reader.Init(encodedCertElements); ReturnErrorOnFailure(reader.Next(kTLVType_Structure, AnonymousTag())); ReturnErrorOnFailure(reader.EnterContainer(outerContainer1)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_FormatVersion))); ReturnErrorOnFailure(reader.Get(certElements.FormatVersion)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VendorId))); ReturnErrorOnFailure(reader.Get(certElements.VendorId)); ReturnErrorOnFailure(reader.Next(kTLVType_Array, ContextTag(kTag_ProductIdArray))); ReturnErrorOnFailure(reader.EnterContainer(outerContainer2)); certElements.ProductIdsCount = 0; while ((err = reader.Next(AnonymousTag())) == CHIP_NO_ERROR) { VerifyOrReturnError(certElements.ProductIdsCount < kMaxProductIdsCount, CHIP_ERROR_INVALID_ARGUMENT); ReturnErrorOnFailure(reader.Get(certElements.ProductIds[certElements.ProductIdsCount++])); } VerifyOrReturnError(err == CHIP_END_OF_TLV, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer2)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DeviceTypeId))); ReturnErrorOnFailure(reader.Get(certElements.DeviceTypeId)); ReturnErrorOnFailure(reader.Next(kTLVType_UTF8String, ContextTag(kTag_CertificateId))); ReturnErrorOnFailure(reader.GetString(certElements.CertificateId, sizeof(certElements.CertificateId))); VerifyOrReturnError(strlen(certElements.CertificateId) == kCertificateIdLength, CHIP_ERROR_INVALID_TLV_ELEMENT); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityLevel))); ReturnErrorOnFailure(reader.Get(certElements.SecurityLevel)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityInformation))); ReturnErrorOnFailure(reader.Get(certElements.SecurityInformation)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VersionNumber))); ReturnErrorOnFailure(reader.Get(certElements.VersionNumber)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_CertificationType))); ReturnErrorOnFailure(reader.Get(certElements.CertificationType)); certElements.DACOriginVIDandPIDPresent = false; // If kTag_DACOriginVendorId present then kTag_DACOriginProductId must be present. if ((err = reader.Next(ContextTag(kTag_DACOriginVendorId))) == CHIP_NO_ERROR) { ReturnErrorOnFailure(reader.Get(certElements.DACOriginVendorId)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DACOriginProductId))); ReturnErrorOnFailure(reader.Get(certElements.DACOriginProductId)); certElements.DACOriginVIDandPIDPresent = true; err = reader.Next(); } VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err); VerifyOrReturnError(reader.GetTag() != TLV::ContextTag(kTag_DACOriginProductId), CHIP_ERROR_INVALID_TLV_ELEMENT); if (err != CHIP_END_OF_TLV && reader.GetTag() == ContextTag(kTag_AuthorizedPAAList)) { VerifyOrReturnError(reader.GetType() == kTLVType_Array, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); ReturnErrorOnFailure(reader.EnterContainer(outerContainer2)); certElements.AuthorizedPAAListCount = 0; while ((err = reader.Next(kTLVType_ByteString, AnonymousTag())) == CHIP_NO_ERROR) { VerifyOrReturnError(reader.GetLength() == kKeyIdentifierLength, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); VerifyOrReturnError(certElements.AuthorizedPAAListCount < kMaxAuthorizedPAAListCount, CHIP_ERROR_INVALID_ARGUMENT); ReturnErrorOnFailure( reader.GetBytes(certElements.AuthorizedPAAList[certElements.AuthorizedPAAListCount++], kKeyIdentifierLength)); } VerifyOrReturnError(err == CHIP_END_OF_TLV, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer2)); err = reader.Next(); } VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer1)); ReturnErrorOnFailure(reader.VerifyEndOfContainer()); return CHIP_NO_ERROR; } CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElementsWithoutPIDs & certDeclContent) { CHIP_ERROR err; TLVReader reader; TLVType outerContainer; TLVType outerContainer2; VerifyOrReturnError(encodedCertElements.size() <= kMaxCMSSignedCDMessage, CHIP_ERROR_INVALID_ARGUMENT); reader.Init(encodedCertElements); ReturnErrorOnFailure(reader.Next(kTLVType_Structure, AnonymousTag())); ReturnErrorOnFailure(reader.EnterContainer(outerContainer)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_FormatVersion))); ReturnErrorOnFailure(reader.Get(certDeclContent.formatVersion)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VendorId))); ReturnErrorOnFailure(reader.Get(certDeclContent.vendorId)); ReturnErrorOnFailure(reader.Next(kTLVType_Array, ContextTag(kTag_ProductIdArray))); ReturnErrorOnFailure(reader.EnterContainer(outerContainer2)); while ((err = reader.Next(kTLVType_UnsignedInteger, AnonymousTag())) == CHIP_NO_ERROR) { // Verifies that the TLV structure of PID Array is correct // but skip the values } VerifyOrReturnError(err == CHIP_END_OF_TLV, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer2)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DeviceTypeId))); ReturnErrorOnFailure(reader.Get(certDeclContent.deviceTypeId)); ReturnErrorOnFailure(reader.Next(kTLVType_UTF8String, ContextTag(kTag_CertificateId))); ReturnErrorOnFailure(reader.GetString(certDeclContent.certificateId, sizeof(certDeclContent.certificateId))); VerifyOrReturnError(strlen(certDeclContent.certificateId) == kCertificateIdLength, CHIP_ERROR_INVALID_TLV_ELEMENT); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityLevel))); ReturnErrorOnFailure(reader.Get(certDeclContent.securityLevel)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_SecurityInformation))); ReturnErrorOnFailure(reader.Get(certDeclContent.securityInformation)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_VersionNumber))); ReturnErrorOnFailure(reader.Get(certDeclContent.versionNumber)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_CertificationType))); ReturnErrorOnFailure(reader.Get(certDeclContent.certificationType)); certDeclContent.dacOriginVIDandPIDPresent = false; // If kTag_DACOriginVendorId present then kTag_DACOriginProductId must be present. if ((err = reader.Next(ContextTag(kTag_DACOriginVendorId))) == CHIP_NO_ERROR) { ReturnErrorOnFailure(reader.Get(certDeclContent.dacOriginVendorId)); ReturnErrorOnFailure(reader.Next(ContextTag(kTag_DACOriginProductId))); ReturnErrorOnFailure(reader.Get(certDeclContent.dacOriginProductId)); certDeclContent.dacOriginVIDandPIDPresent = true; err = reader.Next(); } VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err); VerifyOrReturnError(reader.GetTag() != TLV::ContextTag(kTag_DACOriginProductId), CHIP_ERROR_INVALID_TLV_ELEMENT); if (err != CHIP_END_OF_TLV && reader.GetTag() == ContextTag(kTag_AuthorizedPAAList)) { VerifyOrReturnError(reader.GetType() == kTLVType_Array, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); ReturnErrorOnFailure(reader.EnterContainer(outerContainer2)); while ((err = reader.Next(kTLVType_ByteString, AnonymousTag())) == CHIP_NO_ERROR) { VerifyOrReturnError(reader.GetLength() == kKeyIdentifierLength, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); // Verifies that the TLV structure of the Authorized PAA List is correct // but skip the values } VerifyOrReturnError(err == CHIP_END_OF_TLV, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer2)); certDeclContent.authorizedPAAListPresent = true; err = reader.Next(); } VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err); ReturnErrorOnFailure(reader.ExitContainer(outerContainer)); ReturnErrorOnFailure(reader.VerifyEndOfContainer()); return CHIP_NO_ERROR; } bool CertificationElementsDecoder::IsProductIdIn(const ByteSpan & encodedCertElements, uint16_t productId) { VerifyOrReturnError(FindAndEnterArray(encodedCertElements, ContextTag(kTag_ProductIdArray)) == CHIP_NO_ERROR, false); uint16_t cdProductId = 0; while (GetNextProductId(cdProductId) == CHIP_NO_ERROR) { if (productId == cdProductId) { return true; } } return false; } CHIP_ERROR CertificationElementsDecoder::FindAndEnterArray(const ByteSpan & encodedCertElements, Tag arrayTag) { TLVType outerContainerType1; TLVType outerContainerType2; mReader.Init(encodedCertElements); ReturnErrorOnFailure(mReader.Next(kTLVType_Structure, AnonymousTag())); ReturnErrorOnFailure(mReader.EnterContainer(outerContainerType1)); // position to arrayTag Array do { ReturnErrorOnFailure(mReader.Next()); } while (mReader.Expect(kTLVType_Array, arrayTag) != CHIP_NO_ERROR); ReturnErrorOnFailure(mReader.EnterContainer(outerContainerType2)); return CHIP_NO_ERROR; } CHIP_ERROR CertificationElementsDecoder::GetNextProductId(uint16_t & productId) { ReturnErrorOnFailure(mReader.Next(AnonymousTag())); ReturnErrorOnFailure(mReader.Get(productId)); return CHIP_NO_ERROR; } bool CertificationElementsDecoder::HasAuthorizedPAA(const ByteSpan & encodedCertElements, const ByteSpan & authorizedPAA) { VerifyOrReturnError(FindAndEnterArray(encodedCertElements, ContextTag(kTag_AuthorizedPAAList)) == CHIP_NO_ERROR, false); ByteSpan cdAuthorizedPAA; while (GetNextAuthorizedPAA(cdAuthorizedPAA) == CHIP_NO_ERROR) { if (authorizedPAA.data_equal(cdAuthorizedPAA)) { return true; } } return false; } CHIP_ERROR CertificationElementsDecoder::GetNextAuthorizedPAA(ByteSpan & authorizedPAA) { ReturnErrorOnFailure(mReader.Next(AnonymousTag())); ReturnErrorOnFailure(mReader.Get(authorizedPAA)); return CHIP_NO_ERROR; } namespace { CHIP_ERROR EncodeEncapsulatedContent(const ByteSpan & cdContent, ASN1Writer & writer) { /** * EncapsulatedContentInfo ::= SEQUENCE { * eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1), * eContent [0] EXPLICIT OCTET STRING cd_content } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_START_SEQUENCE { // eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1) ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7Data, sizeof(sOID_ContentType_PKCS7Data))); // eContent [0] EXPLICIT OCTET STRING cd_content ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { // OCTET STRING cd_content ReturnErrorOnFailure(writer.PutOctetString(cdContent.data(), static_cast(cdContent.size()))); } ASN1_END_CONSTRUCTED; } ASN1_END_SEQUENCE; exit: return err; } CHIP_ERROR DecodeEncapsulatedContent(ASN1Reader & reader, ByteSpan & cdContent) { /** * EncapsulatedContentInfo ::= SEQUENCE { * eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1), * eContent [0] EXPLICIT OCTET STRING cd_content } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_PARSE_ENTER_SEQUENCE { // eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1) ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId); VerifyOrReturnError(ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_ContentType_PKCS7Data)), ASN1_ERROR_UNSUPPORTED_ENCODING); // eContent [0] EXPLICIT OCTET STRING cd_content ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { // OCTET STRING cd_content ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString); cdContent = ByteSpan(reader.GetValue(), reader.GetValueLen()); } ASN1_EXIT_CONSTRUCTED; } ASN1_EXIT_SEQUENCE; exit: return err; } CHIP_ERROR EncodeSignerInfo(const ByteSpan & signerKeyId, const P256ECDSASignature & signature, ASN1Writer & writer) { /** * SignerInfo ::= SEQUENCE { * version INTEGER ( v3(3) ), * subjectKeyIdentifier OCTET STRING, * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1), * signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2), * signature OCTET STRING } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_START_SET { ASN1_START_SEQUENCE { // version INTEGER ( v3(3) ) ASN1_ENCODE_INTEGER(3); // subjectKeyIdentifier OCTET STRING ReturnErrorOnFailure(writer.PutOctetString(kASN1TagClass_ContextSpecific, 0, signerKeyId.data(), static_cast(signerKeyId.size()))); // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_START_SEQUENCE { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256))); } ASN1_END_SEQUENCE; // signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2) ASN1_START_SEQUENCE { ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256); } ASN1_END_SEQUENCE; // signature OCTET STRING ASN1_START_OCTET_STRING_ENCAPSULATED { ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan(signature.ConstBytes()), writer)); } ASN1_END_ENCAPSULATED; } ASN1_END_SEQUENCE; } ASN1_END_SET; exit: return err; } CHIP_ERROR DecodeSignerInfo(ASN1Reader & reader, ByteSpan & signerKeyId, P256ECDSASignature & signature) { /** * SignerInfo ::= SEQUENCE { * version INTEGER ( v3(3) ), * subjectKeyIdentifier OCTET STRING, * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1), * signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2), * signature OCTET STRING } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_PARSE_ENTER_SET { ASN1_PARSE_ENTER_SEQUENCE { // version INTEGER ( v3(3) ) { int64_t version; ASN1_PARSE_INTEGER(version); // Verify that the CMS version is v3 VerifyOrExit(version == 3, err = ASN1_ERROR_UNSUPPORTED_ENCODING); } // subjectKeyIdentifier OCTET STRING ASN1_PARSE_ELEMENT(kASN1TagClass_ContextSpecific, 0); signerKeyId = ByteSpan(reader.GetValue(), reader.GetValueLen()); // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_PARSE_ENTER_SEQUENCE { ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId); VerifyOrReturnError(ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_DigestAlgo_SHA256)), ASN1_ERROR_UNSUPPORTED_ENCODING); } ASN1_EXIT_SEQUENCE; // signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2) ASN1_PARSE_ENTER_SEQUENCE { ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId); VerifyOrReturnError( ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_SigAlgo_ECDSAWithSHA256)), ASN1_ERROR_UNSUPPORTED_ENCODING); } ASN1_EXIT_SEQUENCE; // signature OCTET STRING ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString); MutableByteSpan signatureSpan(signature.Bytes(), signature.Capacity()); ReturnErrorOnFailure( EcdsaAsn1SignatureToRaw(kP256_FE_Length, ByteSpan(reader.GetValue(), reader.GetValueLen()), signatureSpan)); ReturnErrorOnFailure(signature.SetLength(signatureSpan.size())); } ASN1_EXIT_SEQUENCE; } ASN1_EXIT_SET; exit: return err; } } // namespace CHIP_ERROR CMS_Sign(const ByteSpan & cdContent, const ByteSpan & signerKeyId, Crypto::P256Keypair & signerKeypair, MutableByteSpan & signedMessage) { /** * CertificationDeclaration ::= SEQUENCE { * version INTEGER ( v3(3) ), * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1), * encapContentInfo EncapsulatedContentInfo, * signerInfo SignerInfo } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1Writer writer; uint32_t size = static_cast(std::min(static_cast(UINT32_MAX), signedMessage.size())); writer.Init(signedMessage.data(), size); ASN1_START_SEQUENCE { // OID identifies the CMS signed-data content type ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7SignedData, sizeof(sOID_ContentType_PKCS7SignedData))); ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { ASN1_START_SEQUENCE { // version INTEGER ( v3(3) ) ASN1_ENCODE_INTEGER(3); // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_START_SET { ASN1_START_SEQUENCE { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256))); } ASN1_END_SEQUENCE; } ASN1_END_SET; // encapContentInfo EncapsulatedContentInfo ReturnErrorOnFailure(EncodeEncapsulatedContent(cdContent, writer)); Crypto::P256ECDSASignature signature; ReturnErrorOnFailure(signerKeypair.ECDSA_sign_msg(cdContent.data(), cdContent.size(), signature)); // signerInfo SignerInfo ReturnErrorOnFailure(EncodeSignerInfo(signerKeyId, signature, writer)); } ASN1_END_SEQUENCE; } ASN1_END_CONSTRUCTED; } ASN1_END_SEQUENCE; signedMessage.reduce_size(writer.GetLengthWritten()); exit: return err; } CHIP_ERROR CMS_Verify(const ByteSpan & signedMessage, const ByteSpan & signerX509Cert, ByteSpan & cdContent) { P256PublicKey signerPubkey; ReturnErrorOnFailure(ExtractPubkeyFromX509Cert(signerX509Cert, signerPubkey)); return CMS_Verify(signedMessage, signerPubkey, cdContent); } CHIP_ERROR CMS_Verify(const ByteSpan & signedMessage, const Crypto::P256PublicKey & signerPubkey, ByteSpan & cdContent) { CHIP_ERROR err = CHIP_NO_ERROR; ASN1Reader reader; uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast(signedMessage.size()); reader.Init(signedMessage.data(), size); // SignedData ::= SEQUENCE ASN1_PARSE_ENTER_SEQUENCE { ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId); // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } // OID identifies the CMS signed-data content type VerifyOrReturnError( ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_ContentType_PKCS7SignedData)), ASN1_ERROR_UNSUPPORTED_ENCODING); // version [0] EXPLICIT Version DEFAULT v3 ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { ASN1_PARSE_ENTER_SEQUENCE { // Version ::= INTEGER { v3(3) } int64_t version; ASN1_PARSE_INTEGER(version); // Verify that the CMS version is v3 VerifyOrExit(version == 3, err = ASN1_ERROR_UNSUPPORTED_ENCODING); // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_PARSE_ENTER_SET { ASN1_PARSE_ENTER_SEQUENCE { ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId); VerifyOrReturnError( ByteSpan(reader.GetValue(), reader.GetValueLen()).data_equal(ByteSpan(sOID_DigestAlgo_SHA256)), ASN1_ERROR_UNSUPPORTED_ENCODING); } ASN1_EXIT_SEQUENCE; } ASN1_EXIT_SET; // encapContentInfo EncapsulatedContentInfo ReturnErrorOnFailure(DecodeEncapsulatedContent(reader, cdContent)); // signerInfo SignerInfo ByteSpan signerKeyId; P256ECDSASignature signature; ReturnErrorOnFailure(DecodeSignerInfo(reader, signerKeyId, signature)); // Validate CD Signature ReturnErrorOnFailure(signerPubkey.ECDSA_validate_msg_signature(cdContent.data(), cdContent.size(), signature)); } ASN1_EXIT_SEQUENCE; } ASN1_EXIT_CONSTRUCTED; } ASN1_EXIT_SEQUENCE; exit: return err; } CHIP_ERROR CMS_ExtractKeyId(const ByteSpan & signedMessage, ByteSpan & signerKeyId) { CHIP_ERROR err = CHIP_NO_ERROR; ASN1Reader reader; uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast(signedMessage.size()); reader.Init(signedMessage.data(), size); // SignedData ::= SEQUENCE ASN1_PARSE_ENTER_SEQUENCE { // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } // OID identifies the CMS signed-data content type ASN1_PARSE_ANY; // version [0] EXPLICIT Version DEFAULT v3 ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { ASN1_PARSE_ENTER_SEQUENCE { // Version ::= INTEGER { v3(3) } ASN1_PARSE_ANY; // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_PARSE_ANY; // encapContentInfo EncapsulatedContentInfo ASN1_PARSE_ANY; // signerInfo SignerInfo P256ECDSASignature signature; ReturnErrorOnFailure(DecodeSignerInfo(reader, signerKeyId, signature)); } ASN1_EXIT_SEQUENCE; } ASN1_EXIT_CONSTRUCTED; } ASN1_EXIT_SEQUENCE; exit: return err; } CHIP_ERROR CMS_ExtractCDContent(const ByteSpan & signedMessage, ByteSpan & cdContent) { CHIP_ERROR err = CHIP_NO_ERROR; ASN1Reader reader; uint32_t size = signedMessage.size() > UINT32_MAX ? UINT32_MAX : static_cast(signedMessage.size()); reader.Init(signedMessage.data(), size); // SignedData ::= SEQUENCE ASN1_PARSE_ENTER_SEQUENCE { // id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } // OID identifies the CMS signed-data content type ASN1_PARSE_ANY; // version [0] EXPLICIT Version DEFAULT v3 ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { ASN1_PARSE_ENTER_SEQUENCE { // Version ::= INTEGER { v3(3) } ASN1_PARSE_ANY; // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_PARSE_ANY; // encapContentInfo EncapsulatedContentInfo ReturnErrorOnFailure(DecodeEncapsulatedContent(reader, cdContent)); // signerInfo SignerInfo ASN1_PARSE_ANY; } ASN1_EXIT_SEQUENCE; } ASN1_EXIT_CONSTRUCTED; } ASN1_EXIT_SEQUENCE; exit: return err; } } // namespace Credentials } // namespace chip