Skip to content

Commit

Permalink
Add a way to specify certificate validity period on Darwin. (#26403)
Browse files Browse the repository at this point in the history
* Add a way to specify certificate validity period on Darwin.

* Address review comments.

* Address review comment.

* Address phrasing comment.
  • Loading branch information
bzbarsky-apple authored May 9, 2023
1 parent 7bbd64d commit 2f70c89
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 37 deletions.
58 changes: 55 additions & 3 deletions src/darwin/Framework/CHIP/MTRCertificates.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MTRKeypair>)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<MTRKeypair>)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
Expand All @@ -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.
*/
Expand All @@ -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<MTRKeypair>)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
Expand All @@ -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.
*/
Expand All @@ -104,8 +142,22 @@ NS_ASSUME_NONNULL_BEGIN
fabricID:(NSNumber *)fabricID
nodeID:(NSNumber *)nodeID
caseAuthenticatedTags:(NSSet<NSNumber *> * _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<MTRKeypair>)signingKeypair
signingCertificate:(MTRCertificateDERBytes)signingCertificate
operationalPublicKey:(SecKeyRef)operationalPublicKey
fabricID:(NSNumber *)fabricID
nodeID:(NSNumber *)nodeID
caseAuthenticatedTags:(NSSet<NSNumber *> * _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
Expand Down
61 changes: 58 additions & 3 deletions src/darwin/Framework/CHIP/MTRCertificates.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ + (void)initialize
+ (MTRCertificateDERBytes _Nullable)createRootCertificate:(id<MTRKeypair>)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];
}
Expand All @@ -55,17 +57,29 @@ + (MTRCertificateDERBytes _Nullable)createRootCertificate:(id<MTRKeypair>)keypai
return rootCert;
}

+ (MTRCertificateDERBytes _Nullable)createRootCertificate:(id<MTRKeypair>)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<MTRKeypair>)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];
}
Expand All @@ -77,18 +91,38 @@ + (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id<MTRKeypair
return intermediate;
}

+ (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id<MTRKeypair>)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<MTRKeypair>)signingKeypair
signingCertificate:(MTRCertificateDERBytes)signingCertificate
operationalPublicKey:(SecKeyRef)operationalPublicKey
fabricID:(NSNumber *)fabricID
nodeID:(NSNumber *)nodeID
caseAuthenticatedTags:(NSSet<NSNumber *> * _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];
}
Expand All @@ -100,6 +134,27 @@ + (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id<MTRKeypair>
return opcert;
}

+ (MTRCertificateDERBytes _Nullable)createOperationalCertificate:(id<MTRKeypair>)signingKeypair
signingCertificate:(MTRCertificateDERBytes)signingCertificate
operationalPublicKey:(SecKeyRef)operationalPublicKey
fabricID:(NSNumber *)fabricID
nodeID:(NSNumber *)nodeID
caseAuthenticatedTags:(NSSet<NSNumber *> * _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<MTRKeypair>)keypair matchesCertificate:(NSData *)certificate
{
P256PublicKey keypairPubKey;
Expand Down
17 changes: 10 additions & 7 deletions src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MTRKeypair> 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;
Expand All @@ -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<MTRKeypair> 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<MTRKeypair> signingKeypair, NSData * signingCertificate,
SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId, NSSet<NSNumber *> * _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
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 2f70c89

Please sign in to comment.