Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Certificate Declaration payload validation #11838

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 122 additions & 1 deletion src/credentials/CertificationDeclaration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, Cer
TLVReader reader;
TLVType outerContainer1, outerContainer2;

VerifyOrReturnError(encodedCertElements.size() <= kMaxCMSSignedCDMessage, CHIP_ERROR_INVALID_ARGUMENT);

reader.Init(encodedCertElements);

ReturnErrorOnFailure(reader.Next(kTLVType_Structure, AnonymousTag));
Expand Down Expand Up @@ -165,7 +167,7 @@ CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, Cer

err = reader.Next();
}
VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
VerifyOrReturnError(err == CHIP_END_OF_TLV || err == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT || err == CHIP_NO_ERROR, err);

ReturnErrorOnFailure(reader.ExitContainer(outerContainer1));

Expand All @@ -174,6 +176,125 @@ CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, Cer
return CHIP_NO_ERROR;
}

CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElementsWithoutPIDs & certDeclContent)
{
CHIP_ERROR err;
TLVReader reader;
TLVType outerContainer;

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)));

// skip PID Array

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);

ReturnErrorOnFailure(reader.ExitContainer(outerContainer));

ReturnErrorOnFailure(reader.VerifyEndOfContainer());

return CHIP_NO_ERROR;
}

bool CertificationElementsDecoder::IsProductIdIn(const ByteSpan & encodedCertElements, uint16_t productId)
{
VerifyOrReturnError(PrepareToReadProductIdList(encodedCertElements) == CHIP_NO_ERROR, false);

uint16_t cdProductId = 0;
CHIP_ERROR error = CHIP_NO_ERROR;

while ((error = GetNextProductId(cdProductId)) == CHIP_NO_ERROR)
{
if (productId == cdProductId)
{
return true;
}
}

return false;
}

CHIP_ERROR CertificationElementsDecoder::PrepareToReadProductIdList(const ByteSpan & encodedCertElements)
{
mIsInitialized = false;
mCertificationDeclarationData = encodedCertElements;

mReader.Init(mCertificationDeclarationData);
ReturnErrorOnFailure(mReader.Next(kTLVType_Structure, AnonymousTag));
ReturnErrorOnFailure(mReader.EnterContainer(mOuterContainerType1));

// position to ProductId Array
CHIP_ERROR error = CHIP_NO_ERROR;
do
{
error = mReader.Next(kTLVType_Array, ContextTag(kTag_ProductIdArray));
// return error code if Next method returned different than CHIP_NO_ERROR.
// also return if different error code than CHIP_ERROR_WRONG_TLV_TYPE/CHIP_ERROR_UNEXPECTED_TLV_ELEMENT, which means that
// the expected type and tags do not match.
VerifyOrReturnError(
error == CHIP_NO_ERROR || error == CHIP_ERROR_WRONG_TLV_TYPE || error == CHIP_ERROR_UNEXPECTED_TLV_ELEMENT, error);
} while (error != CHIP_NO_ERROR);

ReturnErrorOnFailure(mReader.EnterContainer(mOuterContainerType2));

mIsInitialized = true;
return CHIP_NO_ERROR;
}

CHIP_ERROR CertificationElementsDecoder::GetNextProductId(uint16_t & productId)
{
VerifyOrReturnError(mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(mReader.Next(AnonymousTag));
ReturnErrorOnFailure(mReader.Get(productId));
return CHIP_NO_ERROR;
}

namespace {

CHIP_ERROR EncodeEncapsulatedContent(const ByteSpan & cdContent, ASN1Writer & writer)
Expand Down
42 changes: 42 additions & 0 deletions src/credentials/CertificationDeclaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <crypto/CHIPCryptoPAL.h>
#include <lib/asn1/ASN1.h>
#include <lib/asn1/ASN1Macros.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/Span.h>

Expand Down Expand Up @@ -62,6 +63,37 @@ struct CertificationElements
bool DACOriginVIDandPIDPresent;
};

struct CertificationElementsWithoutPIDs
{
uint16_t formatVersion = 0;
uint16_t vendorId = VendorId::NotSpecified;
uint32_t deviceTypeId = 0;
uint8_t securityLevel = 0;
uint16_t securityInformation = 0;
uint16_t versionNumber = 0;
uint8_t certificationType = 0;
uint16_t dacOriginVendorId = VendorId::NotSpecified;
uint16_t dacOriginProductId = 0;
bool dacOriginVIDandPIDPresent = false;
char certificateId[kCertificateIdLength + 1] = { 0 };
};

class CertificationElementsDecoder
{
public:
bool IsProductIdIn(const ByteSpan & encodedCertElements, uint16_t productId);

private:
CHIP_ERROR PrepareToReadProductIdList(const ByteSpan & encodedCertElements);
CHIP_ERROR GetNextProductId(uint16_t & productId);

ByteSpan mCertificationDeclarationData;
bool mIsInitialized = false;
TLV::TLVReader mReader;
TLV::TLVType mOuterContainerType1 = TLV::kTLVType_Structure;
TLV::TLVType mOuterContainerType2 = TLV::kTLVType_Structure;
};

/**
* @brief Encode certification elements in TLV format.
*
Expand All @@ -82,6 +114,16 @@ CHIP_ERROR EncodeCertificationElements(const CertificationElements & certElement
**/
CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElements & certElements);

/**
* @brief Decode certification elements from TLV encoded structure.
*
* @param[in] encodedCertElements A byte span to read the TLV encoded certification elements.
* @param[out] certDeclContent Decoded Certification Declaration Content.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR DecodeCertificationElements(const ByteSpan & encodedCertElements, CertificationElementsWithoutPIDs & certDeclContent);

/**
* @brief Generate CMS signed message.
*
Expand Down
10 changes: 10 additions & 0 deletions src/credentials/DeviceAttestationVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ class UnimplementedDACVerifier : public DeviceAttestationVerifier
(void) certDeclBuffer;
return AttestationVerificationResult::kNotImplemented;
}

AttestationVerificationResult ValidateCertificateDeclarationPayload(const ByteSpan & certDeclBuffer,
const ByteSpan & firmwareInfo,
const DeviceInfoForAttestation & deviceInfo) override
{
(void) certDeclBuffer;
(void) firmwareInfo;
(void) deviceInfo;
return AttestationVerificationResult::kNotImplemented;
}
};

// Default to avoid nullptr on getter and cleanly handle new products/clients before
Expand Down
38 changes: 37 additions & 1 deletion src/credentials/DeviceAttestationVerifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPError.h>
#include <lib/core/CHIPVendorIdentifiers.hpp>
#include <lib/support/Span.h>

namespace chip {
Expand Down Expand Up @@ -60,9 +61,14 @@ enum class AttestationVerificationResult : uint16_t
kCertificationDeclarationNoKeyId = 600,
kCertificationDeclarationNoCertificateFound = 601,
kCertificationDeclarationInvalidSignature = 602,
kCertificationDeclarationInvalidFormat = 603,
kCertificationDeclarationInvalidVendorId = 604,
kCertificationDeclarationInvalidProductId = 605,

kNoMemory = 700,

kInvalidArgument = 800,

kNotImplemented = 0xFFFFU,

// TODO: Add more attestation verification errors
Expand All @@ -75,6 +81,24 @@ enum CertificateType : uint8_t
kPAI = 2,
};

struct DeviceInfoForAttestation
{
// Vendor ID reported by device in Basic Information cluster
uint16_t vendorId = VendorId::NotSpecified;
// Product ID reported by device in Basic Information cluster
uint16_t productId = 0;
// Vendor ID from DAC
uint16_t dacVendorId = VendorId::NotSpecified;
// Product ID from DAC
uint16_t dacProductId = 0;
// Vendor ID from PAI cert
uint16_t paiVendorId = VendorId::NotSpecified;
// Product ID from PAI cert (0 if absent)
uint16_t paiProductId = 0;
// Vendor ID from PAA cert
uint16_t paaVendorId = VendorId::NotSpecified;
};

class DeviceAttestationVerifier
{
public:
Expand Down Expand Up @@ -117,7 +141,19 @@ class DeviceAttestationVerifier
virtual AttestationVerificationResult ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer,
ByteSpan & certDeclBuffer) = 0;

// TODO: Validate Certification Declaration Payload
/**
* @brief Verify a CMS Signed Data Payload against the Basic Information Cluster and DAC/PAI's Vendor and Product IDs
*
* @param[in] certDeclBuffer A ByteSpan with the Certification Declaration content.
* @param[in] firmwareInfo A ByteSpan with the Firmware Information content.
* @param[in] deviceInfo
*
* @returns AttestationVerificationResult::kSuccess on success or another specific
* value from AttestationVerificationResult enum on failure.
*/
virtual AttestationVerificationResult ValidateCertificateDeclarationPayload(const ByteSpan & certDeclBuffer,
const ByteSpan & firmwareInfo,
const DeviceInfoForAttestation & deviceInfo) = 0;

// TODO: Validate Firmware Information

Expand Down
Loading