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,