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

Introduce initial TC-RR-1.1 #22032

Merged
merged 13 commits into from
Aug 19, 2022
6 changes: 6 additions & 0 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
@@ -358,6 +358,12 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId f
// store the credentials in persistent storage, and
// generate when not available in the storage.
ReturnLogErrorOnFailure(mCommissionerStorage.Init(key.c_str()));
if (mUseMaxSizedCerts.HasValue())
{
auto option = CredentialIssuerCommands::CredentialIssuerOptions::kMaximizeCertificateSizes;
mCredIssuerCmds->SetCredentialIssuerOption(option, mUseMaxSizedCerts.Value());
}

ReturnLogErrorOnFailure(mCredIssuerCmds->InitializeCredentialsIssuer(mCommissionerStorage));

chip::MutableByteSpan nocSpan(noc.Get(), chip::Controller::kMaxCHIPDERCertLength);
4 changes: 4 additions & 0 deletions examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
@@ -70,6 +70,9 @@ class CHIPCommand : public Command
"4. The default if not specified is \"alpha\".");
AddArgument("commissioner-nodeid", 0, UINT64_MAX, &mCommissionerNodeId,
"The node id to use for chip-tool. If not provided, kTestControllerNodeId (112233, 0x1B669) will be used.");
AddArgument("use-max-sized-certs", 0, 1, &mUseMaxSizedCerts,
"Maximize the size of operational certificates. If not provided or 0 (\"false\"), normally sized operational "
"certificates are generated.");
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
AddArgument("trace_file", &mTraceFile);
AddArgument("trace_log", 0, 1, &mTraceLog);
@@ -153,6 +156,7 @@ class CHIPCommand : public Command
chip::Optional<chip::NodeId> mCommissionerNodeId;
chip::Optional<uint16_t> mBleAdapterId;
chip::Optional<char *> mPaaTrustStorePath;
chip::Optional<bool> mUseMaxSizedCerts;

// Cached trust store so commands other than the original startup command
// can spin up commissioners as needed.
19 changes: 19 additions & 0 deletions examples/chip-tool/commands/common/CredentialIssuerCommands.h
Original file line number Diff line number Diff line change
@@ -74,4 +74,23 @@ class CredentialIssuerCommands
virtual CHIP_ERROR GenerateControllerNOCChain(chip::NodeId nodeId, chip::FabricId fabricId, const chip::CATValues & cats,
chip::Crypto::P256Keypair & keypair, chip::MutableByteSpan & rcac,
chip::MutableByteSpan & icac, chip::MutableByteSpan & noc) = 0;

// All options must start false
enum CredentialIssuerOptions : uint8_t
{
kMaximizeCertificateSizes = 0, // If set, certificate chains will be maximized for testing via padding
};

virtual void SetCredentialIssuerOption(CredentialIssuerOptions option, bool isEnabled)
{
// Do nothing
(void) option;
(void) isEnabled;
}

virtual bool GetCredentialIssuerOption(CredentialIssuerOptions option)
{
// All options always start false
return false;
}
};
Original file line number Diff line number Diff line change
@@ -49,6 +49,33 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands
return mOpCredsIssuer.GenerateNOCChainAfterValidation(nodeId, fabricId, cats, keypair.Pubkey(), rcac, icac, noc);
}

void SetCredentialIssuerOption(CredentialIssuerOptions option, bool isEnabled) override
{
switch (option)
{
case CredentialIssuerOptions::kMaximizeCertificateSizes:
mUsesMaxSizedCerts = isEnabled;
mOpCredsIssuer.SetMaximallyLargeCertsUsed(mUsesMaxSizedCerts);
break;
default:
break;
}
}

bool GetCredentialIssuerOption(CredentialIssuerOptions option) override
{
switch (option)
{
case CredentialIssuerOptions::kMaximizeCertificateSizes:
return mUsesMaxSizedCerts;
default:
return false;
}
}

protected:
bool mUsesMaxSizedCerts = false;

private:
chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer;
};
168 changes: 154 additions & 14 deletions src/controller/ExampleOperationalCredentialsIssuer.cpp
Original file line number Diff line number Diff line change
@@ -39,6 +39,127 @@ using namespace Credentials;
using namespace Crypto;
using namespace TLV;

namespace {

enum CertType : uint8_t
{
kRcac = 0,
kIcac = 1,
kNoc = 2
};

CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipDN desiredDn, CertType certType, bool maximizeSize,
const Crypto::P256PublicKey & subjectPublicKey, Crypto::P256Keypair & issuerKeypair,
MutableByteSpan & outX509Cert)
{
constexpr size_t kDERCertDnEncodingOverhead = 11;
constexpr size_t kTLVCertDnEncodingOverhead = 3;
constexpr size_t kMaxCertPaddingLength = 150;
constexpr size_t kTLVDesiredSize = kMaxCHIPCertLength - 50;

Platform::ScopedMemoryBuffer<uint8_t> derBuf;
ReturnErrorCodeIf(!derBuf.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan derSpan{ derBuf.Get(), kMaxDERCertLength };

int64_t serialNumber = 1;

switch (certType)
{
case CertType::kRcac: {
X509CertRequestParams rcacRequest = { serialNumber, now, now + validity, desiredDn, desiredDn };
ReturnErrorOnFailure(NewRootX509Cert(rcacRequest, issuerKeypair, derSpan));
break;
}
case CertType::kIcac: {
X509CertRequestParams icacRequest = { serialNumber, now, now + validity, desiredDn, issuerDn };
ReturnErrorOnFailure(NewICAX509Cert(icacRequest, subjectPublicKey, issuerKeypair, derSpan));
break;
}
case CertType::kNoc: {
X509CertRequestParams nocRequest = { serialNumber, now, now + validity, desiredDn, issuerDn };
ReturnErrorOnFailure(NewNodeOperationalX509Cert(nocRequest, subjectPublicKey, issuerKeypair, derSpan));
break;
}
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}

if (maximizeSize && (desiredDn.RDNCount() < CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES))
{
Platform::ScopedMemoryBuffer<uint8_t> paddedTlvBuf;
ReturnErrorCodeIf(!paddedTlvBuf.Alloc(kMaxCHIPCertLength + kMaxCertPaddingLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan paddedTlvSpan{ paddedTlvBuf.Get(), kMaxCHIPCertLength + kMaxCertPaddingLength };
ReturnErrorOnFailure(ConvertX509CertToChipCert(derSpan, paddedTlvSpan));

Platform::ScopedMemoryBuffer<uint8_t> paddedDerBuf;
ReturnErrorCodeIf(!paddedDerBuf.Alloc(kMaxDERCertLength + kMaxCertPaddingLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan paddedDerSpan{ paddedDerBuf.Get(), kMaxDERCertLength + kMaxCertPaddingLength };

Platform::ScopedMemoryBuffer<char> fillerBuf;
ReturnErrorCodeIf(!fillerBuf.Alloc(kMaxCertPaddingLength), CHIP_ERROR_NO_MEMORY);
memset(fillerBuf.Get(), 'A', kMaxCertPaddingLength);

int derPaddingLen = static_cast<int>(kMaxDERCertLength - kDERCertDnEncodingOverhead - derSpan.size());
int tlvPaddingLen = static_cast<int>(kTLVDesiredSize - kTLVCertDnEncodingOverhead - paddedTlvSpan.size());
if (certType == CertType::kRcac)
{
// For RCAC the issuer/subject DN are the same so padding will be present in both
derPaddingLen = (derPaddingLen - static_cast<int>(kDERCertDnEncodingOverhead)) / 2;
tlvPaddingLen = (tlvPaddingLen - static_cast<int>(kTLVCertDnEncodingOverhead)) / 2;
}

size_t paddingLen = 0;
if (derPaddingLen >= 1 && tlvPaddingLen >= 1)
{
paddingLen = std::min(static_cast<size_t>(std::min(derPaddingLen, tlvPaddingLen)), kMaxCertPaddingLength);
}

for (; paddingLen > 0; paddingLen--)
{
paddedDerSpan = MutableByteSpan{ paddedDerBuf.Get(), kMaxDERCertLength + kMaxCertPaddingLength };
paddedTlvSpan = MutableByteSpan{ paddedTlvBuf.Get(), kMaxCHIPCertLength + kMaxCertPaddingLength };

ChipDN certDn = desiredDn;
// Fill the padding in the DomainNameQualifier DN
certDn.AddAttribute_DNQualifier(CharSpan(fillerBuf.Get(), paddingLen), false);

switch (certType)
{
case CertType::kRcac: {
X509CertRequestParams rcacRequest = { serialNumber, now, now + validity, certDn, certDn };
ReturnErrorOnFailure(NewRootX509Cert(rcacRequest, issuerKeypair, paddedDerSpan));
break;
}
case CertType::kIcac: {
X509CertRequestParams icacRequest = { serialNumber, now, now + validity, certDn, issuerDn };
ReturnErrorOnFailure(NewICAX509Cert(icacRequest, subjectPublicKey, issuerKeypair, paddedDerSpan));
break;
}
case CertType::kNoc: {
X509CertRequestParams nocRequest = { serialNumber, now, now + validity, certDn, issuerDn };
ReturnErrorOnFailure(NewNodeOperationalX509Cert(nocRequest, subjectPublicKey, issuerKeypair, paddedDerSpan));
break;
}
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}

ReturnErrorOnFailure(ConvertX509CertToChipCert(paddedDerSpan, paddedTlvSpan));

ChipLogProgress(Controller, "Generated maximized certificate with %u DER bytes, %u TLV bytes",
static_cast<unsigned>(paddedDerSpan.size()), static_cast<unsigned>(paddedTlvSpan.size()));
if (paddedDerSpan.size() <= kMaxDERCertLength && paddedTlvSpan.size() <= kMaxCHIPCertLength)
{
return CopySpanToMutableSpan(paddedDerSpan, outX509Cert);
}
}
}

return CopySpanToMutableSpan(derSpan, outX509Cert);
}

} // namespace

CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDelegate & storage)
{
using namespace ASN1;
@@ -122,6 +243,12 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation(
uint16_t rcacBufLen = static_cast<uint16_t>(std::min(rcac.size(), static_cast<size_t>(UINT16_MAX)));
PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key,
err = mStorage->SyncGetKeyValue(key, rcac.data(), rcacBufLen));
// Always regenerate RCAC on maximally sized certs. The keys remain the same, so everything is fine.
if (mUseMaximallySizedCerts)
{
err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}

if (err == CHIP_NO_ERROR)
{
uint64_t rcacId;
@@ -137,10 +264,14 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation(
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(mIssuerId));

ChipLogProgress(Controller, "Generating RCAC");
X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn };
ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac));

ReturnErrorOnFailure(IssueX509Cert(mNow, mValidity, rcac_dn, rcac_dn, CertType::kRcac, mUseMaximallySizedCerts,
mIssuer.Pubkey(), mIssuer, rcac));
VerifyOrReturnError(CanCastTo<uint16_t>(rcac.size()), CHIP_ERROR_INTERNAL);

// Re-extract DN based on final generated cert
rcac_dn = ChipDN{};
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn));

PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key,
ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, rcac.data(), static_cast<uint16_t>(rcac.size()))));
}
@@ -149,6 +280,11 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation(
uint16_t icacBufLen = static_cast<uint16_t>(std::min(icac.size(), static_cast<size_t>(UINT16_MAX)));
PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsIntermediateCertificateStorage, key,
err = mStorage->SyncGetKeyValue(key, icac.data(), icacBufLen));
// Always regenerate ICAC on maximally sized certs. The keys remain the same, so everything is fine.
if (mUseMaximallySizedCerts)
{
err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}
if (err == CHIP_NO_ERROR)
{
uint64_t icacId;
@@ -164,10 +300,14 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation(
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(mIntermediateIssuerId));

ChipLogProgress(Controller, "Generating ICAC");
X509CertRequestParams icac_request = { 0, mNow, mNow + mValidity, icac_dn, rcac_dn };
ReturnErrorOnFailure(NewICAX509Cert(icac_request, mIntermediateIssuer.Pubkey(), mIssuer, icac));

ReturnErrorOnFailure(IssueX509Cert(mNow, mValidity, rcac_dn, icac_dn, CertType::kIcac, mUseMaximallySizedCerts,
mIntermediateIssuer.Pubkey(), mIssuer, icac));
VerifyOrReturnError(CanCastTo<uint16_t>(icac.size()), CHIP_ERROR_INTERNAL);

// Re-extract DN based on final generated cert
icac_dn = ChipDN{};
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(icac, icac_dn));

PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsIntermediateCertificateStorage, key,
ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, icac.data(), static_cast<uint16_t>(icac.size()))));
}
@@ -178,8 +318,8 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation(
ReturnErrorOnFailure(noc_dn.AddCATs(cats));

ChipLogProgress(Controller, "Generating NOC");
X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, icac_dn };
return NewNodeOperationalX509Cert(noc_request, pubkey, mIntermediateIssuer, noc);
return IssueX509Cert(mNow, mValidity, icac_dn, noc_dn, CertType::kNoc, mUseMaximallySizedCerts, pubkey, mIntermediateIssuer,
noc);
}

CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce,
@@ -227,16 +367,16 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan
ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));

chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan nocSpan(noc.Get(), kMaxCHIPDERCertLength);
ReturnErrorCodeIf(!noc.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan nocSpan(noc.Get(), kMaxDERCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> icac;
ReturnErrorCodeIf(!icac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan icacSpan(icac.Get(), kMaxCHIPDERCertLength);
ReturnErrorCodeIf(!icac.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan icacSpan(icac.Get(), kMaxDERCertLength);

chip::Platform::ScopedMemoryBuffer<uint8_t> rcac;
ReturnErrorCodeIf(!rcac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan rcacSpan(rcac.Get(), kMaxCHIPDERCertLength);
ReturnErrorCodeIf(!rcac.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
MutableByteSpan rcacSpan(rcac.Get(), kMaxDERCertLength);

ReturnErrorOnFailure(
GenerateNOCChainAfterValidation(assignedId, mNextFabricId, chip::kUndefinedCATs, pubkey, rcacSpan, icacSpan, nocSpan));
7 changes: 5 additions & 2 deletions src/controller/ExampleOperationalCredentialsIssuer.h
Original file line number Diff line number Diff line change
@@ -65,6 +65,8 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent
mNodeIdRequested = true;
}

void SetMaximallyLargeCertsUsed(bool areMaximallyLargeCertsUsed) { mUseMaximallySizedCerts = areMaximallyLargeCertsUsed; }

void SetFabricIdForNextNOCRequest(FabricId fabricId) override { mNextFabricId = fabricId; }

/**
@@ -108,15 +110,16 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent
Crypto::P256Keypair mIssuer;
Crypto::P256Keypair mIntermediateIssuer;
bool mInitialized = false;
uint32_t mIssuerId = 0;
uint32_t mIntermediateIssuerId = 1;
uint32_t mIssuerId = 1;
uint32_t mIntermediateIssuerId = 2;
uint32_t mNow = 0;

// By default, let's set validity to 10 years
uint32_t mValidity = 365 * 24 * 60 * 60 * 10;

NodeId mNextAvailableNodeId = 1;
PersistentStorageDelegate * mStorage = nullptr;
bool mUseMaximallySizedCerts = false;

NodeId mNextRequestedNodeId = 1;
FabricId mNextFabricId = 1;
16 changes: 14 additions & 2 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
@@ -77,6 +77,8 @@ class OperationalCredentialsAdapter : public OperationalCredentialsDelegate
return mExampleOpCredsIssuer.GenerateNOCChainAfterValidation(nodeId, fabricId, cats, pubKey, rcac, icac, noc);
}

void SetMaximallyLargeCertsUsed(bool enabled) { mExampleOpCredsIssuer.SetMaximallyLargeCertsUsed(enabled); }

private:
CHIP_ERROR GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & csrNonce, const ByteSpan & attestationSignature,
const ByteSpan & attestationChallenge, const ByteSpan & DAC, const ByteSpan & PAI,
@@ -360,9 +362,10 @@ ChipError::StorageType pychip_OpCreds_AllocateController(OpCredsContext * contex

CATValues catValues;

if ((caseAuthTagLen + 1) > kMaxSubjectCATAttributeCount)
if (caseAuthTagLen > kMaxSubjectCATAttributeCount)
{
ChipLogError(Controller, "# of CASE Tags exceeds kMaxSubjectCATAttributeCount");
ChipLogError(Controller, "Too many of CASE Tags (%u) exceeds kMaxSubjectCATAttributeCount",
static_cast<unsigned>(caseAuthTagLen));
return CHIP_ERROR_INVALID_ARGUMENT.AsInteger();
}

@@ -414,6 +417,15 @@ ChipError::StorageType pychip_OpCreds_AllocateController(OpCredsContext * contex
return CHIP_NO_ERROR.AsInteger();
}

ChipError::StorageType pychip_OpCreds_SetMaximallyLargeCertsUsed(OpCredsContext * context, bool enabled)
{
VerifyOrReturnError(context != nullptr && context->mAdapter != nullptr, CHIP_ERROR_INCORRECT_STATE.AsInteger());

context->mAdapter->SetMaximallyLargeCertsUsed(enabled);

return CHIP_NO_ERROR.AsInteger();
}

void pychip_OpCreds_FreeDelegate(OpCredsContext * context)
{
Platform::Delete(context);
Loading