From 674f7641f21843b8050f02cb203c28ca905042d1 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 9 May 2023 11:41:06 -0400 Subject: [PATCH] Add a way to specify certificate validity period on Darwin. (#26403) * Add a way to specify certificate validity period on Darwin. * Address review comments. * Address review comment. * Address phrasing comment. --- src/darwin/Framework/CHIP/MTRCertificates.h | 58 ++++- src/darwin/Framework/CHIP/MTRCertificates.mm | 61 ++++- .../CHIP/MTROperationalCredentialsDelegate.h | 17 +- .../CHIP/MTROperationalCredentialsDelegate.mm | 64 +++-- .../Framework/CHIPTests/MTRCertificateTests.m | 237 +++++++++++++++++- .../Framework/CHIPTests/MTRDeviceTests.m | 2 + 6 files changed, 402 insertions(+), 37 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRCertificates.h b/src/darwin/Framework/CHIP/MTRCertificates.h index 268ee3e223ac30..9b804dd05339de 100644 --- a/src/darwin/Framework/CHIP/MTRCertificates.h +++ b/src/darwin/Framework/CHIP/MTRCertificates.h @@ -44,14 +44,31 @@ NS_ASSUME_NONNULL_BEGIN * If fabricID is not nil, it will be included in the subject DN of the * certificate. In this case it must be a valid Matter fabric id. * + * validityPeriod specifies when the certificate will be valid. Note that + * there is currently no mechanism available in Matter to update or rotate + * the root certificate of a fabric installed on a device. A certificate with + * no expiration time can be created by specifying [NSDate distantFuture] for + * the end of the period. + * * On failure returns nil and if "error" is not null sets *error to the relevant * error. */ + (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypair issuerID:(NSNumber * _Nullable)issuerID fabricID:(NSNumber * _Nullable)fabricID + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing _Nullable * _Nullable)error - API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); + MTR_NEWLY_AVAILABLE; + +/** + * As above, but defaults to a 10-year validity period starting now. + */ ++ (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypair + issuerID:(NSNumber * _Nullable)issuerID + fabricID:(NSNumber * _Nullable)fabricID + error:(NSError * __autoreleasing _Nullable * _Nullable)error + API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) + MTR_NEWLY_DEPRECATED("Please use the version that specifies an explicit validity period"); /** * Create an intermediate X.509 DER encoded certificate that has the @@ -66,6 +83,10 @@ NS_ASSUME_NONNULL_BEGIN * If fabricID is not nil, it will be included in the subject DN of the * certificate. In this case it must be a valid Matter fabric id. * + * validityPeriod specifies when the certificate will be valid. A certificate + * with no expiration time can be created by specifying [NSDate distantFuture] + * for the end of the period. + * * On failure returns nil and if "error" is not null sets *error to the relevant * error. */ @@ -74,8 +95,21 @@ NS_ASSUME_NONNULL_BEGIN intermediatePublicKey:(SecKeyRef)intermediatePublicKey issuerID:(NSNumber * _Nullable)issuerID fabricID:(NSNumber * _Nullable)fabricID + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing _Nullable * _Nullable)error - API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); + MTR_NEWLY_AVAILABLE; + +/** + * As above, but defaults to a 10-year validity period starting now. + */ ++ (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id)rootKeypair + rootCertificate:(MTRCertificateDERBytes)rootCertificate + intermediatePublicKey:(SecKeyRef)intermediatePublicKey + issuerID:(NSNumber * _Nullable)issuerID + fabricID:(NSNumber * _Nullable)fabricID + error:(NSError * __autoreleasing _Nullable * _Nullable)error + API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) + MTR_NEWLY_DEPRECATED("Please use the version that specifies an explicit validity period"); /** * Create an X.509 DER encoded certificate that has the @@ -95,6 +129,10 @@ NS_ASSUME_NONNULL_BEGIN * 3 numbers, which are expected to be 32-bit unsigned Case Authenticated Tag * values. * + * validityPeriod specifies when the certificate will be valid. A certificate + * with no expiration time can be created by specifying [NSDate distantFuture] + * for the end of the period. + * * On failure returns nil and if "error" is not null sets *error to the relevant * error. */ @@ -104,8 +142,22 @@ NS_ASSUME_NONNULL_BEGIN fabricID:(NSNumber *)fabricID nodeID:(NSNumber *)nodeID caseAuthenticatedTags:(NSSet * _Nullable)caseAuthenticatedTags + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing _Nullable * _Nullable)error - API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); + MTR_NEWLY_AVAILABLE; + +/** + * As above, but defaults to a 10-year validity period starting now. + */ ++ (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id)signingKeypair + signingCertificate:(MTRCertificateDERBytes)signingCertificate + operationalPublicKey:(SecKeyRef)operationalPublicKey + fabricID:(NSNumber *)fabricID + nodeID:(NSNumber *)nodeID + caseAuthenticatedTags:(NSSet * _Nullable)caseAuthenticatedTags + error:(NSError * __autoreleasing _Nullable * _Nullable)error + API_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) + MTR_NEWLY_DEPRECATED("Please use the version that specifies an explicit validity period"); /** * Check whether the given keypair's public key matches the given certificate's diff --git a/src/darwin/Framework/CHIP/MTRCertificates.mm b/src/darwin/Framework/CHIP/MTRCertificates.mm index a8c28f32ea95ec..149f1c58b3a6e2 100644 --- a/src/darwin/Framework/CHIP/MTRCertificates.mm +++ b/src/darwin/Framework/CHIP/MTRCertificates.mm @@ -39,11 +39,13 @@ + (void)initialize + (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypair issuerID:(NSNumber * _Nullable)issuerID fabricID:(NSNumber * _Nullable)fabricID + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing *)error { MTR_LOG_DEFAULT("Generating root certificate"); NSData * rootCert = nil; - CHIP_ERROR err = MTROperationalCredentialsDelegate::GenerateRootCertificate(keypair, issuerID, fabricID, &rootCert); + CHIP_ERROR err + = MTROperationalCredentialsDelegate::GenerateRootCertificate(keypair, issuerID, fabricID, validityPeriod, &rootCert); if (error) { *error = [MTRError errorForCHIPErrorCode:err]; } @@ -55,17 +57,29 @@ + (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypai return rootCert; } ++ (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypair + issuerID:(NSNumber * _Nullable)issuerID + fabricID:(NSNumber * _Nullable)fabricID + error:(NSError * __autoreleasing *)error +{ + auto * validityPeriod = + [[NSDateInterval alloc] initWithStartDate:[NSDate now] + duration:MTROperationalCredentialsDelegate::kCertificateDefaultValiditySecs]; + return [self createRootCertificate:keypair issuerID:issuerID fabricID:fabricID validityPeriod:validityPeriod error:error]; +} + + (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id)rootKeypair rootCertificate:(MTRCertificateDERBytes)rootCertificate intermediatePublicKey:(SecKeyRef)intermediatePublicKey issuerID:(NSNumber * _Nullable)issuerID fabricID:(NSNumber * _Nullable)fabricID + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing *)error { MTR_LOG_DEFAULT("Generating intermediate certificate"); NSData * intermediate = nil; CHIP_ERROR err = MTROperationalCredentialsDelegate::GenerateIntermediateCertificate( - rootKeypair, rootCertificate, intermediatePublicKey, issuerID, fabricID, &intermediate); + rootKeypair, rootCertificate, intermediatePublicKey, issuerID, fabricID, validityPeriod, &intermediate); if (error) { *error = [MTRError errorForCHIPErrorCode:err]; } @@ -77,18 +91,38 @@ + (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id)rootKeypair + rootCertificate:(MTRCertificateDERBytes)rootCertificate + intermediatePublicKey:(SecKeyRef)intermediatePublicKey + issuerID:(NSNumber * _Nullable)issuerID + fabricID:(NSNumber * _Nullable)fabricID + error:(NSError * __autoreleasing *)error +{ + auto * validityPeriod = + [[NSDateInterval alloc] initWithStartDate:[NSDate now] + duration:MTROperationalCredentialsDelegate::kCertificateDefaultValiditySecs]; + return [self createIntermediateCertificate:rootKeypair + rootCertificate:rootCertificate + intermediatePublicKey:intermediatePublicKey + issuerID:issuerID + fabricID:fabricID + validityPeriod:validityPeriod + error:error]; +} + + (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id)signingKeypair signingCertificate:(MTRCertificateDERBytes)signingCertificate operationalPublicKey:(SecKeyRef)operationalPublicKey fabricID:(NSNumber *)fabricID nodeID:(NSNumber *)nodeID caseAuthenticatedTags:(NSSet * _Nullable)caseAuthenticatedTags + validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing _Nullable * _Nullable)error { MTR_LOG_DEFAULT("Generating operational certificate"); NSData * opcert = nil; CHIP_ERROR err = MTROperationalCredentialsDelegate::GenerateOperationalCertificate( - signingKeypair, signingCertificate, operationalPublicKey, fabricID, nodeID, caseAuthenticatedTags, &opcert); + signingKeypair, signingCertificate, operationalPublicKey, fabricID, nodeID, caseAuthenticatedTags, validityPeriod, &opcert); if (error) { *error = [MTRError errorForCHIPErrorCode:err]; } @@ -100,6 +134,27 @@ + (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id return opcert; } ++ (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id)signingKeypair + signingCertificate:(MTRCertificateDERBytes)signingCertificate + operationalPublicKey:(SecKeyRef)operationalPublicKey + fabricID:(NSNumber *)fabricID + nodeID:(NSNumber *)nodeID + caseAuthenticatedTags:(NSSet * _Nullable)caseAuthenticatedTags + error:(NSError * __autoreleasing _Nullable * _Nullable)error +{ + auto * validityPeriod = + [[NSDateInterval alloc] initWithStartDate:[NSDate now] + duration:MTROperationalCredentialsDelegate::kCertificateDefaultValiditySecs]; + return [self createOperationalCertificate:signingKeypair + signingCertificate:signingCertificate + operationalPublicKey:operationalPublicKey + fabricID:fabricID + nodeID:nodeID + caseAuthenticatedTags:caseAuthenticatedTags + validityPeriod:validityPeriod + error:error]; +} + + (BOOL)keypair:(id)keypair matchesCertificate:(NSData *)certificate { P256PublicKey keypairPubKey; diff --git a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h index a824971cb8abb3..8a5ec3bdbd9fd4 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h @@ -92,7 +92,7 @@ class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCr // // The outparam must not be null and is set to nil on errors. static CHIP_ERROR GenerateRootCertificate(id keypair, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, - NSData * _Nullable __autoreleasing * _Nonnull rootCert); + NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull rootCert); // Generate an intermediate DER-encoded X.509 certificate for the given root // and intermediate public key. If issuerId is provided, it is used; @@ -102,21 +102,26 @@ class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCr // The outparam must not be null and is set to nil on errors. static CHIP_ERROR GenerateIntermediateCertificate(id rootKeypair, NSData * rootCertificate, SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, - NSData * _Nullable __autoreleasing * _Nonnull intermediateCert); + NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull intermediateCert); // Generate an operational DER-encoded X.509 certificate for the given // signing certificate and operational public key, using the given fabric // id, node id, and CATs. static CHIP_ERROR GenerateOperationalCertificate(id signingKeypair, NSData * signingCertificate, SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId, NSSet * _Nullable caseAuthenticatedTags, - NSData * _Nullable __autoreleasing * _Nonnull operationalCert); + NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull operationalCert); + + // 10 years. + static const uint32_t kCertificateDefaultValiditySecs = 10 * 365 * 24 * 60 * 60; private: - static bool ToChipEpochTime(uint32_t offset, uint32_t & epoch); + // notAfter times can represent "forever". + static bool ToChipNotAfterEpochTime(NSDate * date, uint32_t & epoch); + static bool ToChipEpochTime(NSDate * date, uint32_t & epoch); static CHIP_ERROR GenerateNOC(chip::Crypto::P256Keypair & signingKeypair, NSData * signingCertificate, chip::NodeId nodeId, chip::FabricId fabricId, const chip::CATValues & cats, const chip::Crypto::P256PublicKey & pubkey, - chip::MutableByteSpan & noc); + NSDateInterval * validityPeriod, chip::MutableByteSpan & noc); // Called asynchronously in response to the MTROperationalCertificateIssuer // calling the completion we passed it when asking it to generate a NOC @@ -135,8 +140,6 @@ class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCr chip::Crypto::IdentityProtectionKey mIPK; - static const uint32_t kCertificateValiditySecs = 365 * 24 * 60 * 60; - MTRPersistentStorageDelegateBridge * mStorage; chip::NodeId mDeviceBeingPaired = chip::kUndefinedNodeId; diff --git a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm index b14ac062784105..79af08e9bfe029 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.mm @@ -89,21 +89,22 @@ return CHIP_ERROR_INCORRECT_STATE; } - return GenerateNOC( - *mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey, noc); + auto * validityPeriod = [[NSDateInterval alloc] initWithStartDate:[NSDate now] duration:kCertificateDefaultValiditySecs]; + return GenerateNOC(*mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey, + validityPeriod, noc); } CHIP_ERROR MTROperationalCredentialsDelegate::GenerateNOC(P256Keypair & signingKeypair, NSData * signingCertificate, NodeId nodeId, - FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, MutableByteSpan & noc) + FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, NSDateInterval * validityPeriod, MutableByteSpan & noc) { uint32_t validityStart, validityEnd; - if (!ToChipEpochTime(0, validityStart)) { + if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) { MTR_LOG_ERROR("Failed in computing certificate validity start date"); return CHIP_ERROR_INTERNAL; } - if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { + if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) { MTR_LOG_ERROR("Failed in computing certificate validity end date"); return CHIP_ERROR_INTERNAL; } @@ -310,19 +311,37 @@ return AsByteSpan(mIntermediateCert); } -bool MTROperationalCredentialsDelegate::ToChipEpochTime(uint32_t offset, uint32_t & epoch) +bool MTROperationalCredentialsDelegate::ToChipNotAfterEpochTime(NSDate * date, uint32_t & epoch) +{ + if ([date isEqualToDate:[NSDate distantFuture]]) { + epoch = kNullCertTime; + return true; + } + + return ToChipEpochTime(date, epoch); +} + +bool MTROperationalCredentialsDelegate::ToChipEpochTime(NSDate * date, uint32_t & epoch) { - NSDate * date = [NSDate dateWithTimeIntervalSinceNow:offset]; NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; NSDateComponents * components = [calendar componentsInTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0] fromDate:date]; - uint16_t year = static_cast([components year]); - uint8_t month = static_cast([components month]); - uint8_t day = static_cast([components day]); - uint8_t hour = static_cast([components hour]); - uint8_t minute = static_cast([components minute]); - uint8_t second = static_cast([components second]); - return chip::CalendarToChipEpochTime(year, month, day, hour, minute, second, epoch); + if (CanCastTo(components.year)) { + uint16_t year = static_cast([components year]); + uint8_t month = static_cast([components month]); + uint8_t day = static_cast([components day]); + uint8_t hour = static_cast([components hour]); + uint8_t minute = static_cast([components minute]); + uint8_t second = static_cast([components second]); + if (chip::CalendarToChipEpochTime(year, month, day, hour, minute, second, epoch)) { + return true; + } + } + + MTR_LOG_ERROR( + "Year %lu is out of range for Matter epoch time. Please use [NSDate distantFuture] to represent \"never expires\".", + static_cast(components.year)); + return false; } namespace { @@ -337,7 +356,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) } // anonymous namespace CHIP_ERROR MTROperationalCredentialsDelegate::GenerateRootCertificate(id keypair, NSNumber * _Nullable issuerId, - NSNumber * _Nullable fabricId, NSData * _Nullable __autoreleasing * _Nonnull rootCert) + NSNumber * _Nullable fabricId, NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull rootCert) { *rootCert = nil; MTRP256KeypairBridge keypairBridge; @@ -354,12 +373,12 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) uint32_t validityStart, validityEnd; - if (!ToChipEpochTime(0, validityStart)) { + if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) { MTR_LOG_ERROR("Failed in computing certificate validity start date"); return CHIP_ERROR_INTERNAL; } - if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { + if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) { MTR_LOG_ERROR("Failed in computing certificate validity end date"); return CHIP_ERROR_INTERNAL; } @@ -373,7 +392,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) } CHIP_ERROR MTROperationalCredentialsDelegate::GenerateIntermediateCertificate(id rootKeypair, NSData * rootCertificate, - SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, + SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull intermediateCert) { *intermediateCert = nil; @@ -404,12 +423,12 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) uint32_t validityStart, validityEnd; - if (!ToChipEpochTime(0, validityStart)) { + if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) { MTR_LOG_ERROR("Failed in computing certificate validity start date"); return CHIP_ERROR_INTERNAL; } - if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { + if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) { MTR_LOG_ERROR("Failed in computing certificate validity end date"); return CHIP_ERROR_INTERNAL; } @@ -424,7 +443,8 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) CHIP_ERROR MTROperationalCredentialsDelegate::GenerateOperationalCertificate(id signingKeypair, NSData * signingCertificate, SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId, - NSSet * _Nullable caseAuthenticatedTags, NSData * _Nullable __autoreleasing * _Nonnull operationalCert) + NSSet * _Nullable caseAuthenticatedTags, NSDateInterval * validityPeriod, + NSData * _Nullable __autoreleasing * _Nonnull operationalCert) { *operationalCert = nil; @@ -459,7 +479,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) uint8_t nocBuffer[Controller::kMaxCHIPDERCertLength]; MutableByteSpan noc(nocBuffer); - ReturnErrorOnFailure(GenerateNOC(keypairBridge, signingCertificate, node, fabric, cats, pubKey, noc)); + ReturnErrorOnFailure(GenerateNOC(keypairBridge, signingCertificate, node, fabric, cats, pubKey, validityPeriod, noc)); *operationalCert = AsData(noc); return CHIP_NO_ERROR; diff --git a/src/darwin/Framework/CHIPTests/MTRCertificateTests.m b/src/darwin/Framework/CHIPTests/MTRCertificateTests.m index 449343066b9d1f..89860b8df2f997 100644 --- a/src/darwin/Framework/CHIPTests/MTRCertificateTests.m +++ b/src/darwin/Framework/CHIPTests/MTRCertificateTests.m @@ -21,11 +21,24 @@ #import "MTRTestKeys.h" -@interface MatterCertificateTests : XCTestCase +@interface MTRCertificateTests : XCTestCase @end -@implementation MatterCertificateTests +@implementation MTRCertificateTests + +/** + * Helper function for creating start dates rounded to the nearest second (and + * which can therefore be represented without data loss in certificates). + */ ++ (NSDate *)startDateWithTimeIntervalSinceNow:(NSTimeInterval)interval +{ + __auto_type * startDate = [NSDate dateWithTimeIntervalSinceNow:interval]; + // Round down to the nearest second, since the certificate bits will do that + // when they compute validity dates. + NSTimeInterval seconds = floor([startDate timeIntervalSinceReferenceDate]); + return [NSDate dateWithTimeIntervalSinceReferenceDate:seconds]; +} - (void)testGenerateRootCert { @@ -45,6 +58,64 @@ - (void)testGenerateRootCert XCTAssertEqualObjects(rootCert, derCert); } +- (void)testGenerateRootCertWithValidityPeriod +{ + __auto_type * testKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(testKeys); + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:100]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate duration:200]; + + __auto_type * rootCert = [MTRCertificates createRootCertificate:testKeys + issuerID:nil + fabricID:nil + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(rootCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:rootCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(rootCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + +- (void)testGenerateRootCertWithInfiniteValidity +{ + __auto_type * testKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(testKeys); + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:100]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate endDate:[NSDate distantFuture]]; + + __auto_type * rootCert = [MTRCertificates createRootCertificate:testKeys + issuerID:nil + fabricID:nil + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(rootCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:rootCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(rootCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + - (void)testGenerateIntermediateCert { __auto_type * rootKeys = [[MTRTestKeys alloc] init]; @@ -74,6 +145,80 @@ - (void)testGenerateIntermediateCert XCTAssertEqualObjects(intermediateCert, derCert); } +- (void)testGenerateIntermediateCertWithValidityPeriod +{ + __auto_type * rootKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + __auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys issuerID:nil fabricID:nil error:nil]; + XCTAssertNotNil(rootCert); + + __auto_type * intermediateKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(intermediateKeys); + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:300]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate duration:400]; + + __auto_type * intermediateCert = [MTRCertificates createIntermediateCertificate:rootKeys + rootCertificate:rootCert + intermediatePublicKey:intermediateKeys.publicKey + issuerID:nil + fabricID:nil + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(intermediateCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:intermediateCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(intermediateCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + +- (void)testGenerateIntermediateCertWithInfiniteValidity +{ + __auto_type * rootKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + __auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys issuerID:nil fabricID:nil error:nil]; + XCTAssertNotNil(rootCert); + + __auto_type * intermediateKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(intermediateKeys); + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:300]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate endDate:[NSDate distantFuture]]; + + __auto_type * intermediateCert = [MTRCertificates createIntermediateCertificate:rootKeys + rootCertificate:rootCert + intermediatePublicKey:intermediateKeys.publicKey + issuerID:nil + fabricID:nil + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(intermediateCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:intermediateCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(intermediateCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + - (void)testGenerateOperationalCertNoIntermediate { __auto_type * rootKeys = [[MTRTestKeys alloc] init]; @@ -110,6 +255,94 @@ - (void)testGenerateOperationalCertNoIntermediate XCTAssertEqualObjects(operationalCert, derCert); } +- (void)testGenerateOperationalCertNoIntermediateWithValidityPeriod +{ + __auto_type * rootKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + __auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys issuerID:nil fabricID:nil error:nil]; + XCTAssertNotNil(rootCert); + + __auto_type * operationalKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(operationalKeys); + + __auto_type * cats = [[NSMutableSet alloc] initWithCapacity:3]; + // High bits are identifier, low bits are version. + [cats addObject:@0x00010001]; + [cats addObject:@0x00020001]; + [cats addObject:@0x0003FFFF]; + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:1000]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate duration:500]; + + __auto_type * operationalCert = [MTRCertificates createOperationalCertificate:rootKeys + signingCertificate:rootCert + operationalPublicKey:operationalKeys.publicKey + fabricID:@1 + nodeID:@1 + caseAuthenticatedTags:cats + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(operationalCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:operationalCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(operationalCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + +- (void)testGenerateOperationalCertNoIntermediateWithInfiniteValidity +{ + __auto_type * rootKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + __auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys issuerID:nil fabricID:nil error:nil]; + XCTAssertNotNil(rootCert); + + __auto_type * operationalKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(operationalKeys); + + __auto_type * cats = [[NSMutableSet alloc] initWithCapacity:3]; + // High bits are identifier, low bits are version. + [cats addObject:@0x00010001]; + [cats addObject:@0x00020001]; + [cats addObject:@0x0003FFFF]; + + __auto_type * startDate = [MTRCertificateTests startDateWithTimeIntervalSinceNow:1000]; + __auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate endDate:[NSDate distantFuture]]; + + __auto_type * operationalCert = [MTRCertificates createOperationalCertificate:rootKeys + signingCertificate:rootCert + operationalPublicKey:operationalKeys.publicKey + fabricID:@1 + nodeID:@1 + caseAuthenticatedTags:cats + validityPeriod:validityPeriod + error:nil]; + XCTAssertNotNil(operationalCert); + + // Test round-trip through TLV format. + __auto_type * tlvCert = [MTRCertificates convertX509Certificate:operationalCert]; + XCTAssertNotNil(tlvCert); + + __auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert]; + XCTAssertNotNil(derCert); + + XCTAssertEqualObjects(operationalCert, derCert); + + __auto_type * certInfo = [[MTRCertificateInfo alloc] initWithTLVBytes:tlvCert]; + XCTAssertEqualObjects(validityPeriod.startDate, certInfo.notBefore); + XCTAssertEqualObjects(validityPeriod.endDate, certInfo.notAfter); +} + - (void)testGenerateOperationalCertWithIntermediate { __auto_type * rootKeys = [[MTRTestKeys alloc] init]; diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index 2a444a85ce0e81..00b1aaa9c24c8d 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -762,6 +762,8 @@ - (void)test010_ReadAllAttribute - (void)test011_ReadCachedAttribute { + // fprintf(stderr, "\n\nPID: %d\n\n\n", getpid()); + // sleep(10); MTRBaseDevice * device = GetConnectedDevice(); dispatch_queue_t queue = dispatch_get_main_queue(); XCTestExpectation * cleanSubscriptionExpectation = [self expectationWithDescription:@"Previous subscriptions cleaned"];