blob: 55f429caeca981df156176724991f2f4065e4517 [file] [log] [blame]
/*
* Copyright (c) 2022 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 "CHIPCommandBridge.h"
#import "CHIPToolKeypair.h"
#import <Matter/Matter.h>
#include <core/CHIPBuildConfig.h>
#include <lib/core/CHIPVendorIdentifiers.hpp>
#include "MTRError_Utils.h"
static CHIPToolPersistentStorageDelegate * storage = nil;
std::set<CHIPCommandBridge *> CHIPCommandBridge::sDeferredCleanups;
std::map<std::string, MTRDeviceController *> CHIPCommandBridge::mControllers;
dispatch_queue_t CHIPCommandBridge::mOTAProviderCallbackQueue;
OTAProviderDelegate * CHIPCommandBridge::mOTADelegate;
constexpr const char * kTrustStorePathVariable = "PAA_TRUST_STORE_PATH";
CHIPToolKeypair * gNocSigner = [[CHIPToolKeypair alloc] init];
CHIP_ERROR CHIPCommandBridge::Run()
{
ChipLogProgress(chipTool, "Running Command");
ReturnErrorOnFailure(MaybeSetUpStack());
SetIdentity(mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha);
ReturnLogErrorOnFailure(RunCommand());
auto err = StartWaiting(GetWaitDuration());
bool deferCleanup = (IsInteractive() && DeferInteractiveCleanup());
Shutdown();
if (deferCleanup) {
sDeferredCleanups.insert(this);
} else {
Cleanup();
}
MaybeTearDownStack();
return err;
}
CHIP_ERROR CHIPCommandBridge::GetPAACertsFromFolder(NSArray<NSData *> * __autoreleasing * paaCertsResult)
{
NSMutableArray * paaCerts = [[NSMutableArray alloc] init];
if (!mPaaTrustStorePath.HasValue()) {
char * const trust_store_path = getenv(kTrustStorePathVariable);
if (trust_store_path != nullptr) {
mPaaTrustStorePath.SetValue(trust_store_path);
}
}
if (mPaaTrustStorePath.HasValue()) {
NSError * error;
NSString * paaStorePath = [NSString stringWithCString:mPaaTrustStorePath.Value() encoding:NSUTF8StringEncoding];
NSArray * derFolder = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:paaStorePath error:&error];
if (error) {
NSLog(@"Error: %@", error);
return CHIP_ERROR_INTERNAL;
}
NSArray * derFiles = [derFolder filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self ENDSWITH '.der'"]];
if ([derFiles count] == 0) {
NSLog(@"Unable to find DER cert files");
return CHIP_ERROR_INTERNAL;
}
for (id derFile in derFiles) {
NSString * certPath = [NSString stringWithFormat:@"%@/%@", paaStorePath, derFile];
NSData * fileData = [NSData dataWithContentsOfFile:certPath];
if (fileData) {
[paaCerts addObject:fileData];
}
}
} else {
return CHIP_NO_ERROR;
}
if ([paaCerts count] == 0) {
NSLog(@"Unable to find PAA certs");
return CHIP_ERROR_INTERNAL;
}
*paaCertsResult = paaCerts;
return CHIP_NO_ERROR;
}
CHIP_ERROR CHIPCommandBridge::MaybeSetUpStack()
{
if (IsInteractive()) {
return CHIP_NO_ERROR;
}
NSData * ipk;
gNocSigner = [[CHIPToolKeypair alloc] init];
storage = [[CHIPToolPersistentStorageDelegate alloc] init];
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;
}
NSError * error;
if ([factory startControllerFactory:params error:&error] == NO) {
ChipLogError(chipTool, "Controller factory startup failed");
return MTRErrorToCHIPErrorCode(error);
}
ReturnLogErrorOnFailure([gNocSigner createOrLoadKeys:storage]);
ipk = [gNocSigner getIPK];
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];
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);
}
mControllers[identities[i]] = controller;
}
return CHIP_NO_ERROR;
}
void CHIPCommandBridge::MaybeTearDownStack()
{
if (IsInteractive()) {
return;
}
ShutdownCommissioner();
}
void CHIPCommandBridge::SetIdentity(const char * identity)
{
std::string name = std::string(identity);
if (name.compare(kIdentityAlpha) != 0 && name.compare(kIdentityBeta) != 0 && name.compare(kIdentityGamma) != 0) {
ChipLogError(chipTool, "Unknown commissioner name: %s. Supported names are [%s, %s, %s]", name.c_str(), kIdentityAlpha,
kIdentityBeta, kIdentityGamma);
chipDie();
}
mCurrentController = mControllers[name];
}
MTRDeviceController * CHIPCommandBridge::CurrentCommissioner() { return mCurrentController; }
MTRDeviceController * CHIPCommandBridge::GetCommissioner(const char * identity) { return mControllers[identity]; }
void CHIPCommandBridge::StopCommissioners()
{
for (auto & pair : mControllers) {
[pair.second shutdown];
}
}
void CHIPCommandBridge::RestartCommissioners()
{
StopCommissioners();
auto factory = [MTRDeviceControllerFactory sharedInstance];
NSData * ipk = [gNocSigner getIPK];
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];
auto controller = [factory createControllerOnExistingFabric:controllerParams error:nil];
mControllers[identities[i]] = controller;
}
}
void CHIPCommandBridge::ShutdownCommissioner()
{
ChipLogProgress(chipTool, "Shutting down controller");
StopCommissioners();
mControllers.clear();
mCurrentController = nil;
[[MTRDeviceControllerFactory sharedInstance] stopControllerFactory];
}
CHIP_ERROR CHIPCommandBridge::StartWaiting(chip::System::Clock::Timeout duration)
{
auto waitingUntil = std::chrono::system_clock::now() + std::chrono::duration_cast<std::chrono::seconds>(duration);
{
std::unique_lock<std::mutex> lk(cvWaitingForResponseMutex);
if (!cvWaitingForResponse.wait_until(lk, waitingUntil, [this]() { return !this->mWaitingForResponse; })) {
mCommandExitStatus = CHIP_ERROR_TIMEOUT;
}
}
return mCommandExitStatus;
}
void CHIPCommandBridge::StopWaiting()
{
{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = NO;
}
cvWaitingForResponse.notify_all();
}
void CHIPCommandBridge::SetCommandExitStatus(NSError * error, const char * logString)
{
if (logString != nullptr) {
LogNSError(logString, error);
}
CHIP_ERROR err = MTRErrorToCHIPErrorCode(error);
SetCommandExitStatus(err);
}
void CHIPCommandBridge::LogNSError(const char * logString, NSError * error)
{
CHIP_ERROR err = MTRErrorToCHIPErrorCode(error);
if (err == CHIP_NO_ERROR) {
ChipLogProgress(chipTool, "%s: %s", logString, chip::ErrorStr(err));
} else {
ChipLogError(chipTool, "%s: %s", logString, chip::ErrorStr(err));
}
}
void CHIPCommandBridge::ExecuteDeferredCleanups()
{
for (auto * cmd : sDeferredCleanups) {
cmd->Cleanup();
}
sDeferredCleanups.clear();
}