Skip to content

Commit

Permalink
TC_RR_1_1: Updated Method that Was Used to Generate Large Sized Opera…
Browse files Browse the repository at this point in the history
…tional Certificates.

Instead of padding the subject DN this method adds size by adding Future Extension.

This new approach doesen't have certain limitations compare to the previous approach and
allows generation of larger certificates of approximate sizes:
{RCAC, ICAC, NOC} ~ {400, 400, 350} bytes in TLV encoded form.
  • Loading branch information
emargolis committed Aug 22, 2022
1 parent 6681060 commit c2da761
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 45 deletions.
39 changes: 17 additions & 22 deletions src/controller/ExampleOperationalCredentialsIssuer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipD
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;
constexpr size_t kDERCertFutureExtEncodingOverhead = 12;
constexpr size_t kTLVCertFutureExtEncodingOverhead = kDERCertFutureExtEncodingOverhead + 5;
constexpr size_t kMaxCertPaddingLength = 200;
constexpr size_t kTLVDesiredSize = kMaxCHIPCertLength;
constexpr uint8_t sOID_Extension_SubjectAltName[] = { 0x55, 0x1d, 0x11 };

Platform::ScopedMemoryBuffer<uint8_t> derBuf;
ReturnErrorCodeIf(!derBuf.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
Expand Down Expand Up @@ -84,7 +85,7 @@ CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipD
return CHIP_ERROR_INVALID_ARGUMENT;
}

if (maximizeSize && (desiredDn.RDNCount() < CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES))
if (maximizeSize)
{
Platform::ScopedMemoryBuffer<uint8_t> paddedTlvBuf;
ReturnErrorCodeIf(!paddedTlvBuf.Alloc(kMaxCHIPCertLength + kMaxCertPaddingLength), CHIP_ERROR_NO_MEMORY);
Expand All @@ -99,15 +100,8 @@ CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipD
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;
}

int derPaddingLen = static_cast<int>(kMaxDERCertLength - kDERCertFutureExtEncodingOverhead - derSpan.size());
int tlvPaddingLen = static_cast<int>(kTLVDesiredSize - kTLVCertFutureExtEncodingOverhead - paddedTlvSpan.size());
size_t paddingLen = 0;
if (derPaddingLen >= 1 && tlvPaddingLen >= 1)
{
Expand All @@ -119,24 +113,25 @@ CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipD
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);
Optional<FutureExtension> futureExt;
FutureExtension ext = { ByteSpan(sOID_Extension_SubjectAltName),
ByteSpan(reinterpret_cast<uint8_t *>(fillerBuf.Get()), paddingLen) };
futureExt.SetValue(ext);

switch (certType)
{
case CertType::kRcac: {
X509CertRequestParams rcacRequest = { serialNumber, now, now + validity, certDn, certDn };
X509CertRequestParams rcacRequest = { serialNumber, now, now + validity, desiredDn, desiredDn, futureExt };
ReturnErrorOnFailure(NewRootX509Cert(rcacRequest, issuerKeypair, paddedDerSpan));
break;
}
case CertType::kIcac: {
X509CertRequestParams icacRequest = { serialNumber, now, now + validity, certDn, issuerDn };
X509CertRequestParams icacRequest = { serialNumber, now, now + validity, desiredDn, issuerDn, futureExt };
ReturnErrorOnFailure(NewICAX509Cert(icacRequest, subjectPublicKey, issuerKeypair, paddedDerSpan));
break;
}
case CertType::kNoc: {
X509CertRequestParams nocRequest = { serialNumber, now, now + validity, certDn, issuerDn };
X509CertRequestParams nocRequest = { serialNumber, now, now + validity, desiredDn, issuerDn, futureExt };
ReturnErrorOnFailure(NewNodeOperationalX509Cert(nocRequest, subjectPublicKey, issuerKeypair, paddedDerSpan));
break;
}
Expand All @@ -146,10 +141,10 @@ CHIP_ERROR IssueX509Cert(uint32_t now, uint32_t validity, ChipDN issuerDn, ChipD

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)
{
ChipLogProgress(Controller, "Generated maximized certificate with %u DER bytes, %u TLV bytes",
static_cast<unsigned>(paddedDerSpan.size()), static_cast<unsigned>(paddedTlvSpan.size()));
return CopySpanToMutableSpan(paddedDerSpan, outX509Cert);
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -507,13 +507,20 @@ CHIP_ERROR ConvertChipCertToX509Cert(const ByteSpan chipCert, MutableByteSpan &
*/
CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac);

struct FutureExtension
{
ByteSpan OID;
ByteSpan Extension;
};

struct X509CertRequestParams
{
int64_t SerialNumber;
uint32_t ValidityStart;
uint32_t ValidityEnd;
ChipDN SubjectDN;
ChipDN IssuerDN;
Optional<FutureExtension> FutureExt;
};

/**
Expand Down
30 changes: 28 additions & 2 deletions src/credentials/GenerateChipX509Cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,31 @@ CHIP_ERROR EncodeNOCSpecificExtensions(ASN1Writer & writer)
return err;
}

CHIP_ERROR EncodeExtensions(bool isCA, const Crypto::P256PublicKey & SKI, const Crypto::P256PublicKey & AKI, ASN1Writer & writer)
CHIP_ERROR EncodeFutureExtension(const Optional<FutureExtension> & futureExt, ASN1Writer & writer)
{
CHIP_ERROR err = CHIP_NO_ERROR;

VerifyOrReturnError(futureExt.HasValue(), CHIP_NO_ERROR);

ASN1_START_SEQUENCE
{
ReturnErrorOnFailure(writer.PutObjectId(futureExt.Value().OID.data(), static_cast<uint16_t>(futureExt.Value().OID.size())));

ASN1_START_OCTET_STRING_ENCAPSULATED
{
ReturnErrorOnFailure(writer.PutOctetString(futureExt.Value().Extension.data(),
static_cast<uint16_t>(futureExt.Value().Extension.size())));
}
ASN1_END_ENCAPSULATED;
}
ASN1_END_SEQUENCE;

exit:
return err;
}

CHIP_ERROR EncodeExtensions(bool isCA, const Crypto::P256PublicKey & SKI, const Crypto::P256PublicKey & AKI,
const Optional<FutureExtension> & futureExt, ASN1Writer & writer)
{
CHIP_ERROR err = CHIP_NO_ERROR;

Expand All @@ -250,6 +274,8 @@ CHIP_ERROR EncodeExtensions(bool isCA, const Crypto::P256PublicKey & SKI, const
ReturnErrorOnFailure(EncodeSubjectKeyIdentifierExtension(SKI, writer));

ReturnErrorOnFailure(EncodeAuthorityKeyIdentifierExtension(AKI, writer));

ReturnErrorOnFailure(EncodeFutureExtension(futureExt, writer));
}
ASN1_END_SEQUENCE;
}
Expand Down Expand Up @@ -336,7 +362,7 @@ CHIP_ERROR EncodeTBSCert(const X509CertRequestParams & requestParams, const Cryp
ReturnErrorOnFailure(EncodeSubjectPublicKeyInfo(subjectPubkey, writer));

// certificate extensions
ReturnErrorOnFailure(EncodeExtensions(isCA, subjectPubkey, issuerPubkey, writer));
ReturnErrorOnFailure(EncodeExtensions(isCA, subjectPubkey, issuerPubkey, requestParams.FutureExt, writer));
}
ASN1_END_SEQUENCE;

Expand Down
36 changes: 36 additions & 0 deletions src/credentials/tests/TestChipCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ static const BitFlags<KeyUsageFlags> sKCandCR(sKC, sCR);
static const BitFlags<KeyUsageFlags> sKCandEO(sKC, sEO);
static const BitFlags<KeyUsageFlags> sKCandDO(sKC, sDO);

constexpr uint8_t sOID_Extension_SubjectAltName[] = { 0x55, 0x1d, 0x11 };
constexpr char kExtension_SubjectAltName[] = "[email protected]";

FutureExtension ext{ ByteSpan(sOID_Extension_SubjectAltName),
ByteSpan(reinterpret_cast<uint8_t *>(const_cast<char *>(kExtension_SubjectAltName)),
strlen(kExtension_SubjectAltName)) };
Optional<FutureExtension> kSubjectAltNameAsFutureExt(ext);

static CHIP_ERROR LoadTestCertSet01(ChipCertificateSet & certSet)
{
CHIP_ERROR err;
Expand Down Expand Up @@ -1247,6 +1255,15 @@ static void TestChipCert_GenerateRootCert(nlTestSuite * inSuite, void * inContex

NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test with FutureExtension
X509CertRequestParams root_params2 = { 1234, 631161876, 729942000, root_dn, root_dn, kSubjectAltNameAsFutureExt };
MutableByteSpan signed_cert_span2(signed_cert);
NL_TEST_ASSERT(inSuite, NewRootX509Cert(root_params2, keypair, signed_cert_span2) == CHIP_NO_ERROR);
outCert = MutableByteSpan(outCertBuf);

NL_TEST_ASSERT(inSuite, ConvertX509CertToChipCert(signed_cert_span2, outCert) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test error case: root cert subject provided ICA OID Attribute.
root_params.SubjectDN.Clear();
NL_TEST_ASSERT(inSuite, root_params.SubjectDN.AddAttribute_MatterICACId(0xabcdabcd) == CHIP_NO_ERROR);
Expand Down Expand Up @@ -1325,6 +1342,15 @@ static void TestChipCert_GenerateICACert(nlTestSuite * inSuite, void * inContext

NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test with FutureExtension
X509CertRequestParams ica_params2 = { 1234, 631161876, 729942000, ica_dn, issuer_dn, kSubjectAltNameAsFutureExt };
MutableByteSpan signed_cert_span2(signed_cert);
NL_TEST_ASSERT(inSuite, NewICAX509Cert(ica_params2, ica_keypair.Pubkey(), keypair, signed_cert_span2) == CHIP_NO_ERROR);
outCert = MutableByteSpan(outCertBuf);

NL_TEST_ASSERT(inSuite, ConvertX509CertToChipCert(signed_cert_span2, outCert) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test error case: ICA cert subject provided a node ID attribute
ica_params.SubjectDN.Clear();
NL_TEST_ASSERT(inSuite, ica_params.SubjectDN.AddAttribute_MatterNodeId(0xABCDABCDABCDABCD) == CHIP_NO_ERROR);
Expand Down Expand Up @@ -1372,6 +1398,16 @@ static void TestChipCert_GenerateNOCRoot(nlTestSuite * inSuite, void * inContext

NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test with FutureExtension
X509CertRequestParams noc_params2 = { 123456, 631161876, 729942000, noc_dn, issuer_dn, kSubjectAltNameAsFutureExt };
MutableByteSpan signed_cert_span2(signed_cert);
NL_TEST_ASSERT(inSuite,
NewNodeOperationalX509Cert(noc_params2, noc_keypair.Pubkey(), keypair, signed_cert_span2) == CHIP_NO_ERROR);
outCert = MutableByteSpan(outCertBuf);

NL_TEST_ASSERT(inSuite, ConvertX509CertToChipCert(signed_cert_span2, outCert) == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, DecodeChipCert(outCert, certData) == CHIP_NO_ERROR);

// Test error case: NOC cert subject doesn't have NodeId attribute
noc_params.SubjectDN.Clear();
NL_TEST_ASSERT(inSuite, noc_params.SubjectDN.AddAttribute_MatterFabricId(0xFAB00000FAB00001) == CHIP_NO_ERROR);
Expand Down
4 changes: 2 additions & 2 deletions src/tools/chip-cert/CertUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ bool WriteChipCert(const char * fileName, const ByteSpan & chipCert, CertFormat
}

bool MakeCert(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom,
uint32_t validDays, int pathLen, const FutureExtension * futureExts, uint8_t futureExtsCount, X509 * newCert,
uint32_t validDays, int pathLen, const FutureExtensionWithNID * futureExts, uint8_t futureExtsCount, X509 * newCert,
EVP_PKEY * newKey, CertStructConfig & certConfig)
{
bool res = true;
Expand Down Expand Up @@ -925,7 +925,7 @@ bool MakeCert(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP
}

CHIP_ERROR MakeCertChipTLV(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey,
const struct tm & validFrom, uint32_t validDays, int pathLen, const FutureExtension * futureExts,
const struct tm & validFrom, uint32_t validDays, int pathLen, const FutureExtensionWithNID * futureExts,
uint8_t futureExtsCount, X509 * x509Cert, EVP_PKEY * newKey, CertStructConfig & certConfig,
MutableByteSpan & chipCert)
{
Expand Down
26 changes: 13 additions & 13 deletions src/tools/chip-cert/Cmd_GenCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,19 +243,19 @@ OptionSet *gCmdOptionSets[] =
// clang-format on

ToolChipDN gSubjectDN;
uint8_t gCertType = kCertType_NotSpecified;
int gPathLengthConstraint = kPathLength_NotSpecified;
bool gSelfSign = false;
const char * gCACertFileName = nullptr;
const char * gCAKeyFileName = nullptr;
const char * gInKeyFileName = nullptr;
const char * gOutCertFileName = nullptr;
const char * gOutKeyFileName = nullptr;
CertFormat gOutCertFormat = kCertFormat_Default;
KeyFormat gOutKeyFormat = kKeyFormat_Default;
uint32_t gValidDays = kCertValidDays_Undefined;
FutureExtension gFutureExtensions[3] = { { 0, nullptr } };
uint8_t gFutureExtensionsCount = 0;
uint8_t gCertType = kCertType_NotSpecified;
int gPathLengthConstraint = kPathLength_NotSpecified;
bool gSelfSign = false;
const char * gCACertFileName = nullptr;
const char * gCAKeyFileName = nullptr;
const char * gInKeyFileName = nullptr;
const char * gOutCertFileName = nullptr;
const char * gOutKeyFileName = nullptr;
CertFormat gOutCertFormat = kCertFormat_Default;
KeyFormat gOutKeyFormat = kKeyFormat_Default;
uint32_t gValidDays = kCertValidDays_Undefined;
FutureExtensionWithNID gFutureExtensions[3] = { { 0, nullptr } };
uint8_t gFutureExtensionsCount = 0;
struct tm gValidFrom;
CertStructConfig gCertConfig;

Expand Down
12 changes: 6 additions & 6 deletions src/tools/chip-cert/chip-cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ enum AttCertType
kAttCertType_DAC, /**< Device Attestation Certificate (DAC). */
};

struct FutureExtension
struct FutureExtensionWithNID
{
int nid;
const char * info;
Expand Down Expand Up @@ -410,12 +410,12 @@ extern bool WriteCert(const char * fileName, X509 * cert, CertFormat certFmt);
extern bool WriteChipCert(const char * fileName, const chip::ByteSpan & cert, CertFormat certFmt);

extern bool MakeCert(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom,
uint32_t validDays, int pathLen, const FutureExtension * futureExts, uint8_t futureExtsCount, X509 * newCert,
EVP_PKEY * newKey, CertStructConfig & certConfig);
uint32_t validDays, int pathLen, const FutureExtensionWithNID * futureExts, uint8_t futureExtsCount,
X509 * newCert, EVP_PKEY * newKey, CertStructConfig & certConfig);
extern CHIP_ERROR MakeCertChipTLV(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey,
const struct tm & validFrom, uint32_t validDays, int pathLen, const FutureExtension * futureExts,
uint8_t futureExtsCount, X509 * x509Cert, EVP_PKEY * newKey, CertStructConfig & certConfig,
chip::MutableByteSpan & chipCert);
const struct tm & validFrom, uint32_t validDays, int pathLen,
const FutureExtensionWithNID * futureExts, uint8_t futureExtsCount, X509 * x509Cert,
EVP_PKEY * newKey, CertStructConfig & certConfig, chip::MutableByteSpan & chipCert);
extern bool ResignCert(X509 * cert, X509 * caCert, EVP_PKEY * caKey);

extern bool MakeAttCert(AttCertType attCertType, const char * subjectCN, uint16_t subjectVID, uint16_t subjectPID,
Expand Down

0 comments on commit c2da761

Please sign in to comment.