Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to specify certificate validity period on Darwin. #26403

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1Un
// times, which in consuming code can create a conversion from CHIP epoch 0 seconds to 99991231235959Z
// for NotBefore, which is not conventional.
//
// If an original X509 certificate encloses a NotBefore time that this the CHIP Epoch itself, 2000-01-01
// If an original X509 certificate encloses a NotBefore time that is the CHIP Epoch itself, 2000-01-01
// 00:00:00, the resultant X509 certificate in a conversion back from CHIP TLV format using this time
// conversion method will instead enclose the NotBefore time 99991231235959Z, which will invalidiate the
// TBS signature. Thus, certificates with this specific attribute are not usable with this code.
Expand Down
3 changes: 2 additions & 1 deletion src/credentials/GenerateChipX509Cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ CHIP_ERROR EncodeTBSCert(const X509CertRequestParams & requestParams, const Cryp
bool isCA;

VerifyOrReturnError(requestParams.SerialNumber >= 0, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(requestParams.ValidityEnd >= requestParams.ValidityStart, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(requestParams.ValidityEnd == kNullCertTime || requestParams.ValidityEnd >= requestParams.ValidityStart,
CHIP_ERROR_INVALID_ARGUMENT);

ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
isCA = (certType == kCertType_ICA || certType == kCertType_Root);
Expand Down
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