Skip to content

Commit

Permalink
Add basic support for recognizing and validating Network Identity cer…
Browse files Browse the repository at this point in the history
…ts (#30114)

* Make BitFlags more constexpr friendly

* Add basic support for recognizing and validating Network Identity certs

This involves changing some Extract*FromCert methods from using
ChipCertificateSet to load the certificate to just using
DecodeChipCert, since ChipCertificateSet makes additional assumptions.

Also remove unused ChipCertificateData.mCertificate member

* Address review comments
  • Loading branch information
ksperling-apple authored and pull[bot] committed Dec 21, 2023
1 parent 1baed7b commit 1095112
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 125 deletions.
89 changes: 49 additions & 40 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

#include <stddef.h>

#include <credentials/CHIPCert.h>
#include <credentials/CHIPCert_Internal.h>
#include <credentials/CHIPCertificateSet.h>
#include <lib/asn1/ASN1.h>
#include <lib/asn1/ASN1Macros.h>
Expand Down Expand Up @@ -468,6 +468,7 @@ ChipCertificateData::~ChipCertificateData() {}

void ChipCertificateData::Clear()
{
mSerialNumber = ByteSpan();
mSubjectDN.Clear();
mIssuerDN.Clear();
mSubjectKeyId = CertificateKeyId();
Expand Down Expand Up @@ -612,6 +613,13 @@ CHIP_ERROR ChipDN::GetCertType(CertType & certType) const
bool catsPresent = false;
uint8_t rdnCount = RDNCount();

if (rdnCount == 1 && rdn[0].mAttrOID == kOID_AttributeType_CommonName && !rdn[0].mAttrIsPrintableString &&
rdn[0].mString.data_equal(kNetworkIdentityCN))
{
certType = CertType::kNetworkIdentity;
return CHIP_NO_ERROR;
}

certType = CertType::kNotSpecified;

for (uint8_t i = 0; i < rdnCount; i++)
Expand Down Expand Up @@ -672,6 +680,7 @@ CHIP_ERROR ChipDN::GetCertType(CertType & certType) const
CHIP_ERROR ChipDN::GetCertChipId(uint64_t & chipId) const
{
uint8_t rdnCount = RDNCount();
bool foundId = false;

chipId = 0;

Expand All @@ -683,15 +692,17 @@ CHIP_ERROR ChipDN::GetCertChipId(uint64_t & chipId) const
case kOID_AttributeType_MatterICACId:
case kOID_AttributeType_MatterNodeId:
case kOID_AttributeType_MatterFirmwareSigningId:
VerifyOrReturnError(chipId == 0, CHIP_ERROR_WRONG_CERT_DN);
VerifyOrReturnError(!foundId, CHIP_ERROR_WRONG_CERT_DN);

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

VerifyOrReturnError(foundId, CHIP_ERROR_WRONG_CERT_DN);
return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -1304,78 +1315,48 @@ CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues &

CHIP_ERROR ExtractFabricIdFromCert(const ByteSpan & opcert, FabricId * fabricId)
{
ChipCertificateSet certSet;
ChipCertificateData certData;
ReturnErrorOnFailure(certSet.Init(&certData, 1));
ReturnErrorOnFailure(certSet.LoadCert(opcert, BitFlags<CertDecodeFlags>()));
ReturnErrorOnFailure(DecodeChipCert(opcert, certData));
return ExtractFabricIdFromCert(certData, fabricId);
}

CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ByteSpan & opcert, NodeId * nodeId, FabricId * fabricId)
{
ChipCertificateSet certSet;
ChipCertificateData certData;

ReturnErrorOnFailure(certSet.Init(&certData, 1));

ReturnErrorOnFailure(certSet.LoadCert(opcert, BitFlags<CertDecodeFlags>()));

ReturnErrorOnFailure(DecodeChipCert(opcert, certData));
return ExtractNodeIdFabricIdFromOpCert(certData, nodeId, fabricId);
}

CHIP_ERROR ExtractPublicKeyFromChipCert(const ByteSpan & chipCert, P256PublicKeySpan & publicKey)
{
ChipCertificateSet certSet;
ChipCertificateData certData;

ReturnErrorOnFailure(certSet.Init(&certData, 1));

ReturnErrorOnFailure(certSet.LoadCert(chipCert, BitFlags<CertDecodeFlags>()));

ReturnErrorOnFailure(DecodeChipCert(chipCert, certData));
publicKey = certData.mPublicKey;

return CHIP_NO_ERROR;
}

CHIP_ERROR ExtractNotBeforeFromChipCert(const ByteSpan & chipCert, chip::System::Clock::Seconds32 & notBeforeChipEpochTime)
{
ChipCertificateSet certSet;
ChipCertificateData certData;

ReturnErrorOnFailure(certSet.Init(&certData, 1));

ReturnErrorOnFailure(certSet.LoadCert(chipCert, BitFlags<CertDecodeFlags>()));

ReturnErrorOnFailure(DecodeChipCert(chipCert, certData));
notBeforeChipEpochTime = chip::System::Clock::Seconds32(certData.mNotBeforeTime);

return CHIP_NO_ERROR;
}

CHIP_ERROR ExtractSKIDFromChipCert(const ByteSpan & chipCert, CertificateKeyId & skid)
{
ChipCertificateSet certSet;
ChipCertificateData certData;

ReturnErrorOnFailure(certSet.Init(&certData, 1));

ReturnErrorOnFailure(certSet.LoadCert(chipCert, BitFlags<CertDecodeFlags>()));

ReturnErrorOnFailure(DecodeChipCert(chipCert, certData));
VerifyOrReturnError(certData.mCertFlags.Has(CertFlags::kExtPresent_AuthKeyId), CHIP_ERROR_NOT_FOUND);
skid = certData.mSubjectKeyId;

return CHIP_NO_ERROR;
}

CHIP_ERROR ExtractSubjectDNFromChipCert(const ByteSpan & chipCert, ChipDN & dn)
{
ChipCertificateSet certSet;
ChipCertificateData certData;

ReturnErrorOnFailure(certSet.Init(&certData, 1));

ReturnErrorOnFailure(certSet.LoadCert(chipCert, BitFlags<CertDecodeFlags>()));

ReturnErrorOnFailure(DecodeChipCert(chipCert, certData));
dn = certData.mSubjectDN;

return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -1446,5 +1427,33 @@ CHIP_ERROR CertificateValidityPolicy::ApplyDefaultPolicy(const ChipCertificateDa
}
}

CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert)
{
ChipCertificateData certData;
ReturnErrorOnFailure(DecodeChipCert(cert, certData, CertDecodeFlags::kGenerateTBSHash));

CertType certType;
ReturnErrorOnFailure(certData.mSubjectDN.GetCertType(certType));
VerifyOrReturnError(certType == CertType::kNetworkIdentity, CHIP_ERROR_WRONG_CERT_TYPE);

VerifyOrReturnError(certData.mSerialNumber.data_equal(kNetworkIdentitySerialNumber), CHIP_ERROR_WRONG_CERT_TYPE);
VerifyOrReturnError(certData.mNotBeforeTime == kNetworkIdentityNotBeforeTime, CHIP_ERROR_WRONG_CERT_TYPE);
VerifyOrReturnError(certData.mNotAfterTime == kNetworkIdentityNotAfterTime, CHIP_ERROR_WRONG_CERT_TYPE);
VerifyOrReturnError(certData.mIssuerDN.IsEqual(certData.mSubjectDN), CHIP_ERROR_WRONG_CERT_TYPE);

VerifyOrReturnError(certData.mCertFlags.Has(CertFlags::kExtPresent_BasicConstraints) &&
!certData.mCertFlags.Has(CertFlags::kIsCA),
CHIP_ERROR_WRONG_CERT_TYPE);
VerifyOrReturnError(certData.mCertFlags.Has(CertFlags::kExtPresent_KeyUsage) &&
certData.mKeyUsageFlags == kNetworkIdentityKeyUsage,
CHIP_ERROR_WRONG_CERT_TYPE);
VerifyOrReturnError(certData.mCertFlags.Has(CertFlags::kExtPresent_ExtendedKeyUsage) &&
certData.mKeyPurposeFlags == kNetworkIdentityKeyPurpose,
CHIP_ERROR_WRONG_CERT_TYPE);

ReturnErrorOnFailure(VerifyCertSignature(certData, certData));
return CHIP_NO_ERROR;
}

} // namespace Credentials
} // namespace chip
17 changes: 16 additions & 1 deletion src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ enum class CertType : uint8_t
firmware image signing is manufacturer-specific. The CHIP
certificate format supports encoding of firmware signing
certificates if chosen by the manufacturer to use them. */
kNetworkIdentity = 0x05, /**< A CHIP Network (Client) Identity. */
};

/** X.509 Certificate Key Purpose Flags
Expand Down Expand Up @@ -428,7 +429,7 @@ struct ChipCertificateData
void Clear();
bool IsEqual(const ChipCertificateData & other) const;

ByteSpan mCertificate; /**< Original raw buffer data. */
ByteSpan mSerialNumber; /**< Certificate Serial Number. */
ChipDN mSubjectDN; /**< Certificate Subject DN. */
ChipDN mIssuerDN; /**< Certificate Issuer DN. */
CertificateKeyId mSubjectKeyId; /**< Certificate Subject public key identifier. */
Expand Down Expand Up @@ -529,6 +530,20 @@ CHIP_ERROR VerifyCertSignature(const ChipCertificateData & cert, const ChipCerti
*/
CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac);

/**
* Validates a Network (Client) Identity in TLV-encoded form.
*
* This function parses the certificate, ensures the rigid fields have the values mandated by the
* specification, and validates the certificate signature.
*
* @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate does
* not conform to the requirements for a Network Identity, CHIP_ERROR_INVALID_SIGNATURE
* if the certificate has an invalid signature, or another CHIP_ERROR.
*
* @see section 11.24 (Wi-Fi Authentication with Per-Device Credentials) of the Matter spec
*/
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert);

struct FutureExtension
{
ByteSpan OID;
Expand Down
7 changes: 4 additions & 3 deletions src/credentials/CHIPCertToX509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,12 @@ static CHIP_ERROR DecodeConvertTBSCert(TLVReader & reader, ASN1Writer & writer,
}
ASN1_END_CONSTRUCTED;

ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, ContextTag(kTag_SerialNumber)));

// serialNumber CertificateSerialNumber
// CertificateSerialNumber ::= INTEGER
ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, reader));
ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, ContextTag(kTag_SerialNumber)));
ReturnErrorOnFailure(reader.Get(certData.mSerialNumber));
ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false,
certData.mSerialNumber.data(), static_cast<uint16_t>(certData.mSerialNumber.size())));

// signature AlgorithmIdentifier
// AlgorithmIdentifier ::= SEQUENCE
Expand Down
39 changes: 39 additions & 0 deletions src/credentials/CHIPCert_Internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
*
* Copyright (c) 2023 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.
*/

#pragma once

#include <credentials/CHIPCert.h>

namespace chip {
namespace Credentials {

// Constants for Network (Client) Identities as per section 11.24 (Wi-Fi
// Authentication with Per-Device Credentials) of the Matter spec.
inline constexpr CharSpan kNetworkIdentityCN = "*"_span;
inline constexpr ByteSpan kNetworkIdentitySerialNumber = ByteSpan((uint8_t[1]){ 1 });

inline constexpr uint32_t kNetworkIdentityNotBeforeTime = 1;
inline constexpr uint32_t kNetworkIdentityNotAfterTime = kNullCertTime;

inline constexpr auto kNetworkIdentityKeyUsage = BitFlags<KeyUsageFlags>(KeyUsageFlags::kDigitalSignature);
inline constexpr auto kNetworkIdentityKeyPurpose =
BitFlags<KeyPurposeFlags>(KeyPurposeFlags::kClientAuth, KeyPurposeFlags::kServerAuth);

} // namespace Credentials
} // namespace chip
Loading

0 comments on commit 1095112

Please sign in to comment.