From c2da7619f1250b46b966338fb182bf7fa25cdcac Mon Sep 17 00:00:00 2001 From: Evgeni Margolis Date: Mon, 22 Aug 2022 14:42:47 -0700 Subject: [PATCH] TC_RR_1_1: Updated Method that Was Used to Generate Large Sized Operational 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. --- .../ExampleOperationalCredentialsIssuer.cpp | 39 ++++++++----------- src/credentials/CHIPCert.h | 7 ++++ src/credentials/GenerateChipX509Cert.cpp | 30 +++++++++++++- src/credentials/tests/TestChipCert.cpp | 36 +++++++++++++++++ src/tools/chip-cert/CertUtils.cpp | 4 +- src/tools/chip-cert/Cmd_GenCert.cpp | 26 ++++++------- src/tools/chip-cert/chip-cert.h | 12 +++--- 7 files changed, 109 insertions(+), 45 deletions(-) diff --git a/src/controller/ExampleOperationalCredentialsIssuer.cpp b/src/controller/ExampleOperationalCredentialsIssuer.cpp index 9c0b376b13716c..dc026dbbf88bfd 100644 --- a/src/controller/ExampleOperationalCredentialsIssuer.cpp +++ b/src/controller/ExampleOperationalCredentialsIssuer.cpp @@ -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 derBuf; ReturnErrorCodeIf(!derBuf.Alloc(kMaxDERCertLength), CHIP_ERROR_NO_MEMORY); @@ -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 paddedTlvBuf; ReturnErrorCodeIf(!paddedTlvBuf.Alloc(kMaxCHIPCertLength + kMaxCertPaddingLength), CHIP_ERROR_NO_MEMORY); @@ -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(kMaxDERCertLength - kDERCertDnEncodingOverhead - derSpan.size()); - int tlvPaddingLen = static_cast(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(kDERCertDnEncodingOverhead)) / 2; - tlvPaddingLen = (tlvPaddingLen - static_cast(kTLVCertDnEncodingOverhead)) / 2; - } - + int derPaddingLen = static_cast(kMaxDERCertLength - kDERCertFutureExtEncodingOverhead - derSpan.size()); + int tlvPaddingLen = static_cast(kTLVDesiredSize - kTLVCertFutureExtEncodingOverhead - paddedTlvSpan.size()); size_t paddingLen = 0; if (derPaddingLen >= 1 && tlvPaddingLen >= 1) { @@ -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 futureExt; + FutureExtension ext = { ByteSpan(sOID_Extension_SubjectAltName), + ByteSpan(reinterpret_cast(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; } @@ -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(paddedDerSpan.size()), static_cast(paddedTlvSpan.size())); if (paddedDerSpan.size() <= kMaxDERCertLength && paddedTlvSpan.size() <= kMaxCHIPCertLength) { + ChipLogProgress(Controller, "Generated maximized certificate with %u DER bytes, %u TLV bytes", + static_cast(paddedDerSpan.size()), static_cast(paddedTlvSpan.size())); return CopySpanToMutableSpan(paddedDerSpan, outX509Cert); } } diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 2f3fb7f0d76253..d95671ee74c3a4 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -507,6 +507,12 @@ CHIP_ERROR ConvertChipCertToX509Cert(const ByteSpan chipCert, MutableByteSpan & */ CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac); +struct FutureExtension +{ + ByteSpan OID; + ByteSpan Extension; +}; + struct X509CertRequestParams { int64_t SerialNumber; @@ -514,6 +520,7 @@ struct X509CertRequestParams uint32_t ValidityEnd; ChipDN SubjectDN; ChipDN IssuerDN; + Optional FutureExt; }; /** diff --git a/src/credentials/GenerateChipX509Cert.cpp b/src/credentials/GenerateChipX509Cert.cpp index e1c6fe5002ce3b..b51c49e0174f22 100644 --- a/src/credentials/GenerateChipX509Cert.cpp +++ b/src/credentials/GenerateChipX509Cert.cpp @@ -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 & 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(futureExt.Value().OID.size()))); + + ASN1_START_OCTET_STRING_ENCAPSULATED + { + ReturnErrorOnFailure(writer.PutOctetString(futureExt.Value().Extension.data(), + static_cast(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 & futureExt, ASN1Writer & writer) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -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; } @@ -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; diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index 97c00d7ca555bd..ea66eaacf7e5cd 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -102,6 +102,14 @@ static const BitFlags sKCandCR(sKC, sCR); static const BitFlags sKCandEO(sKC, sEO); static const BitFlags sKCandDO(sKC, sDO); +constexpr uint8_t sOID_Extension_SubjectAltName[] = { 0x55, 0x1d, 0x11 }; +constexpr char kExtension_SubjectAltName[] = "test@chip.org"; + +FutureExtension ext{ ByteSpan(sOID_Extension_SubjectAltName), + ByteSpan(reinterpret_cast(const_cast(kExtension_SubjectAltName)), + strlen(kExtension_SubjectAltName)) }; +Optional kSubjectAltNameAsFutureExt(ext); + static CHIP_ERROR LoadTestCertSet01(ChipCertificateSet & certSet) { CHIP_ERROR err; @@ -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); @@ -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); @@ -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); diff --git a/src/tools/chip-cert/CertUtils.cpp b/src/tools/chip-cert/CertUtils.cpp index f929e1446317e2..0c22503628bc31 100644 --- a/src/tools/chip-cert/CertUtils.cpp +++ b/src/tools/chip-cert/CertUtils.cpp @@ -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; @@ -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) { diff --git a/src/tools/chip-cert/Cmd_GenCert.cpp b/src/tools/chip-cert/Cmd_GenCert.cpp index e944bdb07733c9..6a0a1c6784335d 100644 --- a/src/tools/chip-cert/Cmd_GenCert.cpp +++ b/src/tools/chip-cert/Cmd_GenCert.cpp @@ -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; diff --git a/src/tools/chip-cert/chip-cert.h b/src/tools/chip-cert/chip-cert.h index 6bdd41855c3a01..39e32fbe580c8b 100644 --- a/src/tools/chip-cert/chip-cert.h +++ b/src/tools/chip-cert/chip-cert.h @@ -141,7 +141,7 @@ enum AttCertType kAttCertType_DAC, /**< Device Attestation Certificate (DAC). */ }; -struct FutureExtension +struct FutureExtensionWithNID { int nid; const char * info; @@ -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,