blob: 507185606e3a62ae1e8e339a62510b206b0e15a3 [file] [log] [blame]
#include <cstddef>
#include <cstdint>
#include <pw_fuzzer/fuzztest.h>
#include <pw_unit_test/framework.h>
#include "credentials/CHIPCert.h"
namespace {
using namespace chip;
using namespace chip::Credentials;
using namespace fuzztest;
/*----------------------------------- Helper Functions: Seed Providers -----------------------------------*/
/******************************************************************************************************************* */
const std::string OpCertsDir = "credentials/test/operational-certificates-error-cases/";
auto isChipFile = [](std::string_view name) { return absl::EndsWith(name, ".chip"); };
auto isDerFile = [](std::string_view name) { return absl::EndsWith(name, ".der"); };
auto isChipRCACFile = [](std::string_view name) { return absl::StrContains(name, "RCAC") && absl::EndsWith(name, ".chip"); };
// Lambda that reads certificates from a directory and returns them as a vector of strings, to be used as seeds
auto seedProvider = [](auto filterFunction) -> std::vector<std::string> {
// fuzztest::ReadFilesFromDirectory returns a vector of tuples, each tuple contains a file
// We need to unpack the tuples and then extract file content into a vector of strings.
std::vector<std::tuple<std::string>> tupleVector = ReadFilesFromDirectory(OpCertsDir, filterFunction);
std::vector<std::string> seeds;
if (tupleVector.size() == 0)
{
std::cout << "No Matching Seed files found in the chosen directory" << std::endl;
}
// DEBUG TIP: print tupleVector.size() here to check that we have the correct number of files as seeds.
for (auto & [fileContents] : tupleVector)
{
seeds.push_back(fileContents);
}
return seeds;
};
void ChipCertFuzzer(const std::string & fuzzChipCerts)
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzChipCerts.data()), fuzzChipCerts.size());
{
NodeId nodeId;
FabricId fabricId;
(void) ExtractFabricIdFromCert(span, &fabricId);
(void) ExtractNodeIdFabricIdFromOpCert(span, &nodeId, &fabricId);
}
{
CATValues cats;
(void) ExtractCATsFromOpCert(span, cats);
}
}
FUZZ_TEST(FuzzChipCert, ChipCertFuzzer).WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isChipFile)));
/*----------------------------------- Chip Cert FuzzTests -----------------------------------*/
/******************************************************************************************************************* */
// The Property function for DecodeChipCertFuzzer, The FUZZ_TEST Macro will call this function.
void DecodeChipCertFuzzer(const std::string & fuzzChipCerts, BitFlags<CertDecodeFlags> aDecodeFlag)
{
// TODO: #34352 To Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzChipCerts.data()), fuzzChipCerts.size());
ChipCertificateData certData;
(void) DecodeChipCert(span, certData, aDecodeFlag);
}
chip::Platform::MemoryShutdown();
}
// This function allows us to fuzz using one of three CertDecodeFlags flags; by using FuzzTests's `ElementOf` API, we define an
// input domain by explicitly enumerating the set of values in it More Info:
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains-element-of
auto AnyCertDecodeFlag()
{
constexpr BitFlags<CertDecodeFlags> NullDecodeFlag;
constexpr BitFlags<CertDecodeFlags> GenTBSHashFlag(CertDecodeFlags::kGenerateTBSHash);
constexpr BitFlags<CertDecodeFlags> TrustAnchorFlag(CertDecodeFlags::kIsTrustAnchor);
return ElementOf<CertDecodeFlags>({ NullDecodeFlag, GenTBSHashFlag, TrustAnchorFlag });
}
FUZZ_TEST(FuzzChipCert, DecodeChipCertFuzzer)
.WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isChipFile)), AnyCertDecodeFlag());
/*************************************** */
void ConvertChipCertToX509CertFuzz(const std::string & fuzzChipCerts)
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzChipCerts.data()), fuzzChipCerts.size());
uint8_t outCertBuf[kMaxDERCertLength];
MutableByteSpan outCert(outCertBuf);
(void) ConvertChipCertToX509Cert(span, outCert);
}
FUZZ_TEST(FuzzChipCert, ConvertChipCertToX509CertFuzz).WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isChipFile)));
/******************************************************************************** */
/******************************************************************************** */
void ValidateChipRCACFuzz(const std::string & fuzzRcacCerts)
{
// TODO: #35369 Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzRcacCerts.data()), fuzzRcacCerts.size());
ValidateChipRCAC(span);
}
chip::Platform::MemoryShutdown();
}
FUZZ_TEST(FuzzChipCert, ValidateChipRCACFuzz).WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isChipRCACFile)));
/*----------------------------------- DER Cert FuzzTests -----------------------------------*/
/******************************************************************************** */
void ConvertX509CertToChipCertFuzz(const std::string & fuzzDerCerts)
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzDerCerts.data()), fuzzDerCerts.size());
uint8_t outCertBuf[kMaxDERCertLength];
MutableByteSpan outCert(outCertBuf);
ConvertX509CertToChipCert(span, outCert);
}
FUZZ_TEST(FuzzChipCert, ConvertX509CertToChipCertFuzz).WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isDerFile)));
/******************************************************************************** */
/******************************************************************************** */
void ExtractSubjectDNFromX509CertFuzz(const std::string & fuzzDerCerts)
{
ByteSpan span(reinterpret_cast<const uint8_t *>(fuzzDerCerts.data()), fuzzDerCerts.size());
ChipDN subjectDN;
ExtractSubjectDNFromX509Cert(span, subjectDN);
}
FUZZ_TEST(FuzzChipCert, ExtractSubjectDNFromX509CertFuzz).WithDomains(Arbitrary<std::string>().WithSeeds(seedProvider(isDerFile)));
} // namespace