| /** |
| * 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 "MTRDeviceControllerStartupParams.h" |
| #import "MTRCertificates.h" |
| #import "MTRConversion.h" |
| #import "MTRDeviceControllerStartupParams_Internal.h" |
| #import "MTRDeviceController_Internal.h" |
| #import "MTRLogging_Internal.h" |
| #import "MTRP256KeypairBridge.h" |
| #import "NSDataSpanConversion.h" |
| |
| #import <Matter/MTRDeviceControllerStorageDelegate.h> |
| |
| #include <controller/OperationalCredentialsDelegate.h> |
| #include <credentials/CHIPCert.h> |
| #include <credentials/FabricTable.h> |
| #include <lib/core/PeerId.h> |
| |
| using namespace chip; |
| |
| static CHIP_ERROR ExtractNodeIDFabricIDFromNOC( |
| MTRCertificateDERBytes noc, NSNumber * __autoreleasing * nodeID, NSNumber * __autoreleasing * fabricID) |
| { |
| // ExtractNodeIdFabricIdFromOpCert needs a TLV-encoded opcert, not a DER-encoded one. |
| auto * tlvNOC = [MTRCertificates convertX509Certificate:noc]; |
| if (tlvNOC == nil) { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| ByteSpan nocSpan = AsByteSpan(tlvNOC); |
| |
| FabricId certFabricID = kUndefinedFabricId; |
| NodeId certNodeID = kUndefinedNodeId; |
| CHIP_ERROR err = Credentials::ExtractNodeIdFabricIdFromOpCert(nocSpan, &certNodeID, &certFabricID); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Unable to extract node ID and fabric ID from operational certificate: %s", err.AsString()); |
| return err; |
| } |
| *nodeID = @(certNodeID); |
| *fabricID = @(certFabricID); |
| return CHIP_NO_ERROR; |
| } |
| |
| static CHIP_ERROR ExtractFabricIDFromNOC(MTRCertificateDERBytes noc, NSNumber * __autoreleasing * fabricID) |
| { |
| NSNumber * ignored; |
| return ExtractNodeIDFabricIDFromNOC(noc, &ignored, fabricID); |
| } |
| |
| static CHIP_ERROR ExtractNodeIDFromNOC(MTRCertificateDERBytes noc, NSNumber * __autoreleasing * nodeID) |
| { |
| NSNumber * ignored; |
| return ExtractNodeIDFabricIDFromNOC(noc, nodeID, &ignored); |
| } |
| |
| @implementation MTRDeviceControllerStartupParams |
| |
| - (instancetype)initWithIPK:(NSData *)ipk fabricID:(NSNumber *)fabricID nocSigner:(id<MTRKeypair>)nocSigner |
| { |
| if (!(self = [super init])) { |
| return nil; |
| } |
| |
| if (!IsValidFabricId(fabricID.unsignedLongLongValue)) { |
| MTR_LOG_ERROR("%llu is not a valid fabric id to initialize a device controller with", fabricID.unsignedLongLongValue); |
| return nil; |
| } |
| |
| _nocSigner = nocSigner; |
| _fabricID = [fabricID copy]; |
| _ipk = [ipk copy]; |
| _uniqueIdentifier = [NSUUID UUID]; |
| |
| return self; |
| } |
| |
| - (instancetype)initWithIPK:(NSData *)ipk |
| operationalKeypair:(id<MTRKeypair>)operationalKeypair |
| operationalCertificate:(MTRCertificateDERBytes)operationalCertificate |
| intermediateCertificate:(MTRCertificateDERBytes _Nullable)intermediateCertificate |
| rootCertificate:(MTRCertificateDERBytes)rootCertificate |
| { |
| if (!(self = [super init])) { |
| return nil; |
| } |
| |
| { // Scope for temporary |
| NSNumber * fabricID; |
| CHIP_ERROR err = ExtractFabricIDFromNOC(operationalCertificate, &fabricID); |
| if (err != CHIP_NO_ERROR) { |
| return nil; |
| } |
| _fabricID = fabricID; |
| } |
| |
| _operationalKeypair = operationalKeypair; |
| _operationalCertificate = [operationalCertificate copy]; |
| _intermediateCertificate = [intermediateCertificate copy]; |
| _rootCertificate = [rootCertificate copy]; |
| _ipk = [ipk copy]; |
| _uniqueIdentifier = [NSUUID UUID]; |
| |
| return self; |
| } |
| |
| - (instancetype)initWithParams:(MTRDeviceControllerStartupParams *)params |
| { |
| if (!(self = [super init])) { |
| return nil; |
| } |
| |
| _nocSigner = params.nocSigner; |
| _fabricID = params.fabricID; |
| _ipk = params.ipk; |
| _vendorID = params.vendorID; |
| _nodeID = params.nodeID; |
| _caseAuthenticatedTags = params.caseAuthenticatedTags; |
| _rootCertificate = params.rootCertificate; |
| _intermediateCertificate = params.intermediateCertificate; |
| _operationalCertificate = params.operationalCertificate; |
| _operationalKeypair = params.operationalKeypair; |
| _operationalCertificateIssuer = params.operationalCertificateIssuer; |
| _operationalCertificateIssuerQueue = params.operationalCertificateIssuerQueue; |
| _uniqueIdentifier = params.uniqueIdentifier; |
| |
| return self; |
| } |
| |
| - (instancetype)initWithParameters:(MTRDeviceControllerParameters *)params error:(CHIP_ERROR &)error |
| { |
| if (!(self = [super init])) { |
| error = CHIP_ERROR_INCORRECT_STATE; |
| return nil; |
| } |
| |
| if (![params isKindOfClass:[MTRDeviceControllerExternalCertificateParameters class]]) { |
| MTR_LOG_ERROR("Unexpected subclass of MTRDeviceControllerParameters"); |
| error = CHIP_ERROR_INVALID_ARGUMENT; |
| return nil; |
| } |
| |
| _nocSigner = nil; |
| |
| NSNumber * fabricID; |
| error = ExtractFabricIDFromNOC(params.operationalCertificate, &fabricID); |
| if (error != CHIP_NO_ERROR) { |
| return nil; |
| } |
| _fabricID = fabricID; |
| |
| _ipk = params.ipk; |
| _vendorID = params.vendorID; |
| // Note: Since we have an operationalCertificate, we do not need a nodeID as |
| // part of our params; it will not be used. Don't even initialize it, to |
| // avoid confusion about that. |
| // |
| // We don't really use the fabricID for anything either, but we promise to |
| // have a non-nil one, which is why we set it above. |
| _nodeID = nil; |
| _caseAuthenticatedTags = nil; |
| _rootCertificate = params.rootCertificate; |
| _intermediateCertificate = params.intermediateCertificate; |
| _operationalCertificate = params.operationalCertificate; |
| _operationalKeypair = params.operationalKeypair; |
| _operationalCertificateIssuer = params.operationalCertificateIssuer; |
| _operationalCertificateIssuerQueue = params.operationalCertificateIssuerQueue; |
| _uniqueIdentifier = params.uniqueIdentifier; |
| |
| return self; |
| } |
| |
| @end |
| |
| // Convert a ByteSpan representing a Matter TLV certificate into NSData holding |
| // a DER X.509 certificate. Returns nil on failures. |
| static NSData * _Nullable MatterCertToX509Data(const ByteSpan & cert) |
| { |
| uint8_t buf[Controller::kMaxCHIPDERCertLength]; |
| MutableByteSpan derCert(buf); |
| CHIP_ERROR err = Credentials::ConvertChipCertToX509Cert(cert, derCert); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to convert Matter certificate to X.509 DER: %s", ErrorStr(err)); |
| return nil; |
| } |
| |
| return AsData(derCert); |
| } |
| |
| @implementation MTRDeviceControllerStartupParams (Deprecated) |
| |
| - (uint64_t)fabricId |
| { |
| return self.fabricID.unsignedLongLongValue; |
| } |
| |
| - (nullable NSNumber *)vendorId |
| { |
| return self.vendorID; |
| } |
| |
| - (void)setVendorId:(nullable NSNumber *)vendorId |
| { |
| self.vendorID = vendorId; |
| } |
| |
| - (nullable NSNumber *)nodeId |
| { |
| return self.nodeID; |
| } |
| |
| - (void)setNodeId:(nullable NSNumber *)nodeId |
| { |
| self.nodeID = nodeId; |
| } |
| |
| - (instancetype)initWithSigningKeypair:(id<MTRKeypair>)nocSigner fabricId:(uint64_t)fabricId ipk:(NSData *)ipk |
| { |
| return [self initWithIPK:ipk fabricID:@(fabricId) nocSigner:nocSigner]; |
| } |
| |
| - (instancetype)initWithOperationalKeypair:(id<MTRKeypair>)operationalKeypair |
| operationalCertificate:(MTRCertificateDERBytes)operationalCertificate |
| intermediateCertificate:(MTRCertificateDERBytes _Nullable)intermediateCertificate |
| rootCertificate:(MTRCertificateDERBytes)rootCertificate |
| ipk:(NSData *)ipk |
| { |
| return [self initWithIPK:ipk |
| operationalKeypair:operationalKeypair |
| operationalCertificate:operationalCertificate |
| intermediateCertificate:intermediateCertificate |
| rootCertificate:rootCertificate]; |
| } |
| |
| @end |
| |
| @implementation MTRDeviceControllerAbstractParameters |
| - (instancetype)_initInternal |
| { |
| if (!(self = [super init])) { |
| return nil; |
| } |
| |
| _startSuspended = NO; |
| |
| return self; |
| } |
| @end |
| |
| constexpr NSUInteger kDefaultConcurrentSubscriptionPoolSize = 300; |
| |
| @implementation MTRDeviceControllerParameters |
| - (instancetype)initWithStorageDelegate:(id<MTRDeviceControllerStorageDelegate>)storageDelegate |
| storageDelegateQueue:(dispatch_queue_t)storageDelegateQueue |
| uniqueIdentifier:(NSUUID *)uniqueIdentifier |
| ipk:(NSData *)ipk |
| vendorID:(NSNumber *)vendorID |
| operationalKeypair:(id<MTRKeypair>)operationalKeypair |
| operationalCertificate:(MTRCertificateDERBytes)operationalCertificate |
| intermediateCertificate:(MTRCertificateDERBytes _Nullable)intermediateCertificate |
| rootCertificate:(MTRCertificateDERBytes)rootCertificate |
| { |
| if (!(self = [super _initInternal])) { |
| return nil; |
| } |
| |
| _productAttestationAuthorityCertificates = nil; |
| _certificationDeclarationCertificates = nil; |
| _shouldAdvertiseOperational = NO; |
| |
| _ipk = ipk; |
| _vendorID = vendorID; |
| _rootCertificate = rootCertificate; |
| _intermediateCertificate = intermediateCertificate; |
| _operationalCertificate = operationalCertificate; |
| _operationalKeypair = operationalKeypair; |
| |
| _operationalCertificateIssuer = nil; |
| _operationalCertificateIssuerQueue = nil; |
| _storageDelegate = storageDelegate; |
| _storageDelegateQueue = storageDelegateQueue; |
| _uniqueIdentifier = uniqueIdentifier; |
| |
| _concurrentSubscriptionEstablishmentsAllowedOnThread = kDefaultConcurrentSubscriptionPoolSize; |
| |
| return self; |
| } |
| |
| - (void)setOperationalCertificateIssuer:(id<MTROperationalCertificateIssuer>)operationalCertificateIssuer |
| queue:(dispatch_queue_t)queue |
| { |
| _operationalCertificateIssuer = operationalCertificateIssuer; |
| _operationalCertificateIssuerQueue = queue; |
| } |
| |
| - (void)setOTAProviderDelegate:(id<MTROTAProviderDelegate>)otaProviderDelegate queue:(dispatch_queue_t)queue |
| { |
| _otaProviderDelegate = otaProviderDelegate; |
| _otaProviderDelegateQueue = queue; |
| } |
| |
| + (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc |
| { |
| NSNumber * nodeID = nil; |
| ExtractNodeIDFromNOC(noc, &nodeID); |
| return nodeID; |
| } |
| |
| + (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc |
| { |
| NSNumber * fabricID = nil; |
| ExtractFabricIDFromNOC(noc, &fabricID); |
| return fabricID; |
| } |
| |
| + (nullable NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate |
| { |
| Crypto::P256PublicKey pubKey; |
| if (ExtractPubkeyFromX509Cert(AsByteSpan(certificate), pubKey) != CHIP_NO_ERROR) { |
| return nil; |
| } |
| return [NSData dataWithBytes:pubKey.Bytes() length:pubKey.Length()]; |
| } |
| |
| @end |
| |
| @implementation MTRDeviceControllerExternalCertificateParameters |
| - (instancetype)initWithStorageDelegate:(id<MTRDeviceControllerStorageDelegate>)storageDelegate |
| storageDelegateQueue:(dispatch_queue_t)storageDelegateQueue |
| uniqueIdentifier:(NSUUID *)uniqueIdentifier |
| ipk:(NSData *)ipk |
| vendorID:(NSNumber *)vendorID |
| operationalKeypair:(id<MTRKeypair>)operationalKeypair |
| operationalCertificate:(MTRCertificateDERBytes)operationalCertificate |
| intermediateCertificate:(MTRCertificateDERBytes _Nullable)intermediateCertificate |
| rootCertificate:(MTRCertificateDERBytes)rootCertificate |
| { |
| return [super initWithStorageDelegate:storageDelegate |
| storageDelegateQueue:storageDelegateQueue |
| uniqueIdentifier:uniqueIdentifier |
| ipk:ipk |
| vendorID:vendorID |
| operationalKeypair:operationalKeypair |
| operationalCertificate:operationalCertificate |
| intermediateCertificate:intermediateCertificate |
| rootCertificate:rootCertificate]; |
| } |
| @end |
| |
| @implementation MTRXPCDeviceControllerParameters |
| |
| @synthesize uniqueIdentifier = _uniqueIdentifier; |
| @synthesize xpcConnectionBlock = _xpcConnectionBlock; |
| |
| - (instancetype)initWithXPConnectionBlock:(NSXPCConnection * (^)(void) )xpcConnectionBlock |
| uniqueIdentifier:(NSUUID *)uniqueIdentifier; |
| { |
| if (self = [super _initInternal]) { |
| _xpcConnectionBlock = [xpcConnectionBlock copy]; |
| _uniqueIdentifier = [uniqueIdentifier copy]; |
| } |
| |
| return self; |
| } |
| @end |
| |
| @implementation MTRDeviceControllerStartupParamsInternal |
| |
| - (instancetype)initWithParams:(MTRDeviceControllerStartupParams *)params |
| { |
| if (!(self = [super initWithParams:params])) { |
| return nil; |
| } |
| |
| _storageDelegate = nil; |
| _storageDelegateQueue = nil; |
| |
| if (self.nocSigner == nil && self.rootCertificate == nil) { |
| MTR_LOG_ERROR("nocSigner and rootCertificate are both nil; no public key available to identify the fabric"); |
| return nil; |
| } |
| |
| if (self.operationalCertificate != nil && self.nodeID != nil) { |
| MTR_LOG_ERROR("nodeID must be nil if operationalCertificate is not nil"); |
| return nil; |
| } |
| |
| if (self.caseAuthenticatedTags != nil && self.nodeID == nil) { |
| MTR_LOG_ERROR("caseAuthenticatedTags must be nil if nodeID is nil"); |
| return nil; |
| } |
| |
| if (self.operationalCertificate != nil) { |
| if (self.operationalKeypair == nil) { |
| MTR_LOG_ERROR("Must have an operational keypair if an operational certificate is provided"); |
| return nil; |
| } |
| |
| if (![MTRCertificates keypair:self.operationalKeypair matchesCertificate:self.operationalCertificate]) { |
| MTR_LOG_ERROR("operationalKeypair public key does not match operationalCertificate"); |
| return nil; |
| } |
| } |
| |
| return self; |
| } |
| |
| - (instancetype)initForNewFabric:(chip::FabricTable *)fabricTable |
| keystore:(chip::Crypto::OperationalKeystore *)keystore |
| advertiseOperational:(BOOL)advertiseOperational |
| params:(MTRDeviceControllerStartupParams *)params |
| { |
| if (!(self = [self initWithParams:params])) { |
| return nil; |
| } |
| |
| if (self.nocSigner == nil && self.operationalCertificate == nil) { |
| MTR_LOG_ERROR("No way to get an operational certificate: nocSigner and operationalCertificate are both nil"); |
| return nil; |
| } |
| |
| if (self.operationalCertificate == nil && self.nodeID == nil) { |
| // Just avoid setting the top bit, to avoid issues with node |
| // ids outside the operational range. |
| uint64_t nodeId = arc4random(); |
| nodeId = (nodeId << 31) | (arc4random() >> 1); |
| self.nodeID = @(nodeId); |
| } |
| |
| if (self.rootCertificate == nil) { |
| NSError * error; |
| self.rootCertificate = [MTRCertificates createRootCertificate:self.nocSigner |
| issuerID:nil |
| fabricID:self.fabricID |
| error:&error]; |
| if (error != nil || self.rootCertificate == nil) { |
| MTR_LOG_ERROR("Failed to generate root certificate: %@", error); |
| return nil; |
| } |
| } |
| |
| _fabricTable = fabricTable; |
| _keystore = keystore; |
| _advertiseOperational = advertiseOperational; |
| _allowMultipleControllersPerFabric = NO; |
| |
| return self; |
| } |
| |
| - (instancetype)initForExistingFabric:(FabricTable *)fabricTable |
| fabricIndex:(FabricIndex)fabricIndex |
| keystore:(chip::Crypto::OperationalKeystore *)keystore |
| advertiseOperational:(BOOL)advertiseOperational |
| params:(MTRDeviceControllerStartupParams *)params |
| { |
| if (!(self = [self initWithParams:params])) { |
| return nil; |
| } |
| |
| const FabricInfo * fabric = fabricTable->FindFabricWithIndex(fabricIndex); |
| |
| if (self.vendorID == nil) { |
| self.vendorID = @(fabric->GetVendorId()); |
| } |
| |
| BOOL usingExistingNOC = NO; |
| if (self.operationalCertificate == nil && self.nodeID == nil) { |
| self.nodeID = @(fabric->GetNodeId()); |
| |
| // Make sure to preserve caseAuthenticatedTags from the existing certificate. |
| uint8_t nocBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan noc(nocBuf); |
| CHIP_ERROR err = fabricTable->FetchNOCCert(fabric->GetFabricIndex(), noc); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to get existing NOC: %s", ErrorStr(err)); |
| return nil; |
| } |
| |
| if (self.operationalKeypair == nil) { |
| self.operationalCertificate = MatterCertToX509Data(noc); |
| if (self.operationalCertificate == nil) { |
| MTR_LOG_ERROR("Failed to convert TLV NOC to DER X.509: %s", ErrorStr(err)); |
| return nil; |
| } |
| if (!keystore->HasOpKeypairForFabric(fabric->GetFabricIndex())) { |
| MTR_LOG_ERROR("No existing operational key for fabric"); |
| return nil; |
| } |
| } |
| |
| CATValues cats; |
| err = Credentials::ExtractCATsFromOpCert(noc, cats); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to extract existing CATs: %s", ErrorStr(err)); |
| return nil; |
| } |
| |
| auto tagCount = cats.GetNumTagsPresent(); |
| if (tagCount > 0) { |
| self.caseAuthenticatedTags = CATValuesToSet(cats); |
| } else { |
| self.caseAuthenticatedTags = nil; |
| } |
| |
| usingExistingNOC = YES; |
| } |
| |
| NSData * oldIntermediateCert = nil; |
| { |
| uint8_t icaBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan icaCert(icaBuf); |
| CHIP_ERROR err = fabricTable->FetchICACert(fabric->GetFabricIndex(), icaCert); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to get existing intermediate certificate: %s", ErrorStr(err)); |
| return nil; |
| } |
| // There might not be an ICA cert for this fabric. |
| if (!icaCert.empty()) { |
| oldIntermediateCert = MatterCertToX509Data(icaCert); |
| if (oldIntermediateCert == nil) { |
| return nil; |
| } |
| } |
| } |
| |
| if (self.nocSigner != nil && self.intermediateCertificate == nil && oldIntermediateCert != nil) { |
| // It's possible that we are switching from using an ICA cert to not using |
| // one. We can detect this case by checking whether the provided nocSigner |
| // matches the ICA cert. |
| if ([MTRCertificates keypair:self.nocSigner matchesCertificate:oldIntermediateCert] == YES) { |
| // Keep using the existing intermediate certificate. |
| self.intermediateCertificate = oldIntermediateCert; |
| } |
| // else presumably the nocSigner matches the root (will be verified later) |
| // and we are no longer using an intermediate. |
| } |
| |
| // If we were planning to use our existing NOC from the fabric info but we are |
| // changing from having an ICA to not having one, or changing from having one |
| // to not having one, or changing the identity of our ICA, we need to generate |
| // a new NOC. But we can keep our existing operational keypair and node id; |
| // nothing is forcing us to rotate those. |
| if (usingExistingNOC == YES |
| && ((oldIntermediateCert == nil) != (self.intermediateCertificate == nil) |
| || ((oldIntermediateCert != nil) && |
| [MTRCertificates isCertificate:oldIntermediateCert |
| equalTo:self.intermediateCertificate] |
| == NO))) { |
| self.operationalCertificate = nil; |
| } |
| |
| NSData * oldRootCert; |
| { |
| uint8_t rootBuf[Credentials::kMaxCHIPCertLength]; |
| MutableByteSpan rootCert(rootBuf); |
| CHIP_ERROR err = fabricTable->FetchRootCert(fabric->GetFabricIndex(), rootCert); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to get existing root certificate: %s", ErrorStr(err)); |
| return nil; |
| } |
| oldRootCert = MatterCertToX509Data(rootCert); |
| if (oldRootCert == nil) { |
| return nil; |
| } |
| } |
| |
| if (self.rootCertificate == nil) { |
| self.rootCertificate = oldRootCert; |
| } else if ([MTRCertificates isCertificate:oldRootCert equalTo:self.rootCertificate] == NO) { |
| MTR_LOG_ERROR("Root certificate identity does not match existing root certificate"); |
| return nil; |
| } |
| |
| _fabricTable = fabricTable; |
| _fabricIndex.Emplace(fabricIndex); |
| _keystore = keystore; |
| _advertiseOperational = advertiseOperational; |
| _allowMultipleControllersPerFabric = NO; |
| |
| return self; |
| } |
| |
| - (instancetype)initForNewController:(MTRDeviceController *)controller |
| fabricTable:(chip::FabricTable *)fabricTable |
| keystore:(chip::Crypto::OperationalKeystore *)keystore |
| advertiseOperational:(BOOL)advertiseOperational |
| params:(MTRDeviceControllerParameters *)params |
| error:(CHIP_ERROR &)error |
| { |
| if (!(self = [super initWithParameters:params error:error])) { |
| return nil; |
| } |
| |
| Crypto::P256PublicKey pubKey; |
| error = ExtractPubkeyFromX509Cert(AsByteSpan(self.rootCertificate), pubKey); |
| if (error != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Can't extract public key from root certificate: %s", error.AsString()); |
| return nil; |
| } |
| |
| NSNumber * nodeID; |
| error = ExtractNodeIDFromNOC(self.operationalCertificate, &nodeID); |
| if (error != CHIP_NO_ERROR) { |
| // Already logged. |
| return nil; |
| } |
| |
| if (fabricTable->FindIdentity(pubKey, self.fabricID.unsignedLongLongValue, nodeID.unsignedLongLongValue)) { |
| MTR_LOG_ERROR("Trying to start a controller identity that is already running"); |
| error = CHIP_ERROR_INVALID_ARGUMENT; |
| return nil; |
| } |
| |
| auto * oldNOCTLV = [controller.controllerDataStore fetchLastLocallyUsedNOC]; |
| if (oldNOCTLV != nil) { |
| ByteSpan oldNOCSpan = AsByteSpan(oldNOCTLV); |
| |
| FabricId ignored = kUndefinedFabricId; |
| NodeId oldNodeID = kUndefinedNodeId; |
| CHIP_ERROR err = Credentials::ExtractNodeIdFabricIdFromOpCert(oldNOCSpan, &oldNodeID, &ignored); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Unable to extract node ID and fabric ID from old operational certificate: %s", err.AsString()); |
| return nil; |
| } |
| |
| CATValues oldCATs; |
| err = Credentials::ExtractCATsFromOpCert(oldNOCSpan, oldCATs); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to extract CATs from old operational certificate: %s", err.AsString()); |
| return nil; |
| } |
| |
| auto * tlvNOC = [MTRCertificates convertX509Certificate:self.operationalCertificate]; |
| if (tlvNOC == nil) { |
| return nil; |
| } |
| |
| ByteSpan nocSpan = AsByteSpan(tlvNOC); |
| CATValues newCATs; |
| err = Credentials::ExtractCATsFromOpCert(nocSpan, newCATs); |
| if (err != CHIP_NO_ERROR) { |
| MTR_LOG_ERROR("Failed to extract CATs from new operational certificate: %s", err.AsString()); |
| return nil; |
| } |
| |
| if (nodeID.unsignedLongLongValue != oldNodeID || oldCATs != newCATs) { |
| // Our NOC has changed in a way that would affect ACL checks. Clear |
| // out our session resumption storage, because resuming those CASE |
| // sessions will end up doing ACL checks against our old NOC. |
| MTR_LOG("Node ID or CATs changed. Clearing CASE resumption storage."); |
| [controller.controllerDataStore clearAllResumptionInfo]; |
| } |
| } |
| |
| _fabricTable = fabricTable; |
| _keystore = keystore; |
| _advertiseOperational = advertiseOperational; |
| _allowMultipleControllersPerFabric = YES; |
| _storageDelegate = params.storageDelegate; |
| _storageDelegateQueue = params.storageDelegateQueue; |
| _productAttestationAuthorityCertificates = params.productAttestationAuthorityCertificates; |
| _certificationDeclarationCertificates = params.certificationDeclarationCertificates; |
| |
| return self; |
| } |
| |
| - (BOOL)keypairsMatchCertificates |
| { |
| if (self.nocSigner != nil) { |
| NSData * signingCert = self.intermediateCertificate; |
| if (signingCert == nil) { |
| signingCert = self.rootCertificate; |
| if (signingCert == nil) { |
| MTR_LOG_ERROR("No certificate to match nocSigner"); |
| return NO; |
| } |
| } |
| |
| if ([MTRCertificates keypair:self.nocSigner matchesCertificate:signingCert] == NO) { |
| MTR_LOG_ERROR("Provided nocSigner does not match certificates"); |
| return NO; |
| } |
| } |
| |
| if (self.operationalCertificate != nil && self.operationalKeypair != nil) { |
| if ([MTRCertificates keypair:self.operationalKeypair matchesCertificate:self.operationalCertificate] == NO) { |
| MTR_LOG_ERROR("Provided operationalKeypair does not match operationalCertificate"); |
| return NO; |
| } |
| } |
| |
| return YES; |
| } |
| |
| @end |