Skip to content

Commit

Permalink
Add utilities for extracting a PeerId from an opcert, and use them in…
Browse files Browse the repository at this point in the history
… CASE. (#8345)

This sets up the PeerNodeId for our CASE session's PeerConnectionState
correctly.

Without doing this, when we try to expire existing connections to the
peer node id at the end of CASE setup (which would normally drop stale
CASE sessions to the same node, PASE sessions, etc), we fail to do
that, because we try to do it for kUndefinedNodeId instead of the
right node id.
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed Aug 21, 2021
1 parent 1faf24f commit 1803037
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,5 +933,56 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1Writ
return err;
}

CHIP_ERROR ExtractPeerIdFromOpCert(const ChipCertificateData & opcert, PeerId * peerId)
{
// Since we assume the cert is pre-validated, we are going to assume that
// its subject in fact has both a node id and a fabric id.
PeerId id;
bool foundNodeId = false;
bool foundFabricId = false;
const ChipDN & subjectDN = opcert.mSubjectDN;
for (uint8_t i = 0; i < subjectDN.RDNCount(); ++i)
{
const auto & rdn = subjectDN.rdn[i];
if (rdn.mAttrOID == ASN1::kOID_AttributeType_ChipNodeId)
{
id.SetNodeId(rdn.mChipVal);
foundNodeId = true;
}
else if (rdn.mAttrOID == ASN1::kOID_AttributeType_ChipFabricId)
{
id.SetFabricId(rdn.mChipVal);
foundFabricId = true;
}
}
if (!foundNodeId || !foundFabricId)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}

*peerId = id;
return CHIP_NO_ERROR;
}

CHIP_ERROR ExtractPeerIdFromOpCert(const ByteSpan & opcert, PeerId * peerId)
{
ChipCertificateSet certSet;

ReturnErrorOnFailure(certSet.Init(1, kMaxCHIPCertDecodeBufLength));

ReturnErrorOnFailure(certSet.LoadCert(opcert.data(), static_cast<uint32_t>(opcert.size()), BitFlags<CertDecodeFlags>()));

return ExtractPeerIdFromOpCert(certSet.GetCertSet()[0], peerId);
}

CHIP_ERROR ExtractPeerIdFromOpCertArray(const ByteSpan & opcertarray, PeerId * peerId)
{
ByteSpan noc;
ByteSpan icac;
ReturnErrorOnFailure(ExtractCertsFromCertArray(opcertarray, noc, icac));

return ExtractPeerIdFromOpCert(noc, peerId);
}

} // namespace Credentials
} // namespace chip
32 changes: 32 additions & 0 deletions src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <core/CHIPConfig.h>
#include <core/CHIPTLV.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/PeerId.h>
#include <support/BitFlags.h>
#include <support/DLLUtil.h>

Expand Down Expand Up @@ -867,5 +868,36 @@ CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1::AS
*/
CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1::ASN1Reader & reader, chip::TLV::TLVWriter & writer, uint64_t tag);

/**
* Extract a PeerId from an operational certificate that has already been
* parsed.
*
* @return CHIP_ERROR_INVALID_ARGUMENT if the passed-in cert does not have at
* least one NodeId RDN and one FabricId RDN in the Subject DN. No other
* validation (e.g. checkign that there is exactly one RDN of each type) is
* performed.
*/
CHIP_ERROR ExtractPeerIdFromOpCert(const ChipCertificateData & opcert, PeerId * peerId);

/**
* Extract a PeerId from an operational certificate in ByteSpan TLV-encoded
* form. This does not perform any sort of validation on the certificate
* structure other than parsing it.
*
* Can return any error that can be returned from parsing the cert or from the
* ChipCertificateData* version of ExtractPeerIdFromOpCert.
*/
CHIP_ERROR ExtractPeerIdFromOpCert(const ByteSpan & opcert, PeerId * peerId);

/**
* Extract a PeerId from an operational certificate array in ByteSpan
* TLV-encoded form. This does not perform any sort of validation on the
* certificate structure other than parsing it.
*
* Can return any error that can be returned from parsing the array or from the
* ChipCertificateData* version of ExtractPeerIdFromOpCert.
*/
CHIP_ERROR ExtractPeerIdFromOpCertArray(const ByteSpan & opcertarray, PeerId * peerId);

} // namespace Credentials
} // namespace chip
87 changes: 87 additions & 0 deletions src/credentials/tests/TestChipCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <credentials/CHIPCert.h>
#include <credentials/CHIPOperationalCredentials.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/PeerId.h>
#include <support/CHIPMem.h>
#include <support/CodeUtils.h>
#include <support/ErrorStr.h>
Expand Down Expand Up @@ -1328,6 +1329,91 @@ static void TestChipCert_ChipArrayToChipCertsNoICA(nlTestSuite * inSuite, void *
NL_TEST_ASSERT(inSuite, certSet.FindValidCert(subjectDN, subjectKeyId, validContext, resultCert) == CHIP_NO_ERROR);
}

static void TestChipCert_ExtractPeerId(nlTestSuite * inSuite, void * inContext)
{
struct TestCase
{
uint8_t Cert;
uint8_t ICACert;
uint64_t ExpectedNodeId;
uint64_t ExpectedFabricId;
};

// clang-format off
static constexpr TestCase sTestCases[] = {
// Cert ICA ExpectedNodeId ExpectedFabricId
// =============================================================
{ TestCert::kNode01_01, TestCert::kICA01, 0xDEDEDEDE00010001, 0xFAB000000000001D },
{ TestCert::kNode01_02, TestCert::kNone, 0xDEDEDEDE00010002, 0xFAB000000000001D },
{ TestCert::kNode02_01, TestCert::kICA02, 0xDEDEDEDE00020001, 0xFAB000000000001D },
{ TestCert::kNode02_02, TestCert::kICA02, 0xDEDEDEDE00020002, 0xFAB000000000001D },
{ TestCert::kNode02_03, TestCert::kICA02, 0xDEDEDEDE00020003, 0xFAB000000000001D },
{ TestCert::kNode02_04, TestCert::kICA02, 0xDEDEDEDE00020004, 0xFAB000000000001D },
{ TestCert::kNode02_05, TestCert::kICA02, 0xDEDEDEDE00020005, 0xFAB000000000001D },
{ TestCert::kNode02_06, TestCert::kICA02, 0xDEDEDEDE00020006, 0xFAB000000000001D },
{ TestCert::kNode02_07, TestCert::kICA02, 0xDEDEDEDE00020007, 0xFAB000000000001D },
};
// clang-format on

// Test extraction from the raw ByteSpan form.
for (auto & testCase : sTestCases)
{
ByteSpan cert;
CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

PeerId peerId;
err = ExtractPeerIdFromOpCert(cert, &peerId);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, peerId.GetNodeId() == testCase.ExpectedNodeId);
NL_TEST_ASSERT(inSuite, peerId.GetFabricId() == testCase.ExpectedFabricId);
}

// Test extraction from the parsed form.
ChipCertificateSet certSet;
for (auto & testCase : sTestCases)
{
CHIP_ERROR err = certSet.Init(1, kMaxCHIPCertDecodeBufLength);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

err = LoadTestCert(certSet, testCase.Cert, sNullLoadFlag, sNullDecodeFlag);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

PeerId peerId;
err = ExtractPeerIdFromOpCert(certSet.GetCertSet()[0], &peerId);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, peerId.GetNodeId() == testCase.ExpectedNodeId);
NL_TEST_ASSERT(inSuite, peerId.GetFabricId() == testCase.ExpectedFabricId);
certSet.Release();
}

// Test extraction from cert array form.
for (auto & testCase : sTestCases)
{
ByteSpan cert;
CHIP_ERROR err = GetTestCert(testCase.Cert, sDerFormFlag, cert);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

ByteSpan icaCert;
if (testCase.ICACert != TestCert::kNone)
{
err = GetTestCert(testCase.ICACert, sDerFormFlag, icaCert);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
}

uint8_t certArray[kMaxCHIPCertLength * 2];
MutableByteSpan certs(certArray);
err = ConvertX509CertsToChipCertArray(cert, icaCert, certs);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

PeerId peerId;
err = ExtractPeerIdFromOpCertArray(certs, &peerId);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, peerId.GetNodeId() == testCase.ExpectedNodeId);
NL_TEST_ASSERT(inSuite, peerId.GetFabricId() == testCase.ExpectedFabricId);
}
}

/**
* Set up the test suite.
*/
Expand Down Expand Up @@ -1376,6 +1462,7 @@ static const nlTest sTests[] = {
NL_TEST_DEF("Test CHIP Certificates X509 to CHIP Array Conversion Error Scenarios", TestChipCert_X509ToChipArrayErrorScenarios),
NL_TEST_DEF("Test CHIP Array to Chip Certificates Conversion", TestChipCert_ChipArrayToChipCerts),
NL_TEST_DEF("Test No ICA CHIP Array to Chip Certificates Conversion", TestChipCert_ChipArrayToChipCertsNoICA),
NL_TEST_DEF("Test extracting PeerId from node certificate", TestChipCert_ExtractPeerId),
NL_TEST_SENTINEL()
};
// clang-format on
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/secure_channel/CASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,12 @@ CHIP_ERROR CASESession::Validate_and_RetrieveResponderID(const uint8_t * respond
const CertificateKeyId & subjectKeyId = certSet.GetCertSet()[0].mSubjectKeyId;

ReturnErrorOnFailure(mOpCredSet->FindValidCert(mTrustedRootId, subjectDN, subjectKeyId, mValidContext, resultCert));

// Now that we have verified that this is a valid cert, try to get the
// peer's operational identity from it.
PeerId peerId;
ReturnErrorOnFailure(ExtractPeerIdFromOpCert(certSet.GetCertSet()[0], &peerId));
mConnectionState.SetPeerNodeId(peerId.GetNodeId());
}

return CHIP_NO_ERROR;
Expand Down

0 comments on commit 1803037

Please sign in to comment.