blob: 856a4daa48753d30194e66262267d0318145b68b [file] [log] [blame]
/*
* 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.
*
*/
#pragma once
#ifdef CONFIG_USE_LOCAL_STORAGE
#include <controller/ExamplePersistentStorage.h>
#endif // CONFIG_USE_LOCAL_STORAGE
#include "Command.h"
#include <TracingCommandLineArgument.h>
#include <app/icd/client/CheckInHandler.h>
#include <app/icd/client/DefaultCheckInDelegate.h>
#include <app/icd/client/DefaultICDClientStorage.h>
#include <commands/common/CredentialIssuerCommands.h>
#include <commands/example/ExampleCredentialIssuerCommands.h>
#include <credentials/GroupDataProviderImpl.h>
#include <credentials/PersistentStorageOpCertStore.h>
#include <crypto/PersistentStorageOperationalKeystore.h>
#include <crypto/RawKeySessionKeystore.h>
#include <string>
inline constexpr char kIdentityAlpha[] = "alpha";
inline constexpr char kIdentityBeta[] = "beta";
inline constexpr char kIdentityGamma[] = "gamma";
// The null fabric commissioner is a commissioner that isn't on a fabric.
// This is a legal configuration in which the commissioner delegates
// operational communication and invocation of the commssioning complete
// command to a separate on-fabric administrator node.
//
// The null-fabric-commissioner identity is provided here to demonstrate the
// commissioner portion of such an architecture. The null-fabric-commissioner
// can carry a commissioning flow up until the point of operational channel
// (CASE) communcation.
inline constexpr char kIdentityNull[] = "null-fabric-commissioner";
class CHIPCommand : public Command
{
public:
using ChipDeviceCommissioner = ::chip::Controller::DeviceCommissioner;
using ChipDeviceController = ::chip::Controller::DeviceController;
using IPAddress = ::chip::Inet::IPAddress;
using NodeId = ::chip::NodeId;
using PeerId = ::chip::PeerId;
using PeerAddress = ::chip::Transport::PeerAddress;
static constexpr uint16_t kMaxGroupsPerFabric = 50;
static constexpr uint16_t kMaxGroupKeysPerFabric = 25;
CHIPCommand(const char * commandName, CredentialIssuerCommands * credIssuerCmds, const char * helpText = nullptr) :
Command(commandName, helpText), mCredIssuerCmds(credIssuerCmds)
{
AddArgument("paa-trust-store-path", &mPaaTrustStorePath,
"Path to directory holding PAA certificate information. Can be absolute or relative to the current working "
"directory.");
AddArgument("cd-trust-store-path", &mCDTrustStorePath,
"Path to directory holding CD certificate information. Can be absolute or relative to the current working "
"directory.");
AddArgument("commissioner-name", &mCommissionerName,
"Name of fabric to use. Valid values are \"alpha\", \"beta\", \"gamma\", and integers greater than or equal to "
"4. The default if not specified is \"alpha\".");
AddArgument("commissioner-nodeid", 0, UINT64_MAX, &mCommissionerNodeId,
"The node id to use for fabric-admin. If not provided, kTestControllerNodeId (112233, 0x1B669) will be used.");
AddArgument("use-max-sized-certs", 0, 1, &mUseMaxSizedCerts,
"Maximize the size of operational certificates. If not provided or 0 (\"false\"), normally sized operational "
"certificates are generated.");
AddArgument("only-allow-trusted-cd-keys", 0, 1, &mOnlyAllowTrustedCdKeys,
"Only allow trusted CD verifying keys (disallow test keys). If not provided or 0 (\"false\"), untrusted CD "
"verifying keys are allowed. If 1 (\"true\"), test keys are disallowed.");
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
AddArgument("trace_file", &mTraceFile);
AddArgument("trace_log", 0, 1, &mTraceLog);
AddArgument("trace_decode", 0, 1, &mTraceDecode);
#endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
AddArgument("trace-to", &mTraceTo, "Trace destinations, comma-separated (" SUPPORTED_COMMAND_LINE_TRACING_TARGETS ")");
AddArgument("ble-adapter", 0, UINT16_MAX, &mBleAdapterId);
AddArgument("storage-directory", &mStorageDirectory,
"Directory to place fabric-admin's storage files in. Defaults to $TMPDIR, with fallback to /tmp");
AddArgument(
"commissioner-vendor-id", 0, UINT16_MAX, &mCommissionerVendorId,
"The vendor id to use for fabric-admin. If not provided, chip::VendorId::TestVendor1 (65521, 0xFFF1) will be used.");
}
/////////// Command Interface /////////
CHIP_ERROR Run() override;
void SetCommandExitStatus(CHIP_ERROR status)
{
mCommandExitStatus = status;
// In interactive mode the stack is not shut down once a command is ended.
// That means calling `ErrorStr(err)` from the main thread when command
// completion is signaled may race since `ErrorStr` uses a static sErrorStr
// buffer for computing the error string. Call it here instead.
if (IsInteractive() && CHIP_NO_ERROR != status)
{
ChipLogError(NotSpecified, "Run command failure: %s", chip::ErrorStr(status));
}
StopWaiting();
}
protected:
// Will be called in a setting in which it's safe to touch the CHIP
// stack. The rules for Run() are as follows:
//
// 1) If error is returned, Run() must not call SetCommandExitStatus.
// 2) If success is returned Run() must either have called
// SetCommandExitStatus() or scheduled async work that will do that.
virtual CHIP_ERROR RunCommand() = 0;
// Get the wait duration, in seconds, before the command times out.
virtual chip::System::Clock::Timeout GetWaitDuration() const = 0;
// Shut down the command. After a Shutdown call the command object is ready
// to be used for another command invocation.
virtual void Shutdown() { ResetArguments(); }
// Clean up any resources allocated by the command. Some commands may hold
// on to resources after Shutdown(), but Cleanup() will guarantee those are
// cleaned up.
virtual void Cleanup() {}
// If true, skip calling Cleanup() when in interactive mode, so the command
// can keep doing work as needed. Cleanup() will be called when quitting
// interactive mode. This method will be called before Shutdown, so it can
// use member values that Shutdown will normally reset.
virtual bool DeferInteractiveCleanup() { return false; }
// If true, the controller will be created with server capabilities enabled,
// such as advertising operational nodes over DNS-SD and accepting incoming
// CASE sessions.
virtual bool NeedsOperationalAdvertising() { return mAdvertiseOperational; }
// Execute any deferred cleanups. Used when exiting interactive mode.
static void ExecuteDeferredCleanups(intptr_t ignored);
#ifdef CONFIG_USE_LOCAL_STORAGE
PersistentStorage mDefaultStorage;
// TODO: It's pretty weird that we re-init mCommissionerStorage for every
// identity without shutting it down or something in between...
PersistentStorage mCommissionerStorage;
#endif // CONFIG_USE_LOCAL_STORAGE
chip::PersistentStorageOperationalKeystore mOperationalKeystore;
chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
static chip::Crypto::RawKeySessionKeystore sSessionKeystore;
static chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
static chip::app::DefaultICDClientStorage sICDClientStorage;
static chip::app::DefaultCheckInDelegate sCheckInDelegate;
static chip::app::CheckInHandler sCheckInHandler;
CredentialIssuerCommands * mCredIssuerCmds;
std::string GetIdentity();
CHIP_ERROR GetIdentityNodeId(std::string identity, chip::NodeId * nodeId);
CHIP_ERROR GetIdentityRootCertificate(std::string identity, chip::ByteSpan & span);
void SetIdentity(const char * name);
// This method returns the commissioner instance to be used for running the command.
// The default commissioner instance name is "alpha", but it can be overridden by passing
// --identity "instance name" when running a command.
ChipDeviceCommissioner & CurrentCommissioner();
ChipDeviceCommissioner & GetCommissioner(std::string identity);
private:
CHIP_ERROR MaybeSetUpStack();
void MaybeTearDownStack();
CHIP_ERROR EnsureCommissionerForIdentity(std::string identity);
// Commissioners are keyed by name and local node id.
struct CommissionerIdentity
{
bool operator<(const CommissionerIdentity & other) const
{
return mName < other.mName || (mName == other.mName && mLocalNodeId < other.mLocalNodeId);
}
std::string mName;
chip::NodeId mLocalNodeId;
uint8_t mRCAC[chip::Controller::kMaxCHIPDERCertLength] = {};
uint8_t mICAC[chip::Controller::kMaxCHIPDERCertLength] = {};
uint8_t mNOC[chip::Controller::kMaxCHIPDERCertLength] = {};
size_t mRCACLen;
size_t mICACLen;
size_t mNOCLen;
};
// InitializeCommissioner uses various members, so can't be static. This is
// obviously a little odd, since the commissioners are then shared across
// multiple commands in interactive mode...
CHIP_ERROR InitializeCommissioner(CommissionerIdentity & identity, chip::FabricId fabricId);
void ShutdownCommissioner(const CommissionerIdentity & key);
chip::FabricId CurrentCommissionerId();
static std::map<CommissionerIdentity, std::unique_ptr<ChipDeviceCommissioner>> mCommissioners;
static std::set<CHIPCommand *> sDeferredCleanups;
chip::Optional<char *> mCommissionerName;
chip::Optional<chip::NodeId> mCommissionerNodeId;
chip::Optional<chip::VendorId> mCommissionerVendorId;
chip::Optional<uint16_t> mBleAdapterId;
chip::Optional<char *> mPaaTrustStorePath;
chip::Optional<char *> mCDTrustStorePath;
chip::Optional<bool> mUseMaxSizedCerts;
chip::Optional<bool> mOnlyAllowTrustedCdKeys;
// Cached trust store so commands other than the original startup command
// can spin up commissioners as needed.
static const chip::Credentials::AttestationTrustStore * sTrustStore;
static void RunQueuedCommand(intptr_t commandArg);
typedef decltype(RunQueuedCommand) MatterWorkCallback;
static void RunCommandCleanup(intptr_t commandArg);
// Do cleanup after a commmand is done running. Must happen with the
// Matter stack locked.
void CleanupAfterRun();
// Run the given callback on the Matter thread. Return whether we managed
// to successfully dispatch it to the Matter thread. If we did, *timedOut
// will be set to whether we timed out or whether our mWaitingForResponse
// got set to false by the callback itself.
CHIP_ERROR RunOnMatterQueue(MatterWorkCallback callback, chip::System::Clock::Timeout timeout, bool * timedOut);
CHIP_ERROR mCommandExitStatus = CHIP_ERROR_INTERNAL;
CHIP_ERROR StartWaiting(chip::System::Clock::Timeout seconds);
void StopWaiting();
#if CONFIG_USE_SEPARATE_EVENTLOOP
std::condition_variable cvWaitingForResponse;
std::mutex cvWaitingForResponseMutex;
bool mWaitingForResponse{ true };
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
void StartTracing();
void StopTracing();
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
chip::Optional<char *> mTraceFile;
chip::Optional<bool> mTraceLog;
chip::Optional<bool> mTraceDecode;
#endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
chip::CommandLineApp::TracingSetup mTracingSetup;
chip::Optional<std::vector<std::string>> mTraceTo;
};