Skip to content

Commit

Permalink
Add a way to specify certificate validity period on Darwin.
Browse files Browse the repository at this point in the history
  • Loading branch information
bzbarsky-apple committed May 6, 2023
1 parent de1c64a commit e3f8d23
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 30 deletions.
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
50 changes: 47 additions & 3 deletions src/darwin/Framework/CHIP/MTRCertificates.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,27 @@ 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.
*
* 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 +79,8 @@ 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.
*
* On failure returns nil and if "error" is not null sets *error to the relevant
* error.
*/
Expand All @@ -74,8 +89,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 +123,8 @@ 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.
*
* On failure returns nil and if "error" is not null sets *error to the relevant
* error.
*/
Expand All @@ -104,8 +134,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
58 changes: 55 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,28 @@ + (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::kCertificateValiditySecs];
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 +90,37 @@ + (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::kCertificateValiditySecs];
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 +132,26 @@ + (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::kCertificateValiditySecs];
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 kCertificateValiditySecs = 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 e3f8d23

Please sign in to comment.