blob: f5009375f1dcd5010741ff354d7eb7adfaef4727 [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 "Options.h"
#include <algorithm>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/data-model/Encode.h>
#include <credentials/DeviceAttestationConstructor.h>
#include <crypto/CHIPCryptoPAL.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters::OperationalCredentials::Commands;
constexpr size_t kMaxResponseLength = 900;
constexpr size_t kCSRNonceLength = 32;
namespace {
CHIP_ERROR ConstructCustomNOCSRElements(TLV::TLVWriter & writer, TLV::Tag tag, const ByteSpan & nocsrElements,
CSRResponseOptions & options)
{
ByteSpan csr;
ByteSpan csrNonce;
ByteSpan vendorReserved1;
ByteSpan vendorReserved2;
ByteSpan vendorReserved3;
ReturnErrorOnFailure(
Credentials::DeconstructNOCSRElements(nocsrElements, csr, csrNonce, vendorReserved1, vendorReserved2, vendorReserved3));
// Add 10 bytes of possible overhead to allow the generation of content longer than the allowed maximum of RESP_MAX.
// 10 has been choosen to leave enough space for the possible TLV overhead when adding the additional data.
uint8_t nocsrElementsData[kMaxResponseLength + 10];
MutableByteSpan nocsrElementsSpan(nocsrElementsData);
TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified;
TLV::TLVWriter tlvWriter;
tlvWriter.Init(nocsrElementsSpan);
ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType));
// Update CSR
if (options.csrIncorrectType)
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(1), true));
}
else
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(1), csr));
}
// Update CSRNonce
if (options.csrNonceIncorrectType)
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), true));
}
else if (options.csrNonceInvalid)
{
uint8_t csrNonceInvalid[kCSRNonceLength] = {};
memcpy(csrNonceInvalid, csrNonce.data(), csrNonce.size());
std::reverse(csrNonceInvalid, csrNonceInvalid + sizeof(csrNonceInvalid));
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), ByteSpan(csrNonceInvalid)));
}
else if (options.csrNonceTooLong)
{
uint8_t csrNonceTooLong[kCSRNonceLength + 1] = {};
memcpy(csrNonceTooLong, csrNonce.data(), csrNonce.size());
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), ByteSpan(csrNonceTooLong)));
}
else
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), csrNonce));
}
// Add vendorReserved1 if present
if (!vendorReserved1.empty())
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(3), vendorReserved1));
}
// Add vendorReserved2 if present
if (!vendorReserved2.empty())
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(4), vendorReserved2));
}
// Add vendorReserved3 if present
if (!vendorReserved3.empty())
{
ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(5), vendorReserved3));
}
// Add additional data
if (options.nocsrElementsTooLong)
{
size_t len = kMaxResponseLength - tlvWriter.GetLengthWritten();
ReturnLogErrorOnFailure(tlvWriter.Put(TLV::ContextTag(6), ByteSpan(nocsrElementsData, len)));
}
ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType));
ReturnErrorOnFailure(tlvWriter.Finalize());
return DataModel::Encode(writer, tag, nocsrElementsSpan.SubSpan(0, tlvWriter.GetLengthWritten()));
}
CHIP_ERROR ConstructCustomAttestationSignature(TLV::TLVWriter & writer, TLV::Tag tag, const ByteSpan & attestationSignature,
CSRResponseOptions & options)
{
if (options.attestationSignatureIncorrectType)
{
return DataModel::Encode(writer, tag, true);
}
if (options.attestationSignatureInvalid)
{
uint8_t invalidAttestationSignature[Crypto::kP256_ECDSA_Signature_Length_Raw] = {};
memcpy(invalidAttestationSignature, attestationSignature.data(), attestationSignature.size());
std::reverse(invalidAttestationSignature, invalidAttestationSignature + sizeof(invalidAttestationSignature));
return DataModel::Encode(writer, tag, ByteSpan(invalidAttestationSignature));
}
return DataModel::Encode(writer, tag, attestationSignature);
}
} // namespace
namespace chip {
namespace app {
namespace DataModel {
template <>
CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, const CSRResponse::Type & responseData)
{
auto tag1 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kNOCSRElements));
auto tag2 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kAttestationSignature));
auto & options = LinuxDeviceOptions::GetInstance().mCSRResponseOptions;
TLV::TLVType outer;
ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer));
ReturnErrorOnFailure(ConstructCustomNOCSRElements(writer, tag1, responseData.NOCSRElements, options));
ReturnErrorOnFailure(ConstructCustomAttestationSignature(writer, tag2, responseData.attestationSignature, options));
ReturnErrorOnFailure(writer.EndContainer(outer));
return CHIP_NO_ERROR;
}
} // namespace DataModel
} // namespace app
} // namespace chip