From c10b4c4edee8390ac689856e6b171bab246c6151 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 6 May 2022 14:50:58 -0400 Subject: [PATCH 1/3] Add Darwin framework APIs for generating root/ICA certificates. --- .../Framework/CHIP.xcodeproj/project.pbxproj | 12 ++ src/darwin/Framework/CHIP/BUILD.gn | 2 + src/darwin/Framework/CHIP/CHIP.h | 1 + src/darwin/Framework/CHIP/CHIPCluster.mm | 3 +- .../CHIP/CHIPOperationalCredentialsDelegate.h | 24 +++- .../CHIPOperationalCredentialsDelegate.mm | 97 +++++++++++++++ .../Framework/CHIP/CHIPP256KeypairBridge.h | 3 + .../Framework/CHIP/CHIPP256KeypairBridge.mm | 7 +- .../Framework/CHIP/MatterCertificates.h | 81 +++++++++++++ .../Framework/CHIP/MatterCertificates.mm | 110 ++++++++++++++++++ src/darwin/Framework/CHIP/SpanUtils.h | 33 ++++++ .../CHIPTests/MatterCertificateTests.m | 59 ++++++++++ 12 files changed, 426 insertions(+), 6 deletions(-) create mode 100644 src/darwin/Framework/CHIP/MatterCertificates.h create mode 100644 src/darwin/Framework/CHIP/MatterCertificates.mm create mode 100644 src/darwin/Framework/CHIP/SpanUtils.h create mode 100644 src/darwin/Framework/CHIPTests/MatterCertificateTests.m diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index 0405947dc40bd9..db73fb2347d476 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -41,6 +41,9 @@ 513DDB8A2761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 513DDB892761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm */; }; 51431AF927D2973E008A7943 /* CHIPIMDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51431AF827D2973E008A7943 /* CHIPIMDispatch.mm */; }; 51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51431AFA27D29CA4008A7943 /* ota-provider.cpp */; }; + 517BF3F0282B62B800A8B7DB /* MatterCertificates.h in Headers */ = {isa = PBXBuildFile; fileRef = 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 517BF3F1282B62B800A8B7DB /* MatterCertificates.mm in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */; }; + 517BF3F3282B62CB00A8B7DB /* MatterCertificateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3F2282B62CB00A8B7DB /* MatterCertificateTests.m */; }; 51B22C1E2740CB0A008D5055 /* CHIPStructsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C1D2740CB0A008D5055 /* CHIPStructsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51B22C222740CB1D008D5055 /* CHIPCommandPayloadsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C212740CB1D008D5055 /* CHIPCommandPayloadsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51B22C262740CB32008D5055 /* CHIPStructsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */; }; @@ -146,6 +149,9 @@ 513DDB892761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPAttributeTLVValueDecoder.mm; path = "zap-generated/CHIPAttributeTLVValueDecoder.mm"; sourceTree = ""; }; 51431AF827D2973E008A7943 /* CHIPIMDispatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPIMDispatch.mm; sourceTree = ""; }; 51431AFA27D29CA4008A7943 /* ota-provider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ota-provider.cpp"; path = "../../../app/clusters/ota-provider/ota-provider.cpp"; sourceTree = ""; }; + 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatterCertificates.h; sourceTree = ""; }; + 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MatterCertificates.mm; sourceTree = ""; }; + 517BF3F2282B62CB00A8B7DB /* MatterCertificateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MatterCertificateTests.m; sourceTree = ""; }; 51B22C1D2740CB0A008D5055 /* CHIPStructsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHIPStructsObjc.h; path = "zap-generated/CHIPStructsObjc.h"; sourceTree = ""; }; 51B22C212740CB1D008D5055 /* CHIPCommandPayloadsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHIPCommandPayloadsObjc.h; path = "zap-generated/CHIPCommandPayloadsObjc.h"; sourceTree = ""; }; 51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPStructsObjc.mm; path = "zap-generated/CHIPStructsObjc.mm"; sourceTree = ""; }; @@ -338,6 +344,8 @@ 5136661228067D550025EDAE /* MatterControllerFactory.h */, 5136661028067D540025EDAE /* MatterControllerFactory.mm */, 5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */, + 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */, + 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */, 5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */, B20252912459E34F00F97062 /* Info.plist */, 998F286C26D55E10001846C6 /* CHIPKeypair.h */, @@ -367,6 +375,7 @@ 5A7947DD27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m */, B2F53AF1245B0DCF0010745E /* CHIPSetupPayloadParserTests.m */, 997DED1926955D0200975E97 /* CHIPThreadOperationalDatasetTests.mm */, + 517BF3F2282B62CB00A8B7DB /* MatterCertificateTests.m */, B202529D2459E34F00F97062 /* Info.plist */, ); path = CHIPTests; @@ -388,6 +397,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 517BF3F0282B62B800A8B7DB /* MatterCertificates.h in Headers */, 5136661628067D550025EDAE /* MatterControllerFactory.h in Headers */, 5A6FEC9927B5C88900F25F42 /* CHIPDeviceOverXPC.h in Headers */, 51B22C222740CB1D008D5055 /* CHIPCommandPayloadsObjc.h in Headers */, @@ -575,6 +585,7 @@ B2E0D7B3245B0B5C003C5B48 /* CHIPError.mm in Sources */, 1E85730C265519AE0050A4D9 /* callback-stub.cpp in Sources */, 1ED276E026C57CF000547A89 /* CHIPCallbackBridge.mm in Sources */, + 517BF3F1282B62B800A8B7DB /* MatterCertificates.mm in Sources */, 5A6FEC9627B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m in Sources */, 5ACDDD7D27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm in Sources */, 513DDB8A2761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm in Sources */, @@ -606,6 +617,7 @@ 5AE6D4E427A99041001F2493 /* CHIPDeviceTests.m in Sources */, 5A7947DE27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m in Sources */, B2F53AF2245B0DCF0010745E /* CHIPSetupPayloadParserTests.m in Sources */, + 517BF3F3282B62CB00A8B7DB /* MatterCertificateTests.m in Sources */, 51E24E73274E0DAC007CCF6E /* CHIPErrorTestUtils.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/src/darwin/Framework/CHIP/BUILD.gn b/src/darwin/Framework/CHIP/BUILD.gn index 2ba9da90acff03..bbb08919975d61 100644 --- a/src/darwin/Framework/CHIP/BUILD.gn +++ b/src/darwin/Framework/CHIP/BUILD.gn @@ -63,6 +63,8 @@ static_library("framework") { "CHIPQRCodeSetupPayloadParser.mm", "CHIPSetupPayload.h", "CHIPSetupPayload.mm", + "MatterCertificates.h", + "MatterCertificates.mm", "MatterControllerFactory.h", "MatterControllerFactory.mm", "MatterControllerFactory_Internal.h", diff --git a/src/darwin/Framework/CHIP/CHIP.h b/src/darwin/Framework/CHIP/CHIP.h index 823806cb155d38..77d26e44354ddc 100644 --- a/src/darwin/Framework/CHIP/CHIP.h +++ b/src/darwin/Framework/CHIP/CHIP.h @@ -34,6 +34,7 @@ #import #import #import +#import #import #import diff --git a/src/darwin/Framework/CHIP/CHIPCluster.mm b/src/darwin/Framework/CHIP/CHIPCluster.mm index 1212c488d230db..8faa510c80160d 100644 --- a/src/darwin/Framework/CHIP/CHIPCluster.mm +++ b/src/darwin/Framework/CHIP/CHIPCluster.mm @@ -17,6 +17,7 @@ #import "CHIPCluster_internal.h" #import "CHIPDevice.h" +#import "SpanUtils.h" using namespace ::chip; @@ -50,7 +51,7 @@ - (instancetype)initWithDevice:(CHIPDevice *)device endpoint:(EndpointId)endpoin - (chip::ByteSpan)asByteSpan:(NSData *)value { - return chip::ByteSpan(static_cast(value.bytes), value.length); + return AsByteSpan(value); } - (chip::CharSpan)asCharSpan:(NSString *)value diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h index 05664812e4c086..549755363438d5 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h @@ -21,6 +21,7 @@ #import #import "CHIPError_Internal.h" +#import "CHIPKeypair.h" #import "CHIPP256KeypairBridge.h" #import "CHIPPersistentStorageDelegateBridge.h" @@ -60,15 +61,34 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC const chip::Crypto::AesCcm128KeySpan GetIPK() { return mIPK.Span(); } + // Generate a root (self-signed) DER-encoded X.509 certificate for the given + // CHIPKeypair. If issuerId is provided, it is used; otherwise a random one + // is generated. If a fabric id is provided it is added to the subject DN + // of the certificate. + // + // 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); + + // Generate an intermediate DER-encoded X.509 certificate for the given root + // and intermediate public key. If issuerId is provided, it is used; + // otherwise a random one is generated. If a fabric id is provided it is + // added to the subject DN of the certificate. + // + // 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); + private: - bool ToChipEpochTime(uint32_t offset, uint32_t & epoch); + static bool ToChipEpochTime(uint32_t offset, uint32_t & epoch); ChipP256KeypairPtr mIssuerKey; uint64_t mIssuerId = 1234; chip::Crypto::AesCcm128Key mIPK; - const uint32_t kCertificateValiditySecs = 365 * 24 * 60 * 60; + static const uint32_t kCertificateValiditySecs = 365 * 24 * 60 * 60; CHIPPersistentStorageDelegateBridge * mStorage; diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm index 7b9090081d1f69..318be9ba94d31f 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm @@ -24,6 +24,8 @@ #include #import "CHIPLogging.h" +#import "MatterCertificates.h" +#import "SpanUtils.h" #include #include @@ -177,3 +179,98 @@ uint8_t second = static_cast([components second]); return chip::CalendarToChipEpochTime(year, month, day, hour, minute, second, epoch); } + +namespace { +uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) +{ + if (providedIssuerId != nil) { + return [providedIssuerId unsignedLongLongValue]; + } + + return (uint64_t(arc4random()) << 32) | arc4random(); +} +} // anonymous namespace + +CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateRootCertificate(id keypair, NSNumber * _Nullable issuerId, + NSNumber * _Nullable fabricId, NSData * _Nullable __autoreleasing * _Nonnull rootCert) +{ + *rootCert = nil; + CHIPP256KeypairBridge keypairBridge; + ReturnErrorOnFailure(keypairBridge.Init(keypair)); + CHIPP256KeypairNativeBridge nativeKeypair(keypairBridge); + + ChipDN rcac_dn; + ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(GetIssuerId(issuerId))); + + if (fabricId != nil) { + ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue])); + } + + uint32_t validityStart, validityEnd; + + if (!ToChipEpochTime(0, validityStart)) { + NSLog(@"Failed in computing certificate validity start date"); + return CHIP_ERROR_INTERNAL; + } + + if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { + NSLog(@"Failed in computing certificate validity end date"); + return CHIP_ERROR_INTERNAL; + } + + uint8_t rcacBuffer[Controller::kMaxCHIPDERCertLength]; + MutableByteSpan rcac(rcacBuffer); + X509CertRequestParams rcac_request = { 0, validityStart, validityEnd, rcac_dn, rcac_dn }; + ReturnErrorOnFailure(NewRootX509Cert(rcac_request, nativeKeypair, rcac)); + *rootCert = AsData(rcac); + return CHIP_NO_ERROR; +} + +CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateIntermediateCertificate(id rootKeypair, + NSData * rootCertificate, SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, + NSData * _Nullable __autoreleasing * _Nonnull intermediateCert) +{ + *intermediateCert = nil; + + // Verify that the provided certificate public key matches the root keypair. + if ([MatterCertificates keypairMatchesCertificate:rootCertificate keypair:rootKeypair] == NO) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + CHIPP256KeypairBridge keypairBridge; + ReturnErrorOnFailure(keypairBridge.Init(rootKeypair)); + CHIPP256KeypairNativeBridge nativeRootKeypair(keypairBridge); + + ByteSpan rcac = AsByteSpan(rootCertificate); + + P256PublicKey pubKey; + ReturnErrorOnFailure(CHIPP256KeypairBridge::MatterPubKeyFromSecKeyRef(intermediatePublicKey, &pubKey)); + + ChipDN rcac_dn; + ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn)); + + ChipDN icac_dn; + ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(GetIssuerId(issuerId))); + if (fabricId != nil) { + ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue])); + } + + uint32_t validityStart, validityEnd; + + if (!ToChipEpochTime(0, validityStart)) { + NSLog(@"Failed in computing certificate validity start date"); + return CHIP_ERROR_INTERNAL; + } + + if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { + NSLog(@"Failed in computing certificate validity end date"); + return CHIP_ERROR_INTERNAL; + } + + uint8_t icacBuffer[Controller::kMaxCHIPDERCertLength]; + MutableByteSpan icac(icacBuffer); + X509CertRequestParams icac_request = { 0, validityStart, validityEnd, icac_dn, rcac_dn }; + ReturnErrorOnFailure(NewICAX509Cert(icac_request, pubKey, nativeRootKeypair, icac)); + *intermediateCert = AsData(icac); + return CHIP_NO_ERROR; +} diff --git a/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.h b/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.h index 356d8a03b1067f..b1beb7a846e168 100644 --- a/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.h +++ b/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.h @@ -48,6 +48,9 @@ class CHIPP256KeypairBridge : public chip::Crypto::P256KeypairBase const chip::Crypto::P256PublicKey & Pubkey() const override { return mPubkey; }; + // On success, writes to *pubKey. + static CHIP_ERROR MatterPubKeyFromSecKeyRef(SecKeyRef pubkeyRef, chip::Crypto::P256PublicKey * matterPubKey); + private: id _Nullable mKeypair; chip::Crypto::P256PublicKey mPubkey; diff --git a/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.mm b/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.mm index ef31304b27d236..1d97909bfe6936 100644 --- a/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.mm +++ b/src/darwin/Framework/CHIP/CHIPP256KeypairBridge.mm @@ -124,9 +124,10 @@ return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } -CHIP_ERROR CHIPP256KeypairBridge::setPubkey() +CHIP_ERROR CHIPP256KeypairBridge::setPubkey() { return MatterPubKeyFromSecKeyRef([mKeypair pubkey], &mPubkey); } + +CHIP_ERROR CHIPP256KeypairBridge::MatterPubKeyFromSecKeyRef(SecKeyRef pubkeyRef, P256PublicKey * matterPubKey) { - SecKeyRef pubkeyRef = [mKeypair pubkey]; if (!pubkeyRef) { CHIP_LOG_ERROR("Unable to initialize Pubkey"); return CHIP_ERROR_INTERNAL; @@ -142,7 +143,7 @@ return CHIP_ERROR_INTERNAL; } chip::FixedByteSpan pubkeyBytes((const uint8_t *) pubkeyData.bytes); - mPubkey = P256PublicKey(pubkeyBytes); + *matterPubKey = P256PublicKey(pubkeyBytes); return CHIP_NO_ERROR; } diff --git a/src/darwin/Framework/CHIP/MatterCertificates.h b/src/darwin/Framework/CHIP/MatterCertificates.h new file mode 100644 index 00000000000000..52e00dfbe1e4ef --- /dev/null +++ b/src/darwin/Framework/CHIP/MatterCertificates.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MATTER_CERTIFICATES_H +#define MATTER_CERTIFICATES_H + +/** + * Utilities for working with Matter certificates. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol CHIPKeypair; + +@interface MatterCertificates : NSObject + +/** + * Generate a root (self-signed) X.509 DER encoded certificate that has the + * right fields to be a valid Matter root certificate. + * + * If issuerId is nil, a random issuer id is generated. Otherwise the provided + * issuer id is used. + * + * If fabricId is not nil, it will be included in the subject DN of the + * certificate. + * + * On failure returns nil and if "error" is not null sets *error to the relevant + * error. + */ ++ (nullable NSData *)generateRootCertificate:(id)keypair + issuerId:(nullable NSNumber *)issuerId + fabricId:(nullable NSNumber *)fabricId + error:(NSError * __autoreleasing _Nullable * _Nullable)error; + +/** + * Generate an intermediate X.509 DER encoded certificate that has the + * right fields to be a valid Matter intermediate certificate. + * + * If issuerId is nil, a random issuer id is generated. Otherwise the provided + * issuer id is used. + * + * If fabricId is not nil, it will be included in the subject DN of the + * certificate. + * + * On failure returns nil and if "error" is not null sets *error to the relevant + * error. + */ ++ (nullable NSData *)generateIntermediateCertificate:(id)rootKeypair + rootCertificate:(NSData *)rootCertificate + intermediatePublicKey:(SecKeyRef)intermediatePublicKey + issuerId:(nullable NSNumber *)issuerId + fabricId:(nullable NSNumber *)fabricId + error:(NSError * __autoreleasing _Nullable * _Nullable)error; + +/** + * Check whether the given keypair's public key matches the given certificate's + * public key. + * + * Will return NO on failures to extract public keys from the objects. + */ ++ (BOOL)keypairMatchesCertificate:(NSData *)certificate keypair:(id)keypair; + +@end + +NS_ASSUME_NONNULL_END + +#endif // MATTER_CERTIFICATES_H diff --git a/src/darwin/Framework/CHIP/MatterCertificates.mm b/src/darwin/Framework/CHIP/MatterCertificates.mm new file mode 100644 index 00000000000000..4dad9790cceee7 --- /dev/null +++ b/src/darwin/Framework/CHIP/MatterCertificates.mm @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MatterCertificates.h" +#import "CHIPError_Internal.h" +#import "CHIPOperationalCredentialsDelegate.h" +#import "CHIPP256KeypairBridge.h" +#import "SpanUtils.h" + +#include +#include +#include + +using namespace chip; +using namespace chip::Crypto; +using namespace chip::Credentials; + +// RAII helper for doing MemoryInit/MemoryShutdown, just in case the underlying +// Matter APIs we are using use Platform::Memory. MemoryInit/MemoryShutdown are +// refcounted, so it's OK if we use AutoPlatformMemory after MemoryInit has +// already happened elsewhere. +struct AutoPlatformMemory { + AutoPlatformMemory() { Platform::MemoryInit(); } + ~AutoPlatformMemory() { Platform::MemoryShutdown(); } +}; + +@implementation MatterCertificates + ++ (nullable NSData *)generateRootCertificate:(id)keypair + issuerId:(nullable NSNumber *)issuerId + fabricId:(nullable NSNumber *)fabricId + error:(NSError * __autoreleasing *)error +{ + NSLog(@"Generating root certificate"); + + AutoPlatformMemory platformMemory; + + NSData * rootCert = nil; + CHIP_ERROR err = CHIPOperationalCredentialsDelegate::GenerateRootCertificate(keypair, issuerId, fabricId, &rootCert); + if (error) { + *error = [CHIPError errorForCHIPErrorCode:err]; + } + + if (err != CHIP_NO_ERROR) { + NSLog(@"Generating root certificate failed: %s", ErrorStr(err)); + } + + return rootCert; +} + ++ (nullable NSData *)generateIntermediateCertificate:(id)rootKeypair + rootCertificate:(NSData *)rootCertificate + intermediatePublicKey:(SecKeyRef)intermediatePublicKey + issuerId:(nullable NSNumber *)issuerId + fabricId:(nullable NSNumber *)fabricId + error:(NSError * __autoreleasing *)error +{ + NSLog(@"Generating intermediate certificate"); + + AutoPlatformMemory platformMemory; + + NSData * intermediate = nil; + CHIP_ERROR err = CHIPOperationalCredentialsDelegate::GenerateIntermediateCertificate( + rootKeypair, rootCertificate, intermediatePublicKey, issuerId, fabricId, &intermediate); + if (error) { + *error = [CHIPError errorForCHIPErrorCode:err]; + } + + if (err != CHIP_NO_ERROR) { + NSLog(@"Generating intermediate certificate failed: %s", ErrorStr(err)); + } + + return intermediate; +} + ++ (BOOL)keypairMatchesCertificate:(NSData *)certificate keypair:(id)keypair +{ + P256PublicKey keypairPubKey; + CHIP_ERROR err = CHIPP256KeypairBridge::MatterPubKeyFromSecKeyRef(keypair.pubkey, &keypairPubKey); + if (err != CHIP_NO_ERROR) { + NSLog(@"Can't extract public key from keypair: %s", ErrorStr(err)); + return NO; + } + P256PublicKeySpan keypairKeySpan(keypairPubKey.ConstBytes()); + + P256PublicKey certPubKey; + err = ExtractPubkeyFromX509Cert(AsByteSpan(certificate), certPubKey); + if (err != CHIP_NO_ERROR) { + NSLog(@"Can't extract public key from certificate: %s", ErrorStr(err)); + return NO; + } + P256PublicKeySpan certKeySpan(certPubKey.ConstBytes()); + + return certKeySpan.data_equal(keypairKeySpan); +} + +@end diff --git a/src/darwin/Framework/CHIP/SpanUtils.h b/src/darwin/Framework/CHIP/SpanUtils.h new file mode 100644 index 00000000000000..d854d46be03ef3 --- /dev/null +++ b/src/darwin/Framework/CHIP/SpanUtils.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#import "Foundation/Foundation.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +/** + * Utilities for converting between NSData and chip::Span. + */ + +inline chip::ByteSpan AsByteSpan(NSData * data) { return chip::ByteSpan(static_cast(data.bytes), data.length); } + +inline NSData * AsData(chip::ByteSpan span) { return [NSData dataWithBytes:span.data() length:span.size()]; } + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIPTests/MatterCertificateTests.m b/src/darwin/Framework/CHIPTests/MatterCertificateTests.m new file mode 100644 index 00000000000000..20eca5ee7c619c --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MatterCertificateTests.m @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +// system dependencies +#import + +#import "CHIPTestKeys.h" + +@interface MatterCertificateTests : XCTestCase + +@end + +@implementation MatterCertificateTests + +- (void)testGenerateRootCert +{ + __auto_type * testKeys = [[CHIPTestKeys alloc] init]; + XCTAssertNotNil(testKeys); + + __auto_type * rootCert = [MatterCertificates generateRootCertificate:testKeys issuerId:nil fabricId:nil error:nil]; + XCTAssertNotNil(rootCert); +} + +- (void)testGenerateIntermediateCert +{ + __auto_type * rootKeys = [[CHIPTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + __auto_type * rootCert = [MatterCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil]; + XCTAssertNotNil(rootCert); + + __auto_type * intermediateKeys = [[CHIPTestKeys alloc] init]; + XCTAssertNotNil(intermediateKeys); + + __auto_type * intermediateCert = [MatterCertificates generateIntermediateCertificate:rootKeys + rootCertificate:rootCert + intermediatePublicKey:intermediateKeys.pubkey + issuerId:nil + fabricId:nil + error:nil]; + XCTAssertNotNil(intermediateCert); +} + +@end From 96d7eac45c591ba203187ae5426b7fe1c72664a0 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 May 2022 00:01:46 -0400 Subject: [PATCH 2/3] Address review comment. --- src/darwin/Framework/CHIP/CHIPCluster.mm | 2 +- src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm | 2 +- src/darwin/Framework/CHIP/MatterCertificates.mm | 2 +- .../Framework/CHIP/{SpanUtils.h => NSDataSpanConversion.h} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/darwin/Framework/CHIP/{SpanUtils.h => NSDataSpanConversion.h} (100%) diff --git a/src/darwin/Framework/CHIP/CHIPCluster.mm b/src/darwin/Framework/CHIP/CHIPCluster.mm index 8faa510c80160d..6b5bc48d75df51 100644 --- a/src/darwin/Framework/CHIP/CHIPCluster.mm +++ b/src/darwin/Framework/CHIP/CHIPCluster.mm @@ -17,7 +17,7 @@ #import "CHIPCluster_internal.h" #import "CHIPDevice.h" -#import "SpanUtils.h" +#import "NSDataSpanConversion.h" using namespace ::chip; diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm index 318be9ba94d31f..9abeb84c2ef08d 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm @@ -25,7 +25,7 @@ #import "CHIPLogging.h" #import "MatterCertificates.h" -#import "SpanUtils.h" +#import "NSDataSpanConversion.h" #include #include diff --git a/src/darwin/Framework/CHIP/MatterCertificates.mm b/src/darwin/Framework/CHIP/MatterCertificates.mm index 4dad9790cceee7..d0748274eec27a 100644 --- a/src/darwin/Framework/CHIP/MatterCertificates.mm +++ b/src/darwin/Framework/CHIP/MatterCertificates.mm @@ -18,7 +18,7 @@ #import "CHIPError_Internal.h" #import "CHIPOperationalCredentialsDelegate.h" #import "CHIPP256KeypairBridge.h" -#import "SpanUtils.h" +#import "NSDataSpanConversion.h" #include #include diff --git a/src/darwin/Framework/CHIP/SpanUtils.h b/src/darwin/Framework/CHIP/NSDataSpanConversion.h similarity index 100% rename from src/darwin/Framework/CHIP/SpanUtils.h rename to src/darwin/Framework/CHIP/NSDataSpanConversion.h From 1ddc1791006c82275bcd1efc5e8860c2185c3992 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 12 May 2022 12:29:08 -0400 Subject: [PATCH 3/3] Address review comment about naming --- .../Framework/CHIP.xcodeproj/project.pbxproj | 16 ++++++++-------- src/darwin/Framework/CHIP/BUILD.gn | 4 ++-- src/darwin/Framework/CHIP/CHIP.h | 2 +- .../CHIP/CHIPOperationalCredentialsDelegate.mm | 4 ++-- .../{MatterCertificates.h => MTRCertificates.h} | 2 +- ...{MatterCertificates.mm => MTRCertificates.mm} | 4 ++-- .../Framework/CHIPTests/MatterCertificateTests.m | 16 ++++++++-------- 7 files changed, 24 insertions(+), 24 deletions(-) rename src/darwin/Framework/CHIP/{MatterCertificates.h => MTRCertificates.h} (98%) rename src/darwin/Framework/CHIP/{MatterCertificates.mm => MTRCertificates.mm} (98%) diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index db73fb2347d476..510dd2289b30fb 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -41,8 +41,8 @@ 513DDB8A2761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 513DDB892761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm */; }; 51431AF927D2973E008A7943 /* CHIPIMDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51431AF827D2973E008A7943 /* CHIPIMDispatch.mm */; }; 51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51431AFA27D29CA4008A7943 /* ota-provider.cpp */; }; - 517BF3F0282B62B800A8B7DB /* MatterCertificates.h in Headers */ = {isa = PBXBuildFile; fileRef = 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 517BF3F1282B62B800A8B7DB /* MatterCertificates.mm in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */; }; + 517BF3F0282B62B800A8B7DB /* MTRCertificates.h in Headers */ = {isa = PBXBuildFile; fileRef = 517BF3EE282B62B800A8B7DB /* MTRCertificates.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 517BF3F1282B62B800A8B7DB /* MTRCertificates.mm in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3EF282B62B800A8B7DB /* MTRCertificates.mm */; }; 517BF3F3282B62CB00A8B7DB /* MatterCertificateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3F2282B62CB00A8B7DB /* MatterCertificateTests.m */; }; 51B22C1E2740CB0A008D5055 /* CHIPStructsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C1D2740CB0A008D5055 /* CHIPStructsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51B22C222740CB1D008D5055 /* CHIPCommandPayloadsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C212740CB1D008D5055 /* CHIPCommandPayloadsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -149,8 +149,8 @@ 513DDB892761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPAttributeTLVValueDecoder.mm; path = "zap-generated/CHIPAttributeTLVValueDecoder.mm"; sourceTree = ""; }; 51431AF827D2973E008A7943 /* CHIPIMDispatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPIMDispatch.mm; sourceTree = ""; }; 51431AFA27D29CA4008A7943 /* ota-provider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ota-provider.cpp"; path = "../../../app/clusters/ota-provider/ota-provider.cpp"; sourceTree = ""; }; - 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatterCertificates.h; sourceTree = ""; }; - 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MatterCertificates.mm; sourceTree = ""; }; + 517BF3EE282B62B800A8B7DB /* MTRCertificates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRCertificates.h; sourceTree = ""; }; + 517BF3EF282B62B800A8B7DB /* MTRCertificates.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRCertificates.mm; sourceTree = ""; }; 517BF3F2282B62CB00A8B7DB /* MatterCertificateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MatterCertificateTests.m; sourceTree = ""; }; 51B22C1D2740CB0A008D5055 /* CHIPStructsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHIPStructsObjc.h; path = "zap-generated/CHIPStructsObjc.h"; sourceTree = ""; }; 51B22C212740CB1D008D5055 /* CHIPCommandPayloadsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHIPCommandPayloadsObjc.h; path = "zap-generated/CHIPCommandPayloadsObjc.h"; sourceTree = ""; }; @@ -344,8 +344,8 @@ 5136661228067D550025EDAE /* MatterControllerFactory.h */, 5136661028067D540025EDAE /* MatterControllerFactory.mm */, 5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */, - 517BF3EE282B62B800A8B7DB /* MatterCertificates.h */, - 517BF3EF282B62B800A8B7DB /* MatterCertificates.mm */, + 517BF3EE282B62B800A8B7DB /* MTRCertificates.h */, + 517BF3EF282B62B800A8B7DB /* MTRCertificates.mm */, 5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */, B20252912459E34F00F97062 /* Info.plist */, 998F286C26D55E10001846C6 /* CHIPKeypair.h */, @@ -397,7 +397,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 517BF3F0282B62B800A8B7DB /* MatterCertificates.h in Headers */, + 517BF3F0282B62B800A8B7DB /* MTRCertificates.h in Headers */, 5136661628067D550025EDAE /* MatterControllerFactory.h in Headers */, 5A6FEC9927B5C88900F25F42 /* CHIPDeviceOverXPC.h in Headers */, 51B22C222740CB1D008D5055 /* CHIPCommandPayloadsObjc.h in Headers */, @@ -585,7 +585,7 @@ B2E0D7B3245B0B5C003C5B48 /* CHIPError.mm in Sources */, 1E85730C265519AE0050A4D9 /* callback-stub.cpp in Sources */, 1ED276E026C57CF000547A89 /* CHIPCallbackBridge.mm in Sources */, - 517BF3F1282B62B800A8B7DB /* MatterCertificates.mm in Sources */, + 517BF3F1282B62B800A8B7DB /* MTRCertificates.mm in Sources */, 5A6FEC9627B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m in Sources */, 5ACDDD7D27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm in Sources */, 513DDB8A2761F6F900DAA01A /* CHIPAttributeTLVValueDecoder.mm in Sources */, diff --git a/src/darwin/Framework/CHIP/BUILD.gn b/src/darwin/Framework/CHIP/BUILD.gn index bbb08919975d61..216e5cab608dac 100644 --- a/src/darwin/Framework/CHIP/BUILD.gn +++ b/src/darwin/Framework/CHIP/BUILD.gn @@ -63,8 +63,8 @@ static_library("framework") { "CHIPQRCodeSetupPayloadParser.mm", "CHIPSetupPayload.h", "CHIPSetupPayload.mm", - "MatterCertificates.h", - "MatterCertificates.mm", + "MTRCertificates.h", + "MTRCertificates.mm", "MatterControllerFactory.h", "MatterControllerFactory.mm", "MatterControllerFactory_Internal.h", diff --git a/src/darwin/Framework/CHIP/CHIP.h b/src/darwin/Framework/CHIP/CHIP.h index 77d26e44354ddc..b609d3459cbe3c 100644 --- a/src/darwin/Framework/CHIP/CHIP.h +++ b/src/darwin/Framework/CHIP/CHIP.h @@ -34,7 +34,7 @@ #import #import #import -#import +#import #import #import diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm index 9abeb84c2ef08d..18746e07d7cfd5 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm @@ -24,7 +24,7 @@ #include #import "CHIPLogging.h" -#import "MatterCertificates.h" +#import "MTRCertificates.h" #import "NSDataSpanConversion.h" #include @@ -233,7 +233,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId) *intermediateCert = nil; // Verify that the provided certificate public key matches the root keypair. - if ([MatterCertificates keypairMatchesCertificate:rootCertificate keypair:rootKeypair] == NO) { + if ([MTRCertificates keypairMatchesCertificate:rootCertificate keypair:rootKeypair] == NO) { return CHIP_ERROR_INVALID_ARGUMENT; } diff --git a/src/darwin/Framework/CHIP/MatterCertificates.h b/src/darwin/Framework/CHIP/MTRCertificates.h similarity index 98% rename from src/darwin/Framework/CHIP/MatterCertificates.h rename to src/darwin/Framework/CHIP/MTRCertificates.h index 52e00dfbe1e4ef..edf1af5cfeec03 100644 --- a/src/darwin/Framework/CHIP/MatterCertificates.h +++ b/src/darwin/Framework/CHIP/MTRCertificates.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol CHIPKeypair; -@interface MatterCertificates : NSObject +@interface MTRCertificates : NSObject /** * Generate a root (self-signed) X.509 DER encoded certificate that has the diff --git a/src/darwin/Framework/CHIP/MatterCertificates.mm b/src/darwin/Framework/CHIP/MTRCertificates.mm similarity index 98% rename from src/darwin/Framework/CHIP/MatterCertificates.mm rename to src/darwin/Framework/CHIP/MTRCertificates.mm index d0748274eec27a..fcc6aac456b45b 100644 --- a/src/darwin/Framework/CHIP/MatterCertificates.mm +++ b/src/darwin/Framework/CHIP/MTRCertificates.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "MatterCertificates.h" +#import "MTRCertificates.h" #import "CHIPError_Internal.h" #import "CHIPOperationalCredentialsDelegate.h" #import "CHIPP256KeypairBridge.h" @@ -37,7 +37,7 @@ ~AutoPlatformMemory() { Platform::MemoryShutdown(); } }; -@implementation MatterCertificates +@implementation MTRCertificates + (nullable NSData *)generateRootCertificate:(id)keypair issuerId:(nullable NSNumber *)issuerId diff --git a/src/darwin/Framework/CHIPTests/MatterCertificateTests.m b/src/darwin/Framework/CHIPTests/MatterCertificateTests.m index 20eca5ee7c619c..eedaffe762c1d2 100644 --- a/src/darwin/Framework/CHIPTests/MatterCertificateTests.m +++ b/src/darwin/Framework/CHIPTests/MatterCertificateTests.m @@ -32,7 +32,7 @@ - (void)testGenerateRootCert __auto_type * testKeys = [[CHIPTestKeys alloc] init]; XCTAssertNotNil(testKeys); - __auto_type * rootCert = [MatterCertificates generateRootCertificate:testKeys issuerId:nil fabricId:nil error:nil]; + __auto_type * rootCert = [MTRCertificates generateRootCertificate:testKeys issuerId:nil fabricId:nil error:nil]; XCTAssertNotNil(rootCert); } @@ -41,18 +41,18 @@ - (void)testGenerateIntermediateCert __auto_type * rootKeys = [[CHIPTestKeys alloc] init]; XCTAssertNotNil(rootKeys); - __auto_type * rootCert = [MatterCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil]; + __auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil]; XCTAssertNotNil(rootCert); __auto_type * intermediateKeys = [[CHIPTestKeys alloc] init]; XCTAssertNotNil(intermediateKeys); - __auto_type * intermediateCert = [MatterCertificates generateIntermediateCertificate:rootKeys - rootCertificate:rootCert - intermediatePublicKey:intermediateKeys.pubkey - issuerId:nil - fabricId:nil - error:nil]; + __auto_type * intermediateCert = [MTRCertificates generateIntermediateCertificate:rootKeys + rootCertificate:rootCert + intermediatePublicKey:intermediateKeys.pubkey + issuerId:nil + fabricId:nil + error:nil]; XCTAssertNotNil(intermediateCert); }