blob: 42bd9656b30750da36b1b4336453e923af7d08a8 [file] [log] [blame]
/*
*
* 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 <algorithm>
#include <cinttypes>
#include <cstddef>
#include <credentials/CertificationDeclaration.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/TLV.h>
#include <lib/support/SafeInt.h>
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<uint16_t>(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<uint16_t>(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;
uint8_t asn1SignatureBuf[kMax_ECDSA_Signature_Length_Der];
MutableByteSpan asn1Signature(asn1SignatureBuf);
ReturnErrorOnFailure(EcdsaRawSignatureToAsn1(kP256_FE_Length, signature.Span(), asn1Signature));
// signature OCTET STRING
ReturnErrorOnFailure(writer.PutOctetString(asn1Signature.data(), static_cast<uint16_t>(asn1Signature.size())));
}
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<uint32_t>(std::min(static_cast<size_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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