Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHIP credential serialization #6400

Merged
merged 13 commits into from
Jun 3, 2021
Merged
32 changes: 30 additions & 2 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ CHIP_ERROR ChipCertificateSet::LoadCert(const uint8_t * chipCert, uint32_t chipC
err = reader.Next(kTLVType_Structure, ProfileTag(Protocols::OpCredentials::Id.ToTLVProfileId(), kTag_ChipCertificate));
SuccessOrExit(err);

err = LoadCert(reader, decodeFlags);
err = LoadCert(reader, decodeFlags, ByteSpan(chipCert, chipCertLen));

exit:
return err;
}

CHIP_ERROR ChipCertificateSet::LoadCert(TLVReader & reader, BitFlags<CertDecodeFlags> decodeFlags)
CHIP_ERROR ChipCertificateSet::LoadCert(TLVReader & reader, BitFlags<CertDecodeFlags> decodeFlags, ByteSpan chipCert)
{
CHIP_ERROR err;
ASN1Writer writer; // ASN1Writer is used to encode TBS portion of the certificate for the purpose of signature
Expand All @@ -177,6 +177,8 @@ CHIP_ERROR ChipCertificateSet::LoadCert(TLVReader & reader, BitFlags<CertDecodeF

cert = new (&mCerts[mCertCount]) ChipCertificateData();

cert->mCertificate = chipCert;

{
TLVType containerType;

Expand Down Expand Up @@ -766,6 +768,32 @@ CHIP_ERROR ChipDN::GetCertType(uint8_t & certType) const
return err;
}

CHIP_ERROR ChipDN::GetCertChipId(uint64_t & chipId) const
{
uint8_t rdnCount = RDNCount();

chipId = 0;

for (uint8_t i = 0; i < rdnCount; i++)
{
switch (rdn[i].mAttrOID)
bitkis marked this conversation as resolved.
Show resolved Hide resolved
{
case kOID_AttributeType_ChipRootId:
case kOID_AttributeType_ChipICAId:
case kOID_AttributeType_ChipNodeId:
case kOID_AttributeType_ChipFirmwareSigningId:
VerifyOrReturnError(chipId == 0, CHIP_ERROR_WRONG_CERT_TYPE);

chipId = rdn[i].mAttrValue.mChipVal;
break;
default:
break;
}
}

return CHIP_NO_ERROR;
}

bool ChipDN::IsEqual(const ChipDN & other) const
{
bool res = true;
Expand Down
14 changes: 13 additions & 1 deletion src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,15 @@ class ChipDN
**/
CHIP_ERROR GetCertType(uint8_t & certType) const;

/**
* @brief Retrieve the ID of a CHIP certificate.
*
* @param certId A reference to the certificate ID value.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR GetCertChipId(uint64_t & chipId) const;

bool IsEqual(const ChipDN & other) const;

/**
Expand Down Expand Up @@ -300,6 +309,7 @@ struct ChipCertificateData

void Clear();

ByteSpan mCertificate; /**< Original raw buffer data. */
ChipDN mSubjectDN; /**< Certificate Subject DN. */
ChipDN mIssuerDN; /**< Certificate Issuer DN. */
CertificateKeyId mSubjectKeyId; /**< Certificate Subject public key identifier. */
Expand Down Expand Up @@ -432,10 +442,12 @@ class DLL_EXPORT ChipCertificateSet
*
* @param reader A TLVReader positioned at the CHIP certificate TLV structure.
* @param decodeFlags Certificate decoding option flags.
* @param chipCert Buffer containing certificate encoded on CHIP format. It is required that this CHIP certificate
* in chipCert ByteSpan stays valid while the certificate data in the set is used.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR LoadCert(chip::TLV::TLVReader & reader, BitFlags<CertDecodeFlags> decodeFlags);
CHIP_ERROR LoadCert(chip::TLV::TLVReader & reader, BitFlags<CertDecodeFlags> decodeFlags, ByteSpan chipCert = ByteSpan());

/**
* @brief Load CHIP certificates into set.
Expand Down
81 changes: 81 additions & 0 deletions src/credentials/CHIPOperationalCredentials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@
#include <credentials/CHIPOperationalCredentials.h>
#include <support/CHIPMem.h>
#include <support/CodeUtils.h>
#include <support/SafeInt.h>

namespace chip {
namespace Credentials {

static constexpr size_t kOperationalCertificatesMax = 3;
static constexpr size_t kOperationalCertificateDecodeBufSize = 1024;

using namespace chip::Crypto;

CHIP_ERROR OperationalCredentialSet::Init(uint8_t maxCertsArraySize)
Expand Down Expand Up @@ -266,6 +270,83 @@ CHIP_ERROR OperationalCredentialSet::SetDevOpCredKeypair(const CertificateKeyId
return CHIP_NO_ERROR;
}

CHIP_ERROR OperationalCredentialSet::ToSerializable(const CertificateKeyId & trustedRootId,
bitkis marked this conversation as resolved.
Show resolved Hide resolved
OperationalCredentialSerializable & serializable)
{
const NodeCredential * nodeCredential = GetNodeCredentialAt(trustedRootId);
P256Keypair * keypair = GetNodeKeypairAt(trustedRootId);
P256SerializedKeypair serializedKeypair;
ChipCertificateSet * certificateSet = FindCertSet(trustedRootId);
const ChipCertificateData * dataSet = nullptr;
uint8_t * ptrSerializableCerts[] = { serializable.mRootCertificate, serializable.mCACertificate };
uint16_t * ptrSerializableCertsLen[] = { &serializable.mRootCertificateLen, &serializable.mCACertificateLen };

VerifyOrReturnError(certificateSet != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(keypair->Serialize(serializedKeypair));
VerifyOrReturnError(serializedKeypair.Length() <= sizeof(serializable.mNodeKeypair), CHIP_ERROR_INVALID_ARGUMENT);

dataSet = certificateSet->GetCertSet();
VerifyOrReturnError(dataSet != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

memset(&serializable, 0, sizeof(serializable));
serializable.mNodeCredentialLen = nodeCredential->mLen;

memcpy(serializable.mNodeCredential, nodeCredential->mCredential, nodeCredential->mLen);
memcpy(serializable.mNodeKeypair, serializedKeypair, serializedKeypair.Length());
serializable.mNodeKeypairLen = static_cast<uint16_t>(serializedKeypair.Length());

for (uint8_t i = 0; i < certificateSet->GetCertCount(); ++i)
{
VerifyOrReturnError(CanCastTo<uint16_t>(dataSet[i].mCertificate.size()), CHIP_ERROR_INTERNAL);
memcpy(ptrSerializableCerts[i], dataSet[i].mCertificate.data(), dataSet[i].mCertificate.size());
*ptrSerializableCertsLen[i] = static_cast<uint16_t>(dataSet[i].mCertificate.size());
}

return CHIP_NO_ERROR;
}

CHIP_ERROR OperationalCredentialSet::FromSerializable(const OperationalCredentialSerializable & serializable)
{
CHIP_ERROR err = CHIP_NO_ERROR;

P256Keypair keypair;
P256SerializedKeypair serializedKeypair;
ChipCertificateSet certificateSet;
CertificateKeyId trustedRootId;

SuccessOrExit(err = certificateSet.Init(kOperationalCertificatesMax, kOperationalCertificateDecodeBufSize));

err = certificateSet.LoadCert(serializable.mRootCertificate, serializable.mRootCertificateLen,
BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor));
SuccessOrExit(err);

trustedRootId.mId = certificateSet.GetLastCert()->mAuthKeyId.mId;
trustedRootId.mLen = certificateSet.GetLastCert()->mAuthKeyId.mLen;

if (serializable.mCACertificateLen != 0)
{
err = certificateSet.LoadCert(serializable.mCACertificate, serializable.mCACertificateLen,
BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash));
SuccessOrExit(err);
}

LoadCertSet(&certificateSet);

memcpy(serializedKeypair, serializable.mNodeKeypair, serializable.mNodeKeypairLen);
SuccessOrExit(err = serializedKeypair.SetLength(serializable.mNodeKeypairLen));

SuccessOrExit(err = keypair.Deserialize(serializedKeypair));

SuccessOrExit(err = SetDevOpCredKeypair(trustedRootId, &keypair));

SuccessOrExit(err = SetDevOpCred(trustedRootId, serializable.mNodeCredential, serializable.mNodeCredentialLen));

exit:
certificateSet.Release();

return err;
}

const NodeCredential * OperationalCredentialSet::GetNodeCredentialAt(const CertificateKeyId & trustedRootId) const
{
for (size_t i = 0; i < kOperationalCredentialsMax && mChipDeviceCredentials[i].nodeCredential.mCredential != nullptr; ++i)
Expand Down
32 changes: 31 additions & 1 deletion src/credentials/CHIPOperationalCredentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
namespace chip {
namespace Credentials {

static constexpr size_t kOperationalCredentialsMax = 5;
static constexpr size_t kOperationalCredentialsMax = 5;
static constexpr size_t kOperationalCertificateMaxSize = 400;

using namespace Crypto;

Expand All @@ -45,6 +46,18 @@ struct NodeCredential
uint16_t mLen = 0;
};

struct OperationalCredentialSerializable
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
{
uint16_t mNodeCredentialLen;
uint8_t mNodeCredential[kOperationalCertificateMaxSize];
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
uint16_t mNodeKeypairLen;
uint8_t mNodeKeypair[kP256_PublicKey_Length + kP256_PrivateKey_Length];
uint16_t mRootCertificateLen;
uint8_t mRootCertificate[kOperationalCertificateMaxSize];
uint16_t mCACertificateLen;
uint8_t mCACertificate[kOperationalCertificateMaxSize];
};

struct NodeCredentialMap
{
CertificateKeyId trustedRootId;
Expand Down Expand Up @@ -219,6 +232,23 @@ class DLL_EXPORT OperationalCredentialSet
CHIP_ERROR SetDevOpCred(const CertificateKeyId & trustedRootId, const uint8_t * chipDeviceCredentials,
uint16_t chipDeviceCredentialsLen);

/**
* @brief
* Serialize the OperationalCredentialSet indexed by a TrustedRootID to the given serializable data structure
*
* This method must be called while the OperationalCredentialSet class is valid (After Init and before Release)
*/
CHIP_ERROR ToSerializable(const CertificateKeyId & trustedRootId, OperationalCredentialSerializable & output);
woody-apple marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief
* Reconstruct OperationalCredentialSet class from the serializable data structure.
*
* This method must be called after initializing the OperationalCredentialSet class with internal allocation.
* No references/pointers to the input parameter are made. The input parameter can be freed after calling this method.
*/
CHIP_ERROR FromSerializable(const OperationalCredentialSerializable & input);

P256Keypair & GetDevOpCredKeypair(const CertificateKeyId & trustedRootId) { return *GetNodeKeypairAt(trustedRootId); }

CHIP_ERROR SetDevOpCredKeypair(const CertificateKeyId & trustedRootId, P256Keypair * newKeypair);
Expand Down
47 changes: 47 additions & 0 deletions src/credentials/tests/TestChipCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,52 @@ static void TestChipCert_CertType(nlTestSuite * inSuite, void * inContext)
}
}

static void TestChipCert_CertId(nlTestSuite * inSuite, void * inContext)
{
CHIP_ERROR err;
ChipCertificateSet certSet;

struct TestCase
{
uint8_t Cert;
uint64_t ExpectedCertId;
};

// clang-format off
static TestCase sTestCases[] = {
// Cert ExpectedCertId
// =============================================================
{ TestCert::kRoot01, 0xCACACACA00000001 },
{ TestCert::kRoot02, 0xCACACACA00000002 },
{ TestCert::kICA01, 0xCACACACA00000003 },
{ TestCert::kICA02, 0xCACACACA00000004 },
{ TestCert::kICA01_1, 0xCACACACA00000005 },
{ TestCert::kFWSign01, 0xFFFFFFFF00000001 },
{ TestCert::kNode01_01, 0xDEDEDEDE00010001 },
{ TestCert::kNode01_02, 0xDEDEDEDE00010002 },
{ TestCert::kNode02_01, 0xDEDEDEDE00020001 },
{ TestCert::kNode02_02, 0xDEDEDEDE00020002 },
};
// clang-format on
static const size_t sNumTestCases = sizeof(sTestCases) / sizeof(sTestCases[0]);

for (unsigned i = 0; i < sNumTestCases; i++)
{
const TestCase & testCase = sTestCases[i];
uint64_t chipId;

// Initialize the certificate set and load the test certificate.
certSet.Init(1, kTestCertBufSize);
err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

err = certSet.GetCertSet()->mSubjectDN.GetCertChipId(chipId);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

NL_TEST_ASSERT(inSuite, chipId == testCase.ExpectedCertId);
}
}

static void TestChipCert_GenerateRootCert(nlTestSuite * inSuite, void * inContext)
{
// Generate a new keypair for cert signing
Expand Down Expand Up @@ -931,6 +977,7 @@ static const nlTest sTests[] = {
NL_TEST_DEF("Test CHIP Certificate Validation time", TestChipCert_CertValidTime),
NL_TEST_DEF("Test CHIP Certificate Usage", TestChipCert_CertUsage),
NL_TEST_DEF("Test CHIP Certificate Type", TestChipCert_CertType),
NL_TEST_DEF("Test CHIP Certificate ID", TestChipCert_CertId),
NL_TEST_DEF("Test CHIP Generate Root Certificate", TestChipCert_GenerateRootCert),
NL_TEST_DEF("Test CHIP Generate Root Certificate with Fabric", TestChipCert_GenerateRootFabCert),
NL_TEST_DEF("Test CHIP Generate ICA Certificate", TestChipCert_GenerateICACert),
Expand Down
Loading