Skip to content

Commit

Permalink
Added Certificate Declaration payload validation (#11838)
Browse files Browse the repository at this point in the history
* Added Certificate Declaration payload validation

* Additional Certificate Declaration payload validation changes

* Additional Certificate Declaration payload validation changes to incorporate PR-11838 review comments.

* Added default constructor for CertificationElementsWithoutPIDs struct

* Made PAI Certificate mandatory in VerifyAttestationInformation method
  • Loading branch information
vijs authored Nov 23, 2021
1 parent 2490197 commit 098e736
Show file tree
Hide file tree
Showing 11 changed files with 555 additions and 49 deletions.
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

0 comments on commit 098e736

Please sign in to comment.