blob: 447ff61a5a1576a1564879fdc04518e700323562 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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.
*/
#include "DeviceAttestationConstructor.h"
#include <lib/core/CHIPTLV.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <cstdint>
namespace chip {
namespace Credentials {
// context tag positions
enum : uint32_t
{
kCertificationDeclarationTagId = 1,
kAttestationNonceTagId = 2,
kTimestampTagId = 3,
kFirmwareInfoTagId = 4,
};
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)
{
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;
firmwareInfo = ByteSpan();
tlvReader.Init(attestationElements);
ReturnErrorOnFailure(tlvReader.Next(containerType, TLV::AnonymousTag));
ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
CHIP_ERROR error = CHIP_NO_ERROR;
// TODO: per conversation with Tennessee, shold be two consecutive loops (rather than one big
// loop, since the contextTags come before the profileTags)
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
{
return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT;
}
}
vendorReservedArraySize = vendorReservedIdx;
VerifyOrReturnError(error == CHIP_END_OF_TLV, error);
VerifyOrReturnError(lastContextTagId != UINT32_MAX, CHIP_ERROR_MISSING_TLV_ELEMENT);
VerifyOrReturnError(certificationDeclarationExists && attestationNonceExists && timestampExists,
CHIP_ERROR_MISSING_TLV_ELEMENT);
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,
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;
ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, outerContainerType));
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(1), certificationDeclaration));
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), attestationNonce));
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(3), timestamp));
if (!firmwareInfo.empty())
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(4), firmwareInfo));
}
uint8_t vendorTagNum = 1;
for (size_t vendorReservedIdx = 0; vendorReservedIdx < vendorReservedArraySize; ++vendorReservedIdx)
{
if (!vendorReservedArray[vendorReservedIdx].empty())
{
ReturnErrorOnFailure(
tlvWriter.Put(TLV::ProfileTag(vendorId, profileNum, vendorTagNum), vendorReservedArray[vendorReservedIdx]));
}
vendorTagNum++;
}
ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
ReturnErrorOnFailure(tlvWriter.Finalize());
attestationElements = attestationElements.SubSpan(0, tlvWriter.GetLengthWritten());
return CHIP_NO_ERROR;
}
} // namespace Credentials
} // namespace chip