blob: c2f73187cac3ff63e3a22ff0d4efafcea384a269 [file] [log] [blame]
/*
* Copyright (c) 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.
*
*/
#include "DecoderCustomLog.h"
#include "../logging/Log.h"
#include <app/AttributeAccessInterface.h>
#include <app/MessageDef/InvokeRequestMessage.h>
#include <app/MessageDef/InvokeResponseMessage.h>
#include <app/MessageDef/ReportDataMessage.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app-common/zap-generated/ids/Commands.h>
#include <credentials/CertificationDeclaration.h>
#include <credentials/DeviceAttestationConstructor.h>
#include <credentials/DeviceAttestationVendorReserved.h>
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/TypeTraits.h>
namespace {
template <typename T>
bool IsTag(const chip::TLV::TLVReader & reader, T tag)
{
return chip::to_underlying(tag) == chip::TLV::TagNumFromTag(reader.GetTag());
}
} // namespace
namespace chip {
namespace trace {
namespace interaction_model {
using namespace logging;
CHIP_ERROR LogCertificateChainResponse(TLV::TLVReader & reader)
{
app::Clusters::OperationalCredentials::Commands::CertificateChainResponse::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
LogCertificate("DAC/PAI", value.certificate);
return CHIP_NO_ERROR;
}
CHIP_ERROR LogCertificationDeclaration(const ByteSpan & cd)
{
auto scopedIndent = ScopedLogIndent("Certification Declaration");
// TODO Add an option to load a TrustStore so the subjectKeyId can be extracted from the CMS envelope in order
// to select the proper public key.
ByteSpan kid;
ReturnErrorOnFailure(Credentials::CMS_ExtractKeyId(cd, kid));
Crypto::P256PublicKey verifyingKey;
Credentials::CsaCdKeysTrustStore cdKeysTrustStore;
ReturnErrorOnFailure(cdKeysTrustStore.LookupVerifyingKey(kid, verifyingKey));
ByteSpan cdContentOut;
ReturnErrorOnFailure(Credentials::CMS_Verify(cd, verifyingKey, cdContentOut));
constexpr uint8_t kTag_FormatVersion = 0; /**< [ unsigned int ] Format version. */
constexpr uint8_t kTag_VendorId = 1; /**< [ unsigned int ] Vedor identifier. */
constexpr uint8_t kTag_ProductIdArray = 2; /**< [ array ] Product identifiers (each is unsigned int). */
constexpr uint8_t kTag_DeviceTypeId = 3; /**< [ unsigned int ] Device Type identifier. */
constexpr uint8_t kTag_CertificateId = 4; /**< [ UTF-8 string, length 19 ] Certificate identifier. */
constexpr uint8_t kTag_SecurityLevel = 5; /**< [ unsigned int ] Security level. */
constexpr uint8_t kTag_SecurityInformation = 6; /**< [ unsigned int ] Security information. */
constexpr uint8_t kTag_VersionNumber = 7; /**< [ unsigned int ] Version number. */
constexpr uint8_t kTag_CertificationType = 8; /**< [ unsigned int ] Certification Type. */
constexpr uint8_t kTag_DACOriginVendorId = 9; /**< [ unsigned int, optional ] DAC origin vendor identifier. */
constexpr uint8_t kTag_DACOriginProductId = 10; /**< [ unsigned int, optional ] DAC origin product identifier. */
constexpr uint8_t kTag_AuthorizedPAAList = 11; /**< [ array, optional ] Authorized PAA List. */
TLV::TLVReader reader;
reader.Init(cdContentOut);
TLV::TLVType containerType = TLV::kTLVType_Structure;
ReturnErrorOnFailure(reader.Next(containerType, TLV::AnonymousTag()));
ReturnErrorOnFailure(reader.EnterContainer(containerType));
uint16_t value;
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
switch (TLV::TagNumFromTag(reader.GetTag()))
{
case kTag_FormatVersion:
ReturnErrorOnFailure(reader.Get(value));
Log("Format Version", value);
break;
case kTag_VendorId:
ReturnErrorOnFailure(reader.Get(value));
Log("Vendor Id", value);
break;
case kTag_ProductIdArray: {
TLV::TLVType arrayType = TLV::kTLVType_Array;
ReturnErrorOnFailure(reader.EnterContainer(arrayType));
auto scopedProductIndent = ScopedLogIndent("Product Ids");
while (CHIP_NO_ERROR == (err = reader.Next()))
{
ReturnErrorOnFailure(reader.Get(value));
Log("Product Id", value);
}
ReturnErrorOnFailure(reader.ExitContainer(arrayType));
break;
}
case kTag_DeviceTypeId:
ReturnErrorOnFailure(reader.Get(value));
Log("Device Type Id", value);
break;
case kTag_CertificateId: {
CharSpan str;
ReturnErrorOnFailure(reader.Get(str));
Log("Certificate Id", str);
break;
}
case kTag_SecurityLevel:
ReturnErrorOnFailure(reader.Get(value));
Log("Security Level", value);
break;
case kTag_SecurityInformation:
ReturnErrorOnFailure(reader.Get(value));
Log("Security Information", value);
break;
case kTag_VersionNumber:
ReturnErrorOnFailure(reader.Get(value));
Log("Version Number", value);
break;
case kTag_CertificationType:
ReturnErrorOnFailure(reader.Get(value));
Log("Certification Type", value);
break;
case kTag_DACOriginVendorId:
ReturnErrorOnFailure(reader.Get(value));
Log("DAC Origin Vendor Id", value);
break;
case kTag_DACOriginProductId:
ReturnErrorOnFailure(reader.Get(value));
Log("DAC Origin Product Id", value);
break;
case kTag_AuthorizedPAAList: {
TLV::TLVType arrayType = TLV::kTLVType_Array;
ReturnErrorOnFailure(reader.EnterContainer(arrayType));
auto scopedAuthorizedPAAIndent = ScopedLogIndent("Authorized PAAs");
while (CHIP_NO_ERROR == (err = reader.Next()))
{
ByteSpan str;
ReturnErrorOnFailure(reader.Get(str));
Log("Product Id", str);
}
ReturnErrorOnFailure(reader.ExitContainer(arrayType));
break;
}
default:
Log("<Unsupported Tag>");
break;
}
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
return CHIP_NO_ERROR;
}
CHIP_ERROR LogAttestationResponse(TLV::TLVReader & reader)
{
app::Clusters::OperationalCredentials::Commands::AttestationResponse::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
ByteSpan certificationDeclaration;
ByteSpan attestationNonce;
uint32_t timestamp;
ByteSpan firmwareInfo;
Credentials::DeviceAttestationVendorReservedDeconstructor vendorReserved;
ReturnErrorOnFailure(Credentials::DeconstructAttestationElements(value.attestationElements, certificationDeclaration,
attestationNonce, timestamp, firmwareInfo, vendorReserved));
auto scopedIndent = ScopedLogIndentWithSize("Attestation Elements", value.attestationElements.size());
ReturnErrorOnFailure(LogCertificationDeclaration(certificationDeclaration));
Log("Attestation Nonce", attestationNonce);
Log("Timestamp", timestamp);
Log("Firmware Info", firmwareInfo);
if (vendorReserved.GetNumberOfElements())
{
auto scopedVendorIndent = ScopedLogIndent("Vendors Reserved");
Credentials::VendorReservedElement element;
while (vendorReserved.GetNextVendorReservedElement(element) == CHIP_NO_ERROR)
{
auto scopedVendorReservedIndent = ScopedLogIndent("Vendor Reserved");
Log("Vendor Id", element.vendorId);
Log("Profile Number", element.profileNum);
Log("Profile Data", element.vendorReservedData);
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR LogCSRResponse(TLV::TLVReader & reader)
{
app::Clusters::OperationalCredentials::Commands::CSRResponse::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
ByteSpan csr;
ByteSpan csrNonce;
ByteSpan vendorReserved1;
ByteSpan vendorReserved2;
ByteSpan vendorReserved3;
ReturnErrorOnFailure(Credentials::DeconstructNOCSRElements(value.NOCSRElements, csr, csrNonce, vendorReserved1, vendorReserved2,
vendorReserved3));
{
auto scopedIndent = ScopedLogIndent("NOCSR Elements");
Log("CSR", csr);
Log("CSRNonce", csrNonce);
Log("Vendor Reserved 1", vendorReserved1);
Log("Vendor Reserved 2", vendorReserved2);
Log("Vendor Reserved 3", vendorReserved3);
}
LogCertificateRequest("CSR", csr);
return CHIP_NO_ERROR;
}
CHIP_ERROR LogAddNOC(TLV::TLVReader & reader)
{
app::Clusters::OperationalCredentials::Commands::AddNOC::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
LogCertificate("NOCValue", value.NOCValue);
if (value.ICACValue.HasValue())
{
LogCertificate("ICACValue", value.ICACValue.Value());
}
return CHIP_NO_ERROR;
}
CHIP_ERROR LogAddTrustedRootCertificateRequest(TLV::TLVReader & reader)
{
app::Clusters::OperationalCredentials::Commands::AddTrustedRootCertificate::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
LogCertificate("RCACValue", value.rootCertificate);
return CHIP_NO_ERROR;
}
CHIP_ERROR LogNOCsAttribute(TLV::TLVReader & reader)
{
if (reader.GetType() == TLV::kTLVType_Array)
{
return CHIP_NO_ERROR;
}
chip::app::Clusters::OperationalCredentials::Structs::NOCStruct::DecodableType value;
ReturnErrorOnFailure(chip::app::DataModel::Decode(reader, value));
LogCertificate("NOC", value.noc);
if (!value.icac.IsNull())
{
LogCertificate("ICAC", value.icac.Value());
}
return CHIP_NO_ERROR;
}
CHIP_ERROR MaybeDecodeAttributeData(TLV::TLVReader & reader)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ClusterId clusterId;
AttributeId attributeId;
TLV::TLVType containerType;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::AttributeDataIB::Tag::kPath))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (to_underlying(app::AttributePathIB::Tag::kCluster) == TLV::TagNumFromTag(reader.GetTag()))
{
reader.Get(clusterId);
}
else if (to_underlying(app::AttributePathIB::Tag::kAttribute) == TLV::TagNumFromTag(reader.GetTag()))
{
reader.Get(attributeId);
}
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
else if (IsTag(reader, app::AttributeDataIB::Tag::kData))
{
switch (clusterId)
{
case app::Clusters::OperationalCredentials::Id:
switch (attributeId)
{
case app::Clusters::OperationalCredentials::Attributes::NOCs::Id:
return LogNOCsAttribute(reader);
default:
return CHIP_NO_ERROR;
}
default:
return CHIP_NO_ERROR;
}
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR MaybeDecodeCommandData(TLV::TLVReader & reader)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ClusterId clusterId;
CommandId commandId;
TLV::TLVType containerType;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::CommandDataIB::Tag::kPath))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (to_underlying(app::CommandPathIB::Tag::kClusterId) == TLV::TagNumFromTag(reader.GetTag()))
{
reader.Get(clusterId);
}
else if (to_underlying(app::CommandPathIB::Tag::kCommandId) == TLV::TagNumFromTag(reader.GetTag()))
{
reader.Get(commandId);
}
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
else if (IsTag(reader, app::CommandDataIB::Tag::kFields))
{
switch (clusterId)
{
case app::Clusters::OperationalCredentials::Id:
switch (commandId)
{
case app::Clusters::OperationalCredentials::Commands::CertificateChainResponse::Id:
return LogCertificateChainResponse(reader);
case app::Clusters::OperationalCredentials::Commands::AttestationResponse::Id:
return LogAttestationResponse(reader);
case app::Clusters::OperationalCredentials::Commands::CSRResponse::Id:
return LogCSRResponse(reader);
case app::Clusters::OperationalCredentials::Commands::AddNOC::Id:
return LogAddNOC(reader);
case app::Clusters::OperationalCredentials::Commands::AddTrustedRootCertificate::Id:
return LogAddTrustedRootCertificateRequest(reader);
default:
return CHIP_NO_ERROR;
}
break;
default:
break;
}
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR MaybeDecodeNestedReadResponse(const uint8_t * data, size_t dataLen)
{
TLV::TLVReader reader;
reader.Init(data, dataLen);
TLV::TLVType containerType = TLV::kTLVType_Structure;
ReturnErrorOnFailure(reader.Next(containerType, TLV::AnonymousTag()));
ReturnErrorOnFailure(reader.EnterContainer(containerType));
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::ReportDataMessage::Tag::kAttributeReportIBs))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
TLV::TLVType arrayType = TLV::kTLVType_Array;
ReturnErrorOnFailure(reader.EnterContainer(arrayType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::AttributeReportIB::Tag::kAttributeData))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(MaybeDecodeAttributeData(reader));
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
}
ReturnErrorOnFailure(reader.ExitContainer(arrayType));
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
}
return reader.ExitContainer(containerType);
}
CHIP_ERROR MaybeDecodeNestedCommandResponse(const uint8_t * data, size_t dataLen)
{
TLV::TLVReader reader;
reader.Init(data, dataLen);
ReturnErrorOnFailure(reader.Next());
TLV::TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::InvokeResponseMessage::Tag::kInvokeResponses))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::InvokeResponseIB::Tag::kCommand))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(MaybeDecodeCommandData(reader));
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
}
return reader.ExitContainer(containerType);
}
CHIP_ERROR MaybeDecodeNestedCommandRequest(const uint8_t * data, size_t dataLen)
{
TLV::TLVReader reader;
reader.Init(data, dataLen);
ReturnErrorOnFailure(reader.Next());
TLV::TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
CHIP_ERROR err = CHIP_NO_ERROR;
while (CHIP_NO_ERROR == (err = reader.Next()))
{
if (IsTag(reader, app::InvokeRequestMessage::Tag::kInvokeRequests))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
while (CHIP_NO_ERROR == (err = reader.Next()))
{
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(MaybeDecodeCommandData(reader));
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
ReturnErrorOnFailure(reader.ExitContainer(containerType));
}
}
return reader.ExitContainer(containerType);
}
} // namespace interaction_model
} // namespace trace
} // namespace chip