[darwin-framework-tool] Add an option to use per-controller storage instead of a global shared storage (#36044)
diff --git a/examples/darwin-framework-tool/BUILD.gn b/examples/darwin-framework-tool/BUILD.gn
index 5e7b7d6..0d1e142 100644
--- a/examples/darwin-framework-tool/BUILD.gn
+++ b/examples/darwin-framework-tool/BUILD.gn
@@ -190,10 +190,16 @@
"commands/common/CHIPCommandBridge.mm",
"commands/common/CHIPCommandStorageDelegate.mm",
"commands/common/CHIPToolKeypair.mm",
+ "commands/common/CertificateIssuer.h",
+ "commands/common/CertificateIssuer.mm",
+ "commands/common/ControllerStorage.h",
+ "commands/common/ControllerStorage.mm",
"commands/common/MTRDevice_Externs.h",
"commands/common/MTRError.mm",
"commands/common/MTRError_Utils.h",
"commands/common/MTRLogging.h",
+ "commands/common/PreferencesStorage.h",
+ "commands/common/PreferencesStorage.mm",
"commands/common/RemoteDataModelLogger.h",
"commands/common/RemoteDataModelLogger.mm",
"commands/configuration/Commands.h",
diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h
index 9458d30..f58251b 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h
+++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h
@@ -41,6 +41,8 @@
"Sets the commissioner node ID of the given "
"commissioner-name. Interactive mode will only set a single commissioner on the inital command. "
"The commissioner node ID will be persisted until a different one is specified.");
+ AddArgument("commissioner-shared-storage", 0, 1, &mCommissionerSharedStorage,
+ "Use a shared storage instance instead of individual storage for each commissioner. Default is true.");
AddArgument("paa-trust-store-path", &mPaaTrustStorePath,
"Path to directory holding PAA certificate information. Can be absolute or relative to the current working "
"directory.");
@@ -87,6 +89,7 @@
// This method returns the commissioner instance to be used for running the command.
MTRDeviceController * CurrentCommissioner();
+ NSNumber * CurrentCommissionerFabricId();
MTRDeviceController * GetCommissioner(const char * identity);
@@ -130,6 +133,8 @@
void StopWaiting();
CHIP_ERROR MaybeSetUpStack();
+ CHIP_ERROR SetUpStackWithSharedStorage(NSArray<NSData *> * productAttestationAuthorityCertificates);
+ CHIP_ERROR SetUpStackWithPerControllerStorage(NSArray<NSData *> * productAttestationAuthorityCertificates);
void MaybeTearDownStack();
CHIP_ERROR GetPAACertsFromFolder(NSArray<NSData *> * __autoreleasing * paaCertsResult);
@@ -140,6 +145,9 @@
// The current controller; the one the current command should be using.
MTRDeviceController * mCurrentController;
+ static bool sUseSharedStorage;
+ chip::Optional<bool> mCommissionerSharedStorage;
+
std::condition_variable cvWaitingForResponse;
std::mutex cvWaitingForResponseMutex;
chip::Optional<char *> mCommissionerName;
@@ -148,4 +156,5 @@
static dispatch_queue_t mOTAProviderCallbackQueue;
chip::Optional<char *> mPaaTrustStorePath;
chip::Optional<chip::VendorId> mCommissionerVendorId;
+ std::string mCurrentIdentity;
};
diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm
index d52c29a..724a9a2 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm
+++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm
@@ -23,7 +23,11 @@
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPVendorIdentifiers.hpp>
+#include <protocols/secure_channel/PASESession.h> // for chip::kTestControllerNodeId
+#import "CHIPCommandStorageDelegate.h"
+#import "CertificateIssuer.h"
+#import "ControllerStorage.h"
#include "MTRError_Utils.h"
#include <map>
@@ -34,10 +38,9 @@
std::map<std::string, MTRDeviceController *> CHIPCommandBridge::mControllers;
dispatch_queue_t CHIPCommandBridge::mOTAProviderCallbackQueue;
OTAProviderDelegate * CHIPCommandBridge::mOTADelegate;
+bool CHIPCommandBridge::sUseSharedStorage = true;
constexpr char kTrustStorePathVariable[] = "PAA_TRUST_STORE_PATH";
-CHIPToolKeypair * gNocSigner = [[CHIPToolKeypair alloc] init];
-
CHIP_ERROR CHIPCommandBridge::Run()
{
// In interactive mode, we want to avoid memory accumulating in the main autorelease pool,
@@ -120,61 +123,113 @@
CHIP_ERROR CHIPCommandBridge::MaybeSetUpStack()
{
- if (IsInteractive()) {
- return CHIP_NO_ERROR;
- }
- NSData * ipk;
- gNocSigner = [[CHIPToolKeypair alloc] init];
- storage = [[CHIPToolPersistentStorageDelegate alloc] init];
+ VerifyOrReturnError(!IsInteractive(), CHIP_NO_ERROR);
mOTADelegate = [[OTAProviderDelegate alloc] init];
-
- auto factory = [MTRDeviceControllerFactory sharedInstance];
- if (factory == nil) {
- ChipLogError(chipTool, "Controller factory is nil");
- return CHIP_ERROR_INTERNAL;
- }
-
- auto params = [[MTRDeviceControllerFactoryParams alloc] initWithStorage:storage];
- params.shouldStartServer = YES;
- params.otaProviderDelegate = mOTADelegate;
- NSArray<NSData *> * paaCertResults;
- ReturnLogErrorOnFailure(GetPAACertsFromFolder(&paaCertResults));
- if ([paaCertResults count] > 0) {
- params.productAttestationAuthorityCertificates = paaCertResults;
- }
+ storage = [[CHIPToolPersistentStorageDelegate alloc] init];
NSError * error;
- if ([factory startControllerFactory:params error:&error] == NO) {
- ChipLogError(chipTool, "Controller factory startup failed");
- return MTRErrorToCHIPErrorCode(error);
+ __auto_type * certificateIssuer = [CertificateIssuer sharedInstance];
+ [certificateIssuer startWithStorage:storage error:&error];
+ VerifyOrReturnError(nil == error, MTRErrorToCHIPErrorCode(error), ChipLogError(chipTool, "Can not start the certificate issuer: %@", error));
+
+ NSArray<NSData *> * productAttestationAuthorityCertificates = nil;
+ ReturnLogErrorOnFailure(GetPAACertsFromFolder(&productAttestationAuthorityCertificates));
+ if ([productAttestationAuthorityCertificates count] == 0) {
+ productAttestationAuthorityCertificates = nil;
}
- ReturnLogErrorOnFailure([gNocSigner createOrLoadKeys:storage]);
+ sUseSharedStorage = mCommissionerSharedStorage.ValueOr(true);
+ if (sUseSharedStorage) {
+ return SetUpStackWithSharedStorage(productAttestationAuthorityCertificates);
+ }
- ipk = [gNocSigner getIPK];
+ return SetUpStackWithPerControllerStorage(productAttestationAuthorityCertificates);
+}
+
+CHIP_ERROR CHIPCommandBridge::SetUpStackWithPerControllerStorage(NSArray<NSData *> * productAttestationAuthorityCertificates)
+{
+ __auto_type * certificateIssuer = [CertificateIssuer sharedInstance];
constexpr const char * identities[] = { kIdentityAlpha, kIdentityBeta, kIdentityGamma };
std::string commissionerName = mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha;
for (size_t i = 0; i < ArraySize(identities); ++i) {
- auto controllerParams = [[MTRDeviceControllerStartupParams alloc] initWithIPK:ipk fabricID:@(i + 1) nocSigner:gNocSigner];
+ __auto_type * uuidString = [NSString stringWithFormat:@"%@%@", @"8DCADB14-AF1F-45D0-B084-00000000000", @(i)];
+ __auto_type * controllerId = [[NSUUID alloc] initWithUUIDString:uuidString];
+ __auto_type * vendorId = @(mCommissionerVendorId.ValueOr(chip::VendorId::TestVendor1));
+ __auto_type * fabricId = @(i + 1);
+ __auto_type * nodeId = @(chip::kTestControllerNodeId);
if (commissionerName.compare(identities[i]) == 0 && mCommissionerNodeId.HasValue()) {
- controllerParams.nodeId = @(mCommissionerNodeId.Value());
- }
- // We're not sure whether we're creating a new fabric or using an
- // existing one, so just try both.
- auto controller = [factory createControllerOnExistingFabric:controllerParams error:&error];
- if (controller == nil) {
- // Maybe we didn't have this fabric yet.
- controllerParams.vendorID = @(mCommissionerVendorId.ValueOr(chip::VendorId::TestVendor1));
- controller = [factory createControllerOnNewFabric:controllerParams error:&error];
- }
- if (controller == nil) {
- ChipLogError(chipTool, "Controller startup failure.");
- return MTRErrorToCHIPErrorCode(error);
+ nodeId = @(mCommissionerNodeId.Value());
}
+ __auto_type * controllerStorage = [[ControllerStorage alloc] initWithControllerID:controllerId];
+
+ NSError * error;
+ __auto_type * operationalKeypair = [certificateIssuer issueOperationalKeypairWithControllerStorage:controllerStorage error:&error];
+ __auto_type * operational = [certificateIssuer issueOperationalCertificateForNodeID:nodeId
+ fabricID:fabricId
+ publicKey:operationalKeypair.publicKey
+ error:&error];
+ VerifyOrReturnError(nil == error, MTRErrorToCHIPErrorCode(error), ChipLogError(chipTool, "Can not issue an operational certificate: %@", error));
+
+ __auto_type * controllerStorageQueue = dispatch_queue_create("com.chip.storage", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
+ __auto_type * params = [[MTRDeviceControllerExternalCertificateParameters alloc] initWithStorageDelegate:controllerStorage
+ storageDelegateQueue:controllerStorageQueue
+ uniqueIdentifier:controllerId
+ ipk:certificateIssuer.ipk
+ vendorID:vendorId
+ operationalKeypair:operationalKeypair
+ operationalCertificate:operational
+ intermediateCertificate:nil
+ rootCertificate:certificateIssuer.rootCertificate];
+ [params setOperationalCertificateIssuer:certificateIssuer queue:controllerStorageQueue];
+ params.productAttestationAuthorityCertificates = productAttestationAuthorityCertificates;
+
+ __auto_type * controller = [[MTRDeviceController alloc] initWithParameters:params error:&error];
+ VerifyOrReturnError(nil != controller, MTRErrorToCHIPErrorCode(error), ChipLogError(chipTool, "Controller startup failure: %@", error));
+ mControllers[identities[i]] = controller;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CHIPCommandBridge::SetUpStackWithSharedStorage(NSArray<NSData *> * productAttestationAuthorityCertificates)
+{
+ __auto_type * factory = [MTRDeviceControllerFactory sharedInstance];
+ VerifyOrReturnError(nil != factory, CHIP_ERROR_INTERNAL, ChipLogError(chipTool, "Controller factory is nil"));
+
+ auto factoryParams = [[MTRDeviceControllerFactoryParams alloc] initWithStorage:storage];
+ factoryParams.shouldStartServer = YES;
+ factoryParams.otaProviderDelegate = mOTADelegate;
+ factoryParams.productAttestationAuthorityCertificates = productAttestationAuthorityCertificates;
+
+ NSError * error;
+ auto started = [factory startControllerFactory:factoryParams error:&error];
+ VerifyOrReturnError(started, MTRErrorToCHIPErrorCode(error), ChipLogError(chipTool, "Controller factory startup failed"));
+
+ __auto_type * certificateIssuer = [CertificateIssuer sharedInstance];
+
+ constexpr const char * identities[] = { kIdentityAlpha, kIdentityBeta, kIdentityGamma };
+ std::string commissionerName = mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha;
+ for (size_t i = 0; i < ArraySize(identities); ++i) {
+ __auto_type * fabricId = @(i + 1);
+ __auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithIPK:certificateIssuer.ipk
+ fabricID:fabricId
+ nocSigner:certificateIssuer.signingKey];
+ if (commissionerName.compare(identities[i]) == 0 && mCommissionerNodeId.HasValue()) {
+ params.nodeId = @(mCommissionerNodeId.Value());
+ }
+
+ // We're not sure whether we're creating a new fabric or using an existing one, so just try both.
+ auto controller = [factory createControllerOnExistingFabric:params error:&error];
+ if (controller == nil) {
+ // Maybe we didn't have this fabric yet.
+ params.vendorID = @(mCommissionerVendorId.ValueOr(chip::VendorId::TestVendor1));
+ controller = [factory createControllerOnNewFabric:params error:&error];
+ }
+ VerifyOrReturnError(nil != controller, MTRErrorToCHIPErrorCode(error), ChipLogError(chipTool, "Controller startup failure: %@", error));
mControllers[identities[i]] = controller;
}
@@ -197,11 +252,29 @@
kIdentityBeta, kIdentityGamma);
chipDie();
}
+ mCurrentIdentity = name;
mCurrentController = mControllers[name];
}
MTRDeviceController * CHIPCommandBridge::CurrentCommissioner() { return mCurrentController; }
+NSNumber * CHIPCommandBridge::CurrentCommissionerFabricId()
+{
+ if (mCurrentIdentity.compare(kIdentityAlpha) == 0) {
+ return @(1);
+ } else if (mCurrentIdentity.compare(kIdentityBeta) == 0) {
+ return @(2);
+ } else if (mCurrentIdentity.compare(kIdentityGamma) == 0) {
+ return @(3);
+ } else {
+ ChipLogError(chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", mCurrentIdentity.c_str(), kIdentityAlpha,
+ kIdentityBeta, kIdentityGamma);
+ chipDie();
+ }
+
+ return @(0); // This should never happens.
+}
+
MTRDeviceController * CHIPCommandBridge::GetCommissioner(const char * identity) { return mControllers[identity]; }
MTRBaseDevice * CHIPCommandBridge::BaseDeviceWithNodeId(chip::NodeId nodeId)
@@ -223,15 +296,25 @@
{
StopCommissioners();
- auto factory = [MTRDeviceControllerFactory sharedInstance];
- NSData * ipk = [gNocSigner getIPK];
+ if (sUseSharedStorage) {
+ auto factory = [MTRDeviceControllerFactory sharedInstance];
- constexpr const char * identities[] = { kIdentityAlpha, kIdentityBeta, kIdentityGamma };
- for (size_t i = 0; i < ArraySize(identities); ++i) {
- auto controllerParams = [[MTRDeviceControllerStartupParams alloc] initWithIPK:ipk fabricID:@(i + 1) nocSigner:gNocSigner];
+ constexpr const char * identities[] = { kIdentityAlpha, kIdentityBeta, kIdentityGamma };
+ for (size_t i = 0; i < ArraySize(identities); ++i) {
+ __auto_type * certificateIssuer = [CertificateIssuer sharedInstance];
+ auto controllerParams = [[MTRDeviceControllerStartupParams alloc] initWithIPK:certificateIssuer.ipk fabricID:@(i + 1) nocSigner:certificateIssuer.signingKey];
- auto controller = [factory createControllerOnExistingFabric:controllerParams error:nil];
- mControllers[identities[i]] = controller;
+ auto controller = [factory createControllerOnExistingFabric:controllerParams error:nil];
+ mControllers[identities[i]] = controller;
+ }
+ } else {
+ NSArray<NSData *> * productAttestationAuthorityCertificates = nil;
+ ReturnOnFailure(GetPAACertsFromFolder(&productAttestationAuthorityCertificates));
+ if ([productAttestationAuthorityCertificates count] == 0) {
+ productAttestationAuthorityCertificates = nil;
+ }
+
+ ReturnOnFailure(SetUpStackWithPerControllerStorage(productAttestationAuthorityCertificates));
}
}
diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.h b/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.h
index d5f743d..1da1b6b 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.h
+++ b/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.h
@@ -1,8 +1,28 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 <Foundation/Foundation.h>
#import <Matter/Matter.h>
NS_ASSUME_NONNULL_BEGIN
+extern NSString * const kDarwinFrameworkToolCertificatesDomain;
+
@interface CHIPToolPersistentStorageDelegate : NSObject <MTRStorage>
- (nullable NSData *)storageDataForKey:(NSString *)key;
- (BOOL)setStorageData:(NSData *)value forKey:(NSString *)key;
diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.mm b/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.mm
index 7cbdce8..3ec3dda 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.mm
+++ b/examples/darwin-framework-tool/commands/common/CHIPCommandStorageDelegate.mm
@@ -1,89 +1,70 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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.
+ *
+ */
+
#include "CHIPCommandStorageDelegate.h"
#import <Matter/Matter.h>
-#define LOG_DEBUG_PERSISTENT_STORAGE_DELEGATE 0
+#import "PreferencesStorage.h"
-NSString * const kCHIPToolDefaultsDomain = @"com.apple.chiptool";
+NSString * const kDarwinFrameworkToolCertificatesDomain = @"com.apple.chiptool";
-id MTRGetDomainValueForKey(NSString * domain, NSString * key)
-{
- id value = (id) CFBridgingRelease(CFPreferencesCopyAppValue((CFStringRef) key, (CFStringRef) domain));
- if (value) {
- return value;
- }
- return nil;
-}
-
-BOOL MTRSetDomainValueForKey(NSString * domain, NSString * key, id value)
-{
- CFPreferencesSetAppValue((CFStringRef) key, (__bridge CFPropertyListRef _Nullable)(value), (CFStringRef) domain);
- return CFPreferencesAppSynchronize((CFStringRef) domain) == true;
-}
-
-BOOL MTRRemoveDomainValueForKey(NSString * domain, NSString * key)
-{
- CFPreferencesSetAppValue((CFStringRef) key, nullptr, (CFStringRef) domain);
- return CFPreferencesAppSynchronize((CFStringRef) domain) == true;
-}
-
-id CHIPGetDomainKeyList(NSString * domain)
-{
- id value
- = (id) CFBridgingRelease(CFPreferencesCopyKeyList((CFStringRef) domain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost));
- if (value) {
- return value;
- }
- return nil;
-}
-
-BOOL CHIPClearAllDomain(NSString * domain)
-{
-
- NSArray * allKeys = CHIPGetDomainKeyList(domain);
-#if LOG_DEBUG_PERSISTENT_STORAGE_DELEGATE
- NSLog(@"Removing keys: %@ %@", allKeys, domain);
-#endif
- for (id key in allKeys) {
-#if LOG_DEBUG_PERSISTENT_STORAGE_DELEGATE
- NSLog(@"Removing key: %@", key);
-#endif
- if (!MTRRemoveDomainValueForKey(domain, (NSString *) key)) {
- return NO;
- }
- }
- return YES;
-}
+@interface CHIPToolPersistentStorageDelegate ()
+@property (nonatomic, readonly) PreferencesStorage * storage;
+@end
@implementation CHIPToolPersistentStorageDelegate
+- (instancetype)init
+{
+ if (!(self = [super init])) {
+ return nil;
+ }
+
+ _storage = [[PreferencesStorage alloc] initWithDomain:kDarwinFrameworkToolCertificatesDomain];
+ return self;
+}
+
- (BOOL)deleteAllStorage
{
- return CHIPClearAllDomain(kCHIPToolDefaultsDomain);
+ return [_storage reset];
}
// MARK: CHIPPersistentStorageDelegate
- (nullable NSData *)storageDataForKey:(NSString *)key
{
- NSData * value = MTRGetDomainValueForKey(kCHIPToolDefaultsDomain, key);
-#if LOG_DEBUG_PERSISTENT_STORAGE_DELEGATE
- NSLog(@"CHIPPersistentStorageDelegate Get Value for Key: %@, value %@", key, value);
-#endif
- return value;
+ return _storage[key];
}
- (BOOL)setStorageData:(NSData *)value forKey:(NSString *)key
{
- return MTRSetDomainValueForKey(kCHIPToolDefaultsDomain, key, value);
+ _storage[key] = value;
+ return YES;
}
- (BOOL)removeStorageDataForKey:(NSString *)key
{
- if (MTRGetDomainValueForKey(kCHIPToolDefaultsDomain, key) == nil) {
+ if (_storage[key] == nil) {
return NO;
}
- return MTRRemoveDomainValueForKey(kCHIPToolDefaultsDomain, key);
+ _storage[key] = nil;
+ return YES;
}
@end
diff --git a/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.h b/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.h
index a58d2e8..f0ee3f8 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.h
+++ b/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.h
@@ -1,4 +1,21 @@
-#include "CHIPCommandStorageDelegate.h"
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 <Matter/Matter.h>
#include <crypto/CHIPCryptoPAL.h>
@@ -8,7 +25,7 @@
- (SecKeyRef)publicKey;
- (CHIP_ERROR)Serialize:(chip::Crypto::P256SerializedKeypair &)output;
- (CHIP_ERROR)Deserialize:(chip::Crypto::P256SerializedKeypair &)input;
-- (CHIP_ERROR)createOrLoadKeys:(CHIPToolPersistentStorageDelegate *)storage;
+- (CHIP_ERROR)createOrLoadKeys:(id)storage;
- (NSData *)getIPK;
@end
diff --git a/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.mm b/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.mm
index 4d6f53d..ce0ef58 100644
--- a/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.mm
+++ b/examples/darwin-framework-tool/commands/common/CHIPToolKeypair.mm
@@ -1,3 +1,21 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 "CHIPToolKeypair.h"
#import <Matter/Matter.h>
#include <credentials/CHIPCert.h>
@@ -5,6 +23,9 @@
#include <lib/asn1/ASN1.h>
#include <stddef.h>
+#import "CHIPCommandStorageDelegate.h"
+#import "ControllerStorage.h"
+
#define CHIPPlugin_CAKeyTag "com.apple.matter.commissioner.ca.issuer.id"
#define Public_KeySize "256"
@@ -76,12 +97,10 @@
return _ipk;
}
-- (CHIP_ERROR)createOrLoadKeys:(CHIPToolPersistentStorageDelegate *)storage
+- (CHIP_ERROR)createOrLoadKeys:(id)storage
{
chip::ASN1::ASN1UniversalTime effectiveTime;
chip::Crypto::P256SerializedKeypair serializedKey;
- NSData * value;
- CHIP_ERROR err = CHIP_NO_ERROR;
// Initializing the default start validity to start of 2021. The default validity duration is 10 years.
CHIP_ZERO_AT(effectiveTime);
@@ -90,8 +109,8 @@
effectiveTime.Day = 1;
ReturnErrorOnFailure(chip::Credentials::ASN1ToChipEpochTime(effectiveTime, _mNow));
- value = [storage storageDataForKey:kOperationalCredentialsIssuerKeypairStorage];
- err = [self initSerializedKeyFromValue:value serializedKey:serializedKey];
+ __auto_type * value = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage];
+ __auto_type err = [self initSerializedKeyFromValue:value serializedKey:serializedKey];
if (err != CHIP_NO_ERROR) {
// Storage doesn't have an existing keypair. Let's create one and add it to the storage.
@@ -101,12 +120,12 @@
ReturnErrorOnFailure([self Serialize:serializedKey]);
NSData * valueData = [NSData dataWithBytes:serializedKey.Bytes() length:serializedKey.Length()];
- [storage setStorageData:valueData forKey:kOperationalCredentialsIssuerKeypairStorage];
+ [self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage value:valueData];
} else {
ReturnErrorOnFailure([self Deserialize:serializedKey]);
}
- NSData * ipk = [storage storageDataForKey:kOperationalCredentialsIPK];
+ NSData * ipk = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIPK];
if (ipk == nil) {
err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}
@@ -116,7 +135,7 @@
ReturnLogErrorOnFailure(chip::Crypto::DRBG_get_bytes(tempIPK, sizeof(tempIPK)));
_ipk = [NSData dataWithBytes:tempIPK length:sizeof(tempIPK)];
- [storage setStorageData:_ipk forKey:kOperationalCredentialsIPK];
+ [self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIPK value:_ipk];
} else {
_ipk = ipk;
}
@@ -124,6 +143,25 @@
return CHIP_NO_ERROR;
}
+- (NSData *)_getValueForKeyWithStorage:(id)storage key:(NSString *)key
+{
+ if ([storage isKindOfClass:[CHIPToolPersistentStorageDelegate class]]) {
+ return [storage storageDataForKey:key];
+ } else if ([storage isKindOfClass:[ControllerStorage class]]) {
+ return [storage valueForKey:key];
+ }
+ return nil;
+}
+
+- (void)_setValueForKeyWithStorage:(id)storage key:(NSString *)key value:(NSData *)value
+{
+ if ([storage isKindOfClass:[CHIPToolPersistentStorageDelegate class]]) {
+ [storage setStorageData:value forKey:key];
+ } else if ([storage isKindOfClass:[ControllerStorage class]]) {
+ [storage storeValue:value forKey:key];
+ }
+}
+
- (CHIP_ERROR)initSerializedKeyFromValue:(NSData *)value serializedKey:(chip::Crypto::P256SerializedKeypair &)serializedKey
{
if (value == nil) {
diff --git a/examples/darwin-framework-tool/commands/common/CertificateIssuer.h b/examples/darwin-framework-tool/commands/common/CertificateIssuer.h
new file mode 100644
index 0000000..0034f28
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/CertificateIssuer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 <Matter/Matter.h>
+
+#import "ControllerStorage.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CertificateIssuer : NSObject <MTROperationalCertificateIssuer>
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)new NS_UNAVAILABLE;
+
++ (CertificateIssuer *)sharedInstance;
+
+- (void)startWithStorage:(id<MTRStorage>)storage
+ error:(NSError * _Nullable __autoreleasing * _Nonnull)error;
+
+- (id<MTRKeypair>)issueOperationalKeypairWithControllerStorage:(ControllerStorage *)storage error:(NSError * _Nullable __autoreleasing * _Nonnull)error;
+
+- (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID
+ fabricID:(NSNumber *)fabricID
+ publicKey:(SecKeyRef)publicKey
+ error:(NSError * _Nullable __autoreleasing * _Nonnull)error;
+
+@property (nonatomic, readonly) MTRCertificateDERBytes rootCertificate;
+@property (nonatomic, readonly) id<MTRKeypair> signingKey;
+@property (nonatomic, readonly) NSData * ipk;
+
+@property (nonatomic, nullable) NSNumber * fabricID;
+@property (nonatomic, nullable) NSNumber * nextNodeID;
+@property (nonatomic, readonly) BOOL shouldSkipAttestationCertificateValidation;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm b/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm
new file mode 100644
index 0000000..bd12878
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/CertificateIssuer.mm
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 "CertificateIssuer.h"
+#import "CHIPToolKeypair.h"
+
+#include <lib/support/logging/CHIPLogging.h>
+
+@interface CertificateIssuer ()
+- (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID
+ fabricID:(NSNumber *)fabricID
+ publicKey:(SecKeyRef)publicKey
+ error:(NSError * _Nullable __autoreleasing * _Nonnull)error;
+@end
+
+@implementation CertificateIssuer
+
++ (CertificateIssuer *)sharedInstance
+{
+ static CertificateIssuer * certificateIssuer = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ certificateIssuer = [[CertificateIssuer alloc] init];
+ });
+ return certificateIssuer;
+}
+
+- (instancetype)init
+{
+ if (!(self = [super init])) {
+ return nil;
+ }
+
+ _rootCertificate = nil;
+ _ipk = nil;
+ _signingKey = nil;
+ _fabricID = nil;
+ _nextNodeID = nil;
+ _shouldSkipAttestationCertificateValidation = NO;
+
+ return self;
+}
+
+- (void)startWithStorage:(id<MTRStorage>)storage
+ error:(NSError * _Nullable __autoreleasing * _Nonnull)error
+{
+ __auto_type * signingKey = [[CHIPToolKeypair alloc] init];
+
+ __auto_type err = [signingKey createOrLoadKeys:storage];
+ if (CHIP_NO_ERROR != err) {
+ *error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating or loading keys" }];
+ return;
+ }
+
+ __auto_type * rootCertificate = [MTRCertificates createRootCertificate:signingKey issuerID:nil fabricID:nil error:error];
+ if (nil == rootCertificate) {
+ *error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating root certificate" }];
+ return;
+ }
+
+ _rootCertificate = rootCertificate;
+ _signingKey = signingKey;
+ _ipk = [signingKey getIPK];
+}
+
+- (id<MTRKeypair>)issueOperationalKeypairWithControllerStorage:(ControllerStorage *)storage error:(NSError * _Nullable __autoreleasing * _Nonnull)error
+{
+ __auto_type * keypair = [[CHIPToolKeypair alloc] init];
+
+ __auto_type err = [keypair createOrLoadKeys:storage];
+ if (CHIP_NO_ERROR != err) {
+ *error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating or loading keys" }];
+ return nil;
+ }
+
+ return keypair;
+}
+
+- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
+ attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo
+ controller:(MTRDeviceController *)controller
+ completion:(void (^)(MTROperationalCertificateChain * _Nullable info,
+ NSError * _Nullable error))completion
+{
+ NSError * error = nil;
+
+ if (self.nextNodeID == nil) {
+ error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"nextNodeID is nil" }];
+ completion(nil, error);
+ return;
+ }
+
+ __auto_type * csr = csrInfo.csr;
+ __auto_type * rawPublicKey = [MTRCertificates publicKeyFromCSR:csr error:&error];
+ if (error != nil) {
+ completion(nil, error);
+ return;
+ }
+
+ NSDictionary * attributes = @{
+ (__bridge NSString *) kSecAttrKeyType : (__bridge NSString *) kSecAttrKeyTypeECSECPrimeRandom,
+ (__bridge NSString *) kSecAttrKeyClass : (__bridge NSString *) kSecAttrKeyClassPublic
+ };
+ CFErrorRef keyCreationError = NULL;
+ SecKeyRef publicKey
+ = SecKeyCreateWithData((__bridge CFDataRef) rawPublicKey, (__bridge CFDictionaryRef) attributes, &keyCreationError);
+
+ __auto_type * operationalCert = [self issueOperationalCertificateForNodeID:self.nextNodeID
+ fabricID:self.fabricID
+ publicKey:publicKey
+ error:&error];
+
+ // Release no-longer-needed key before we do anything else.
+ CFRelease(publicKey);
+
+ if (error != nil) {
+ completion(nil, error);
+ return;
+ }
+
+ __auto_type * certChain = [[MTROperationalCertificateChain alloc] initWithOperationalCertificate:operationalCert
+ intermediateCertificate:nil
+ rootCertificate:self.rootCertificate
+ adminSubject:nil];
+ completion(certChain, nil);
+}
+
+- (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID
+ fabricID:(NSNumber *)fabricID
+ publicKey:(SecKeyRef)publicKey
+ error:(NSError * _Nullable __autoreleasing * _Nonnull)error
+{
+ __auto_type * operationalCert = [MTRCertificates createOperationalCertificate:self.signingKey
+ signingCertificate:self.rootCertificate
+ operationalPublicKey:publicKey
+ fabricID:fabricID
+ nodeID:nodeID
+ caseAuthenticatedTags:nil
+ error:error];
+ return operationalCert;
+}
+
+@end
diff --git a/examples/darwin-framework-tool/commands/common/ControllerStorage.h b/examples/darwin-framework-tool/commands/common/ControllerStorage.h
new file mode 100644
index 0000000..f70081f
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/ControllerStorage.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 <Matter/Matter.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern NSString * const kDarwinFrameworkToolControllerDomain;
+
+@interface ControllerStorage : NSObject <MTRDeviceControllerStorageDelegate>
+- (instancetype)initWithControllerID:(NSUUID *)controllerID;
+@property (nonatomic, readonly) NSUUID * controllerID;
+
+- (nullable id<NSSecureCoding>)controller:(MTRDeviceController *)controller
+ valueForKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType;
+- (BOOL)controller:(MTRDeviceController *)controller
+ storeValue:(id<NSSecureCoding>)value
+ forKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType;
+- (BOOL)controller:(MTRDeviceController *)controller
+ removeValueForKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType;
+- (NSDictionary<NSString *, id<NSSecureCoding>> *)valuesForController:(MTRDeviceController *)controller securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType;
+- (BOOL)controller:(MTRDeviceController *)controller storeValues:(NSDictionary<NSString *, id<NSSecureCoding>> *)values securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType;
+
+- (NSData *)valueForKey:(NSString *)key;
+- (void)storeValue:(NSData *)value forKey:key;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/examples/darwin-framework-tool/commands/common/ControllerStorage.mm b/examples/darwin-framework-tool/commands/common/ControllerStorage.mm
new file mode 100644
index 0000000..058f00a
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/ControllerStorage.mm
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 "ControllerStorage.h"
+#import "PreferencesStorage.h"
+
+#include <lib/support/logging/CHIPLogging.h>
+
+NSString * const kDarwinFrameworkToolControllerDomain = @"com.apple.darwin-framework-tool.controller";
+
+@interface ControllerStorage ()
+@property (nonatomic, readonly) PreferencesStorage * storage;
+@property (nonatomic, readonly) NSString * keyScopingPrefix;
+
+- (NSString *)_keyToControllerScopedKey:(NSString *)key;
+- (NSString *)_controllerScopedKeyToKey:(NSString *)controllerKey;
+- (BOOL)_isControllerScopedKey:(NSString *)controllerKey;
+@end
+
+@implementation ControllerStorage
+- (instancetype)initWithControllerID:(NSUUID *)controllerID
+{
+ if (!(self = [super init])) {
+ return nil;
+ }
+
+ _storage = [[PreferencesStorage alloc] initWithDomain:kDarwinFrameworkToolControllerDomain];
+ _controllerID = controllerID;
+ _keyScopingPrefix = [NSString stringWithFormat:@"%@/", [_controllerID UUIDString]];
+ return self;
+}
+
+- (nullable id<NSSecureCoding>)controller:(MTRDeviceController *)controller
+ valueForKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType
+{
+#ifdef LOG_DEBUG_CONTROLLER_STORAGE
+ ChipLogError(chipTool, "Controller(%@) Storage - Get value for %@", controller, key);
+#endif // LOG_DEBUG_CONTROLLER_STORAGE
+
+ __auto_type * controllerKey = [self _keyToControllerScopedKey:key];
+ __auto_type * data = self.storage[controllerKey];
+ if (data == nil) {
+ return data;
+ }
+
+ NSError * error;
+ id value = [NSKeyedUnarchiver unarchivedObjectOfClasses:MTRDeviceControllerStorageClasses() fromData:data error:&error];
+ return value;
+}
+
+- (BOOL)controller:(MTRDeviceController *)controller
+ storeValue:(id<NSSecureCoding>)value
+ forKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType
+{
+#ifdef LOG_DEBUG_CONTROLLER_STORAGE
+ ChipLogError(chipTool, "Controller(%@) Storage - Set value for %@", controller, key);
+#endif // LOG_DEBUG_CONTROLLER_STORAGE
+ NSError * error;
+ NSData * data = [NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:&error];
+
+ __auto_type * controllerKey = [self _keyToControllerScopedKey:key];
+ self.storage[controllerKey] = data;
+ return YES;
+}
+
+- (BOOL)controller:(MTRDeviceController *)controller
+ removeValueForKey:(NSString *)key
+ securityLevel:(MTRStorageSecurityLevel)securityLevel
+ sharingType:(MTRStorageSharingType)sharingType
+{
+#ifdef LOG_DEBUG_CONTROLLER_STORAGE
+ ChipLogError(chipTool, "Controller(%@) Storage - Remove value for %@", controller, key);
+#endif // LOG_DEBUG_CONTROLLER_STORAGE
+
+ __auto_type * controllerKey = [self _keyToControllerScopedKey:key];
+ self.storage[controllerKey] = nil;
+ return YES;
+}
+
+- (NSDictionary<NSString *, id<NSSecureCoding>> *)valuesForController:(MTRDeviceController *)controller securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType
+{
+#ifdef LOG_DEBUG_CONTROLLER_STORAGE
+ ChipLogError(chipTool, "Controller(%@) Storage - Get all values", controller);
+#endif // LOG_DEBUG_CONTROLLER_STORAGE
+
+ NSMutableDictionary * valuesToReturn = [NSMutableDictionary dictionary];
+ for (NSString * controllerKey in self.storage) {
+ if (![self _isControllerScopedKey:controllerKey]) {
+ continue;
+ }
+ __auto_type * key = [self _controllerScopedKeyToKey:controllerKey];
+ valuesToReturn[key] = [self controller:controller valueForKey:key securityLevel:securityLevel sharingType:sharingType];
+ }
+
+ if (!valuesToReturn.count) {
+ return nil;
+ }
+
+ return valuesToReturn;
+}
+
+- (BOOL)controller:(MTRDeviceController *)controller storeValues:(NSDictionary<NSString *, id<NSSecureCoding>> *)values securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType
+{
+#ifdef LOG_DEBUG_CONTROLLER_STORAGE
+ ChipLogError(chipTool, "Controller(%@) Storage - store values", controller);
+#endif // LOG_DEBUG_CONTROLLER_STORAGE
+
+ for (NSString * key in values) {
+ [self controller:controller storeValue:values[key] forKey:key securityLevel:securityLevel sharingType:sharingType];
+ }
+
+ return YES;
+}
+
+- (NSData *)valueForKey:(NSString *)key
+{
+ __auto_type * controllerKey = [self _keyToControllerScopedKey:key];
+ return self.storage[controllerKey];
+}
+
+- (void)storeValue:(NSData *)value forKey:key
+{
+ __auto_type * controllerKey = [self _keyToControllerScopedKey:key];
+ self.storage[controllerKey] = value;
+}
+
+- (NSString *)_keyToControllerScopedKey:(NSString *)key
+{
+ return [NSString stringWithFormat:@"%@%@", _keyScopingPrefix, key];
+}
+
+- (NSString *)_controllerScopedKeyToKey:(NSString *)controllerKey
+{
+ return [controllerKey substringFromIndex:_keyScopingPrefix.length];
+}
+
+- (BOOL)_isControllerScopedKey:(NSString *)controllerKey
+{
+ return [controllerKey hasPrefix:_keyScopingPrefix];
+}
+@end
diff --git a/examples/darwin-framework-tool/commands/common/PreferencesStorage.h b/examples/darwin-framework-tool/commands/common/PreferencesStorage.h
new file mode 100644
index 0000000..85c1203
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/PreferencesStorage.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 <Foundation/Foundation.h>
+
+@interface PreferencesStorage : NSObject <NSFastEnumeration>
+
+@property (nonatomic, strong) NSString * domain;
+
+- (instancetype)initWithDomain:(NSString *)domain;
+- (NSData *)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+- (NSArray<NSString *> *)allKeys;
+- (bool)reset;
+- (void)print;
+
+@end
diff --git a/examples/darwin-framework-tool/commands/common/PreferencesStorage.mm b/examples/darwin-framework-tool/commands/common/PreferencesStorage.mm
new file mode 100644
index 0000000..e7ddba1
--- /dev/null
+++ b/examples/darwin-framework-tool/commands/common/PreferencesStorage.mm
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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 "PreferencesStorage.h"
+
+@implementation PreferencesStorage
+- (instancetype)initWithDomain:(NSString *)domain
+{
+ self = [super init];
+ if (self) {
+ _domain = domain;
+ }
+
+ return self;
+}
+
+- (NSData *)objectForKeyedSubscript:(NSString *)key
+{
+ __auto_type domainRef = (__bridge CFStringRef) self.domain;
+ __auto_type keyRef = (__bridge CFStringRef) key;
+ __auto_type value = CFPreferencesCopyAppValue(keyRef, domainRef);
+ if (value) {
+ id obj = (__bridge_transfer id) value;
+ return obj;
+ }
+ return nil;
+}
+
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key
+{
+ __auto_type domainRef = (__bridge CFStringRef) self.domain;
+ __auto_type keyRef = (__bridge CFStringRef) key;
+ __auto_type value = (__bridge CFPropertyListRef) obj;
+
+ CFPreferencesSetAppValue(keyRef, value, domainRef);
+ CFPreferencesAppSynchronize(domainRef);
+}
+
+- (NSArray<NSString *> *)allKeys
+{
+ __auto_type domainRef = (__bridge CFStringRef) self.domain;
+ __auto_type keys = CFPreferencesCopyKeyList(domainRef, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+
+ if (!keys) {
+ return @[];
+ }
+
+ return (__bridge_transfer NSArray *) keys;
+}
+
+- (bool)reset
+{
+ __auto_type * keys = [self allKeys];
+ __auto_type domainRef = (__bridge CFStringRef) self.domain;
+
+ for (NSString * key in keys) {
+ __auto_type keyRef = (__bridge CFStringRef) key;
+ CFPreferencesSetAppValue(keyRef, NULL, domainRef);
+ }
+
+ return CFPreferencesAppSynchronize(domainRef);
+}
+
+- (void)print
+{
+ NSLog(@"%@:", self.domain);
+ NSArray<NSString *> * keys = [self allKeys];
+ for (NSString * key in keys) {
+ __auto_type * data = [self objectForKeyedSubscript:key];
+ NSLog(@" * %@: %@", key, data);
+ }
+}
+
+#pragma mark - NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable __unsafe_unretained[])buffer count:(NSUInteger)len
+{
+ __auto_type * keys = [self allKeys];
+ if (state->state >= keys.count) {
+ return 0;
+ }
+
+ state->itemsPtr = buffer;
+ state->mutationsPtr = &state->extra[0];
+
+ NSUInteger count = 0;
+ while (state->state < keys.count && count < len) {
+ buffer[count] = keys[state->state];
+ state->state++;
+ count++;
+ }
+ return count;
+}
+
+@end
diff --git a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm
index 1a06081..3ec4aee 100644
--- a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm
+++ b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm
@@ -19,6 +19,7 @@
#import <Matter/Matter.h>
#include "../common/CHIPCommandBridge.h"
+#include "../common/CertificateIssuer.h"
#include "DeviceControllerDelegateBridge.h"
#include "PairingCommandBridge.h"
#include <lib/support/logging/CHIPLogging.h>
@@ -52,6 +53,12 @@
[deviceControllerDelegate setCommandBridge:this];
[deviceControllerDelegate setDeviceID:mNodeId];
+ // With per-controller storage, the certificate issuer creates the operational certificate.
+ // When using shared storage, this step is a no-op.
+ auto * certificateIssuer = [CertificateIssuer sharedInstance];
+ certificateIssuer.nextNodeID = @(mNodeId);
+ certificateIssuer.fabricID = CurrentCommissionerFabricId();
+
if (mCommissioningType != CommissioningType::None) {
MTRCommissioningParameters * params = [[MTRCommissioningParameters alloc] init];
switch (mCommissioningType) {
diff --git a/examples/darwin-framework-tool/commands/storage/Commands.h b/examples/darwin-framework-tool/commands/storage/Commands.h
index 6553427..983fb22 100644
--- a/examples/darwin-framework-tool/commands/storage/Commands.h
+++ b/examples/darwin-framework-tool/commands/storage/Commands.h
@@ -25,7 +25,10 @@
{
const char * clusterName = "storage";
- commands_list clusterCommands = { make_unique<StorageClearAll>() };
+ commands_list clusterCommands = {
+ make_unique<StorageViewAll>(), //
+ make_unique<StorageClearAll>(), //
+ };
commands.RegisterCommandSet(clusterName, clusterCommands,
"Commands for managing persistent data stored by darwin-framework-tool.");
diff --git a/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.h b/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.h
index c2abddf..8c3ed69 100644
--- a/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.h
+++ b/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.h
@@ -29,3 +29,11 @@
CHIP_ERROR Run() override;
};
+
+class StorageViewAll : public Command
+{
+public:
+ StorageViewAll() : Command("view-all") {}
+
+ CHIP_ERROR Run() override;
+};
diff --git a/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.mm b/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.mm
index e0c1b18..007d282 100644
--- a/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.mm
+++ b/examples/darwin-framework-tool/commands/storage/StorageManagementCommand.mm
@@ -17,18 +17,43 @@
*/
#include "../common/CHIPCommandStorageDelegate.h"
+#include "../common/ControllerStorage.h"
+#include "../common/PreferencesStorage.h"
#include "StorageManagementCommand.h"
#import <Matter/Matter.h>
-static CHIPToolPersistentStorageDelegate * storage = nil;
+namespace {
+NSArray<NSString *> * GetDomains()
+{
+ __auto_type * domains = @[
+ kDarwinFrameworkToolCertificatesDomain,
+ kDarwinFrameworkToolControllerDomain
+ ];
+
+ return domains;
+}
+}
CHIP_ERROR StorageClearAll::Run()
{
- storage = [[CHIPToolPersistentStorageDelegate alloc] init];
- if (![storage deleteAllStorage]) {
- return CHIP_ERROR_INTERNAL;
+ __auto_type * domains = GetDomains();
+ for (NSString * domain in domains) {
+ __auto_type * storage = [[PreferencesStorage alloc] initWithDomain:domain];
+ VerifyOrReturnError([storage reset], CHIP_ERROR_INTERNAL);
}
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR StorageViewAll::Run()
+{
+ __auto_type * domains = GetDomains();
+ for (NSString * domain in domains) {
+ __auto_type * storage = [[PreferencesStorage alloc] initWithDomain:domain];
+ [storage print];
+ }
+
return CHIP_NO_ERROR;
}
diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
index 173fb7b..e191785 100644
--- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
@@ -334,6 +334,12 @@
B43B39EC2CB859A5006AA284 /* DumpMemoryGraphCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39E52CB859A5006AA284 /* DumpMemoryGraphCommand.h */; };
B43B39ED2CB859A5006AA284 /* Commands.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39E42CB859A5006AA284 /* Commands.h */; };
B43B39EE2CB859A5006AA284 /* LeaksTool.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39E72CB859A5006AA284 /* LeaksTool.h */; };
+ B43B39F52CB99090006AA284 /* ControllerStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = B43B39F22CB99090006AA284 /* ControllerStorage.mm */; };
+ B43B39F62CB99090006AA284 /* CertificateIssuer.mm in Sources */ = {isa = PBXBuildFile; fileRef = B43B39F02CB99090006AA284 /* CertificateIssuer.mm */; };
+ B43B39F72CB99090006AA284 /* PreferencesStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = B43B39F42CB99090006AA284 /* PreferencesStorage.mm */; };
+ B43B39F82CB99090006AA284 /* CertificateIssuer.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39EF2CB99090006AA284 /* CertificateIssuer.h */; };
+ B43B39F92CB99090006AA284 /* PreferencesStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39F32CB99090006AA284 /* PreferencesStorage.h */; };
+ B43B39FA2CB99090006AA284 /* ControllerStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B43B39F12CB99090006AA284 /* ControllerStorage.h */; };
B45373AA2A9FE73400807602 /* WebSocketServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B45373A92A9FE73400807602 /* WebSocketServer.cpp */; };
B45373BD2A9FEA9100807602 /* service.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373B22A9FEA9000807602 /* service.c */; settings = {COMPILER_FLAGS = "-Wno-error -Wno-unreachable-code -Wno-conversion -Wno-format-nonliteral"; }; };
B45373BE2A9FEA9100807602 /* network.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373B32A9FEA9000807602 /* network.c */; settings = {COMPILER_FLAGS = "-Wno-error -Wno-unreachable-code -Wno-conversion -Wno-format-nonliteral"; }; };
@@ -785,6 +791,12 @@
B43B39E62CB859A5006AA284 /* DumpMemoryGraphCommand.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DumpMemoryGraphCommand.mm; sourceTree = "<group>"; };
B43B39E72CB859A5006AA284 /* LeaksTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LeaksTool.h; sourceTree = "<group>"; };
B43B39E82CB859A5006AA284 /* LeaksTool.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LeaksTool.mm; sourceTree = "<group>"; };
+ B43B39EF2CB99090006AA284 /* CertificateIssuer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CertificateIssuer.h; sourceTree = "<group>"; };
+ B43B39F02CB99090006AA284 /* CertificateIssuer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CertificateIssuer.mm; sourceTree = "<group>"; };
+ B43B39F12CB99090006AA284 /* ControllerStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ControllerStorage.h; sourceTree = "<group>"; };
+ B43B39F22CB99090006AA284 /* ControllerStorage.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ControllerStorage.mm; sourceTree = "<group>"; };
+ B43B39F32CB99090006AA284 /* PreferencesStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PreferencesStorage.h; sourceTree = "<group>"; };
+ B43B39F42CB99090006AA284 /* PreferencesStorage.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PreferencesStorage.mm; sourceTree = "<group>"; };
B45373A92A9FE73400807602 /* WebSocketServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketServer.cpp; sourceTree = "<group>"; };
B45373B22A9FEA9000807602 /* service.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = service.c; path = "repo/lib/core-net/service.c"; sourceTree = "<group>"; };
B45373B32A9FEA9000807602 /* network.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = network.c; path = "repo/lib/core-net/network.c"; sourceTree = "<group>"; };
@@ -987,6 +999,12 @@
037C3D9B2991BD4F00B7EEE2 /* common */ = {
isa = PBXGroup;
children = (
+ B43B39EF2CB99090006AA284 /* CertificateIssuer.h */,
+ B43B39F02CB99090006AA284 /* CertificateIssuer.mm */,
+ B43B39F12CB99090006AA284 /* ControllerStorage.h */,
+ B43B39F22CB99090006AA284 /* ControllerStorage.mm */,
+ B43B39F32CB99090006AA284 /* PreferencesStorage.h */,
+ B43B39F42CB99090006AA284 /* PreferencesStorage.mm */,
B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */,
B4E262122AA0C7A300DBA5BC /* RemoteDataModelLogger.mm */,
037C3D9C2991BD4F00B7EEE2 /* CHIPCommandBridge.mm */,
@@ -1632,6 +1650,9 @@
B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */,
037C3DC32991BD5100B7EEE2 /* Commands.h in Headers */,
B4F773CA2CB54B61008C6B23 /* LeakChecker.h in Headers */,
+ B43B39F82CB99090006AA284 /* CertificateIssuer.h in Headers */,
+ B43B39F92CB99090006AA284 /* PreferencesStorage.h in Headers */,
+ B43B39FA2CB99090006AA284 /* ControllerStorage.h in Headers */,
037C3DB82991BD5000B7EEE2 /* ClusterCommandBridge.h in Headers */,
037C3DC82991BD5100B7EEE2 /* CHIPToolKeypair.h in Headers */,
037C3DB52991BD5000B7EEE2 /* WriteAttributeCommandBridge.h in Headers */,
@@ -1971,6 +1992,9 @@
B45373DF2A9FEB6F00807602 /* system.c in Sources */,
B45373FC2A9FEC4F00807602 /* unix-caps.c in Sources */,
B45373FE2A9FEC4F00807602 /* unix-fds.c in Sources */,
+ B43B39F52CB99090006AA284 /* ControllerStorage.mm in Sources */,
+ B43B39F62CB99090006AA284 /* CertificateIssuer.mm in Sources */,
+ B43B39F72CB99090006AA284 /* PreferencesStorage.mm in Sources */,
B43B39EA2CB859A5006AA284 /* DumpMemoryGraphCommand.mm in Sources */,
B43B39EB2CB859A5006AA284 /* LeaksTool.mm in Sources */,
B45374002A9FEC4F00807602 /* unix-init.c in Sources */,