Skip to content

Commit

Permalink
Issue #9825 -- handle vendor reserved data properly (#10546)
Browse files Browse the repository at this point in the history
* added credentials/DeviceAttestationVendorReserved.h

added classes for deconstruction and construction for VendorReserved
data

changed calls for device attestation for new signatures

* coauthor: restyled - whitespace

* added credentials/DeviceAttestationVendorReserved.h

added classes for deconstruction and construction for VendorReserved
data

changed calls for device attestation for new signatures

* coauthor: restyled - whitespace

* Update src/credentials/DeviceAttestationConstructor.cpp

Co-authored-by: Boris Zbarsky <[email protected]>

* review updates

made sure private is after public

private data items are prefixed with 'm'

* review comment, used strlen instead of hardcoded number,
compute bytespans based on strlen instead of size of arrays
test err code from to many added elements (CHIP_ERROR_NO_MEMORY instread of
!CHIP_NO_ERROR)

* coauthor: clang-format

* per review, replace Init and SaveAttestationElements in
DeviceAttestationsElementsDeconstructor  with
PrepareToReadVendorReservedElements.

* Incorporated PR 10546 review comments

* Incorporated PR 10546 review comments (part 2)

* Addressed PR 10546 review comments (11/10)

Co-authored-by: Boris Zbarsky <[email protected]>
Co-authored-by: Vijay Selvaraj <[email protected]>
Co-authored-by: Vijay Selvaraj <[email protected]>
  • Loading branch information
4 people authored Nov 12, 2021
1 parent 5d4bade commit 3c750d3
Show file tree
Hide file tree
Showing 7 changed files with 514 additions and 240 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -552,10 +552,7 @@ bool emberAfOperationalCredentialsClusterAttestationRequestCallback(app::Command
// TODO: retrieve vendor information to populate the fields below.
uint32_t timestamp = 0;
ByteSpan firmwareInfo;
ByteSpan * vendorReservedArray = nullptr;
size_t vendorReservedArraySize = 0;
uint16_t vendorId = 0;
uint16_t profileNum = 0;
Credentials::DeviceAttestationVendorReservedConstructor emptyVendorReserved(nullptr, 0);

SuccessOrExit(err = dacProvider->GetCertificationDeclaration(certDeclSpan));
// TODO: Retrieve firmware Information
Expand All @@ -565,8 +562,7 @@ bool emberAfOperationalCredentialsClusterAttestationRequestCallback(app::Command

MutableByteSpan attestationElementsSpan(attestationElements.Get(), attestationElementsLen);
SuccessOrExit(err = Credentials::ConstructAttestationElements(certDeclSpan, attestationNonce, timestamp, firmwareInfo,
vendorReservedArray, vendorReservedArraySize, vendorId,
profileNum, attestationElementsSpan));
emptyVendorReserved, attestationElementsSpan));
attestationElementsLen = attestationElementsSpan.size();
}

Expand Down
1 change: 1 addition & 0 deletions src/credentials/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ static_library("credentials") {
"DeviceAttestationConstructor.h",
"DeviceAttestationCredsProvider.cpp",
"DeviceAttestationCredsProvider.h",
"DeviceAttestationVendorReserved.h",
"DeviceAttestationVerifier.cpp",
"DeviceAttestationVerifier.h",
"GenerateChipX509Cert.cpp",
Expand Down
161 changes: 72 additions & 89 deletions src/credentials/DeviceAttestationConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/
#include "DeviceAttestationConstructor.h"
#include "DeviceAttestationVendorReserved.h"

#include <lib/core/CHIPTLV.h>
#include <lib/support/CodeUtils.h>
Expand All @@ -34,16 +35,40 @@ enum : uint32_t
kFirmwareInfoTagId = 4,
};

// utility to determine number of Vendor Reserved elements in a bytespan
CHIP_ERROR CountVendorReservedElementsInDA(const ByteSpan & attestationElements, size_t & numOfElements)
{
TLV::ContiguousBufferTLVReader tlvReader;
TLV::TLVType containerType = TLV::kTLVType_Structure;

tlvReader.Init(attestationElements);
ReturnErrorOnFailure(tlvReader.Next(containerType, TLV::AnonymousTag));
ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));

size_t count = 0;
CHIP_ERROR error;
while ((error = tlvReader.Next()) == CHIP_NO_ERROR)
{
uint64_t tag = tlvReader.GetTag();
if (TLV::IsProfileTag(tag))
{
count++;
}
}
VerifyOrReturnError(error == CHIP_NO_ERROR || error == CHIP_END_OF_TLV, error);

numOfElements = count;
return CHIP_NO_ERROR;
}

CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements, ByteSpan & certificationDeclaration,
ByteSpan & attestationNonce, uint32_t & timestamp, ByteSpan & firmwareInfo,
ByteSpan * vendorReservedArray, size_t & vendorReservedArraySize, uint16_t & vendorId,
uint16_t & profileNum)
DeviceAttestationVendorReservedDeconstructor & vendorReserved)
{
bool certificationDeclarationExists = false;
bool attestationNonceExists = false;
bool timestampExists = false;
bool firmwareInfoExists = false;
size_t vendorReservedIdx = 0;
uint32_t lastContextTagId = UINT32_MAX;
TLV::ContiguousBufferTLVReader tlvReader;
TLV::TLVType containerType = TLV::kTLVType_Structure;
Expand All @@ -54,110 +79,72 @@ CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements,
ReturnErrorOnFailure(tlvReader.Next(containerType, TLV::AnonymousTag));
ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));

CHIP_ERROR error = CHIP_NO_ERROR;
CHIP_ERROR error;

// TODO: per conversation with Tennessee, shold be two consecutive loops (rather than one big
// loop, since the contextTags come before the profileTags)
// process context tags first (should be in sorted order)
while ((error = tlvReader.Next()) == CHIP_NO_ERROR)
{
TLV::Tag tag = tlvReader.GetTag();

if (TLV::IsContextTag(tag))
{
switch (TLV::TagNumFromTag(tag))
{
case kCertificationDeclarationTagId:
VerifyOrReturnError(lastContextTagId == UINT32_MAX, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(certificationDeclarationExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(certificationDeclaration));
certificationDeclarationExists = true;
break;
case kAttestationNonceTagId:
VerifyOrReturnError(lastContextTagId == kCertificationDeclarationTagId,
CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(attestationNonceExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(attestationNonce));
attestationNonceExists = true;
break;
case kTimestampTagId:
VerifyOrReturnError(lastContextTagId == kAttestationNonceTagId, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(timestampExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.Get(timestamp));
timestampExists = true;
break;
case kFirmwareInfoTagId:
VerifyOrReturnError(lastContextTagId == kTimestampTagId, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(firmwareInfoExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(firmwareInfo));
firmwareInfoExists = true;
break;
default:
return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT;
}

lastContextTagId = TLV::TagNumFromTag(tag);
}
else if (TLV::IsProfileTag(tag))
{
// vendor fields
bool seenProfile = false;
uint16_t currentVendorId;
uint16_t currentProfileNum;

currentVendorId = TLV::VendorIdFromTag(tag);
currentProfileNum = TLV::ProfileNumFromTag(tag);
if (!seenProfile)
{
seenProfile = true;
vendorId = currentVendorId;
profileNum = currentProfileNum;
}
else
{
// TODO: do not check for this - map vendorId and profileNum to each Vendor Reserved entry
// check that vendorId and profileNum match in every Vendor Reserved entry
VerifyOrReturnError(currentVendorId == vendorId && currentProfileNum == profileNum,
CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
}

ByteSpan vendorReservedEntry;
ReturnErrorOnFailure(tlvReader.GetByteView(vendorReservedEntry));
VerifyOrReturnError(vendorReservedIdx < vendorReservedArraySize, CHIP_ERROR_NO_MEMORY);
vendorReservedArray[vendorReservedIdx++] = vendorReservedEntry;
}
else
if (!TLV::IsContextTag(tag))
break;

switch (TLV::TagNumFromTag(tag))
{
case kCertificationDeclarationTagId:
VerifyOrReturnError(lastContextTagId == UINT32_MAX, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(certificationDeclarationExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(certificationDeclaration));
certificationDeclarationExists = true;
break;
case kAttestationNonceTagId:
VerifyOrReturnError(lastContextTagId == kCertificationDeclarationTagId, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(attestationNonceExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(attestationNonce));
attestationNonceExists = true;
break;
case kTimestampTagId:
VerifyOrReturnError(lastContextTagId == kAttestationNonceTagId, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(timestampExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.Get(timestamp));
timestampExists = true;
break;
case kFirmwareInfoTagId:
VerifyOrReturnError(lastContextTagId == kTimestampTagId, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
VerifyOrReturnError(firmwareInfoExists == false, CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT);
ReturnErrorOnFailure(tlvReader.GetByteView(firmwareInfo));
firmwareInfoExists = true;
break;
default:
return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT;
}

lastContextTagId = TLV::TagNumFromTag(tag);
}

vendorReservedArraySize = vendorReservedIdx;
VerifyOrReturnError(error == CHIP_NO_ERROR || error == CHIP_END_OF_TLV, error);

VerifyOrReturnError(error == CHIP_END_OF_TLV, error);
VerifyOrReturnError(lastContextTagId != UINT32_MAX, CHIP_ERROR_MISSING_TLV_ELEMENT);
VerifyOrReturnError(certificationDeclarationExists && attestationNonceExists && timestampExists,
VerifyOrReturnError(lastContextTagId == kTimestampTagId || lastContextTagId == kFirmwareInfoTagId,
CHIP_ERROR_MISSING_TLV_ELEMENT);

size_t count = 0;
ReturnErrorOnFailure(CountVendorReservedElementsInDA(attestationElements, count));
ReturnErrorOnFailure(vendorReserved.PrepareToReadVendorReservedElements(attestationElements, count));
return CHIP_NO_ERROR;
}

// TODO: have independent vendorId and profileNum entries map to each vendor Reserved entry
// Have a class for vendor reserved data, discussed in:
// https://github.com/project-chip/connectedhomeip/issues/9825
CHIP_ERROR ConstructAttestationElements(const ByteSpan & certificationDeclaration, const ByteSpan & attestationNonce,
uint32_t timestamp, const ByteSpan & firmwareInfo, ByteSpan * vendorReservedArray,
size_t vendorReservedArraySize, uint16_t vendorId, uint16_t profileNum,
uint32_t timestamp, const ByteSpan & firmwareInfo,
DeviceAttestationVendorReservedConstructor & vendorReserved,
MutableByteSpan & attestationElements)
{
TLV::TLVWriter tlvWriter;
TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;

VerifyOrReturnError(!certificationDeclaration.empty() && !attestationNonce.empty(), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(attestationNonce.size() == 32, CHIP_ERROR_INVALID_ARGUMENT);
if (vendorReservedArraySize != 0)
{
VerifyOrReturnError(vendorReservedArray != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
}

tlvWriter.Init(attestationElements.data(), static_cast<uint32_t>(attestationElements.size()));
outerContainerType = TLV::kTLVType_NotSpecified;
Expand All @@ -170,15 +157,11 @@ CHIP_ERROR ConstructAttestationElements(const ByteSpan & certificationDeclaratio
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(4), firmwareInfo));
}

uint8_t vendorTagNum = 1;
for (size_t vendorReservedIdx = 0; vendorReservedIdx < vendorReservedArraySize; ++vendorReservedIdx)
const VendorReservedElement * element = vendorReserved.cbegin();
while ((element = vendorReserved.Next()) != nullptr)
{
if (!vendorReservedArray[vendorReservedIdx].empty())
{
ReturnErrorOnFailure(
tlvWriter.Put(TLV::ProfileTag(vendorId, profileNum, vendorTagNum), vendorReservedArray[vendorReservedIdx]));
}
vendorTagNum++;
ReturnErrorOnFailure(
tlvWriter.Put(TLV::ProfileTag(element->vendorId, element->profileNum, element->tagNum), element->vendorReservedData));
}

ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
Expand Down
29 changes: 15 additions & 14 deletions src/credentials/DeviceAttestationConstructor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
*/
#pragma once

#include <credentials/DeviceAttestationVendorReserved.h>
#include <lib/core/CHIPError.h>
#include <lib/support/Span.h>

#include <vector>

namespace chip {
namespace Credentials {

Expand All @@ -34,15 +33,11 @@ namespace Credentials {
* @param[out] timestamp
* @param[out] firmwareInfo ByteSpan containing Firmware Information data if present within attestationElements.
* Empty ByteSpan if not present in attestationElements.
* @param[out] vendorReservedArray
* @param[inout] vendorReservedArraySize
* @param[out] vendorId Vendor ID fetched from Attestation Elements data.
* @param[out] profileNum Profile Number fetched from Attestation Elements data.
* @param[out] VendorReserved Placeholder to for client to examine VendorReserved elements later
*/
CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements, ByteSpan & certificationDeclaration,
ByteSpan & attestationNonce, uint32_t & timestamp, ByteSpan & firmwareInfo,
ByteSpan * vendorReservedArray, size_t & vendorReservedArraySize, uint16_t & vendorId,
uint16_t & profileNum);
DeviceAttestationVendorReservedDeconstructor & vendorReserved);

/**
* @brief Take each component separately and form the Attestation Elements buffer.
Expand All @@ -51,17 +46,23 @@ CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements,
* @param[in] attestationNonce Attestation Nonce - 32 octets required.
* @param[in] timestamp Timestamp data in epoch time format.
* @param[in] firmwareInfo Optional Firmware Information data - Can be empty.
* @param[in] vendorReservedArray Array of Vendor Reserved entries.
* @param[in] vendorReservedArraySize Number of Vendor Reserved entries present in the array.
* @param[in] vendorId Vendor ID to be written to Vendor Reserved entries' Qualified Tags
* @param[in] profileNum Profile Number to be written to Vendor Reserved entries' Qualified Tags
* @param[in] VendorReserved Prefilled-in vendor reserved elements to be put into DA elements.
* @param[out] attestationElements Buffer used to write all AttestationElements data, formed with all the data fields above.
* Provided buffer needs to be capable to handle all data fields + tags.
*/
CHIP_ERROR ConstructAttestationElements(const ByteSpan & certificationDeclaration, const ByteSpan & attestationNonce,
uint32_t timestamp, const ByteSpan & firmwareInfo, ByteSpan * vendorReservedArray,
size_t vendorReservedArraySize, uint16_t vendorId, uint16_t profileNum,
uint32_t timestamp, const ByteSpan & firmwareInfo,
DeviceAttestationVendorReservedConstructor & vendorReserved,
MutableByteSpan & attestationElements);

/***
* @brief Count the number of VendorReservedElements in a DeviceAttestation blob
*
* @param[in] attestationElements ByeSpan conitaining source of Attestation Elements data
* @param[out]
* @returns CHIP_NO_ERROR on success
*/
CHIP_ERROR CountVendorReservedElementsInDA(const ByteSpan & attestationElements, size_t & numElements);

} // namespace Credentials
} // namespace chip
Loading

0 comments on commit 3c750d3

Please sign in to comment.