blob: 51efb44b19376ecb2b6533eb585444f8c0778854 [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <app/data-model/Nullable.h>
#include <app/server/AppDelegate.h>
#include <app/server/CommissioningModeProvider.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPVendorIdentifiers.hpp>
#include <lib/core/ClusterEnums.h>
#include <lib/core/DataModelTypes.h>
#include <lib/dnssd/Advertiser.h>
#include <messaging/ExchangeDelegate.h>
#include <platform/CHIPDeviceConfig.h>
#include <protocols/secure_channel/RendezvousParameters.h>
#include <system/SystemClock.h>
namespace chip {
enum class CommissioningWindowAdvertisement
{
kAllSupported,
kDnssdOnly,
};
class Server;
class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
public SessionEstablishmentDelegate,
public app::CommissioningModeProvider,
public SessionDelegate
{
public:
CommissioningWindowManager() : mPASESession(*this) {}
CHIP_ERROR Init(Server * server)
{
if (server == nullptr)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
mServer = server;
return CHIP_NO_ERROR;
}
static constexpr System::Clock::Seconds32 MaxCommissioningTimeout()
{
#if CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING
// Specification section 2.3.1 - Extended Announcement Duration up to 48h
return System::Clock::Seconds32(60 * 60 * 48);
#else
// Specification section 5.4.2.3. Announcement Duration says 15 minutes.
return System::Clock::Seconds32(15 * 60);
#endif
}
System::Clock::Seconds32 MinCommissioningTimeout() const
{
// Specification section 5.4.2.3. Announcement Duration says 3 minutes.
return mMinCommissioningTimeoutOverride.ValueOr(System::Clock::Seconds32(3 * 60));
}
void SetAppDelegate(AppDelegate * delegate) { mAppDelegate = delegate; }
/**
* Open the pairing window using default configured parameters.
*/
CHIP_ERROR
OpenBasicCommissioningWindow(
System::Clock::Seconds32 commissioningTimeout = System::Clock::Seconds32(CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS),
CommissioningWindowAdvertisement advertisementMode = chip::CommissioningWindowAdvertisement::kAllSupported);
/**
* Open the pairing window using default configured parameters, triggered by
* the Administrator Commmissioning cluster implementation.
*/
CHIP_ERROR
OpenBasicCommissioningWindowForAdministratorCommissioningCluster(System::Clock::Seconds32 commissioningTimeout,
FabricIndex fabricIndex, VendorId vendorId);
CHIP_ERROR OpenEnhancedCommissioningWindow(System::Clock::Seconds32 commissioningTimeout, uint16_t discriminator,
Crypto::Spake2pVerifier & verifier, uint32_t iterations, chip::ByteSpan salt,
FabricIndex fabricIndex, VendorId vendorId);
void CloseCommissioningWindow();
app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum CommissioningWindowStatusForCluster() const;
bool IsCommissioningWindowOpen() const;
const app::DataModel::Nullable<VendorId> & GetOpenerVendorId() const { return mOpenerVendorId; }
const app::DataModel::Nullable<FabricIndex> & GetOpenerFabricIndex() const { return mOpenerFabricIndex; }
void OnFabricRemoved(FabricIndex removedIndex);
// CommissioningModeProvider implementation.
Dnssd::CommissioningMode GetCommissioningMode() const override;
//// UnsolicitedMessageHandler Implementation ////
CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader,
Messaging::ExchangeDelegate *& newDelegate) override;
void OnExchangeCreationFailed(Messaging::ExchangeDelegate * delegate) override;
//////////// SessionEstablishmentDelegate Implementation ///////////////
void OnSessionEstablishmentError(CHIP_ERROR error) override;
void OnSessionEstablishmentStarted() override;
void OnSessionEstablished(const SessionHandle & session) override;
void Shutdown();
void OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * event);
// For tests only, allow overriding the spec-defined minimum value of the
// commissioning window timeout.
void OverrideMinCommissioningTimeout(System::Clock::Seconds32 timeout) { mMinCommissioningTimeoutOverride.SetValue(timeout); }
private:
//////////// SessionDelegate Implementation ///////////////
void OnSessionReleased() override;
void SetBLE(bool ble) { mIsBLE = ble; }
CHIP_ERROR SetTemporaryDiscriminator(uint16_t discriminator);
CHIP_ERROR RestoreDiscriminator();
CHIP_ERROR StartAdvertisement();
CHIP_ERROR StopAdvertisement(bool aShuttingDown);
// Start a timer that will call HandleCommissioningWindowTimeout, and then
// start advertising and listen for PASE.
CHIP_ERROR OpenCommissioningWindow(System::Clock::Seconds32 commissioningTimeout);
// Start advertising and listening for PASE connections. Should only be
// called when a commissioning window timeout timer is running.
CHIP_ERROR AdvertiseAndListenForPASE();
// Call AdvertiseAndListenForPASE, only if max attempts have not been reached.
// Cleans up and calls app server delegate on failure.
// err gives the current error we're attemping to recover from
void HandleFailedAttempt(CHIP_ERROR err);
// Helper for Shutdown and Cleanup. Does not do anything with
// advertisements, because Shutdown and Cleanup want to handle those
// differently.
void ResetState();
void Cleanup();
/**
* Function that gets called when our commissioning window timeout timer
* fires.
*
* This timer is started when a commissioning window is initially opened via
* OpenEnhancedCommissioningWindow or OpenBasicCommissioningWindow.
*
* The timer is canceled when a PASE connection is established, because it
* should not affect the actual commissioning process, and after a PASE
* connection is established we will not re-enter commissioning mode without
* a new call to OpenEnhancedCommissioningWindow or
* OpenBasicCommissioningWindow.
*/
static void HandleCommissioningWindowTimeout(chip::System::Layer * aSystemLayer, void * aAppState);
/**
* Helper to immediately expire the fail-safe if it's currently armed.
*/
void ExpireFailSafeIfArmed();
/**
* Helpers to ensure the right attribute reporting happens when our state is
* updated.
*/
void UpdateWindowStatus(app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum aNewStatus);
void UpdateOpenerVendorId(app::DataModel::Nullable<VendorId> aNewOpenerVendorId);
void UpdateOpenerFabricIndex(app::DataModel::Nullable<FabricIndex> aNewOpenerFabricIndex);
AppDelegate * mAppDelegate = nullptr;
Server * mServer = nullptr;
app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum mWindowStatus =
app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen;
bool mIsBLE = true;
PASESession mPairingSession;
uint8_t mFailedCommissioningAttempts = 0;
bool mUseECM = false;
Crypto::Spake2pVerifier mECMPASEVerifier;
uint16_t mECMDiscriminator = 0;
// mListeningForPASE is true only when we are listening for
// PBKDFParamRequest messages or when we're in the middle of a PASE
// handshake.
bool mListeningForPASE = false;
// Boolean that tracks whether we have a live commissioning timeout timer.
bool mCommissioningTimeoutTimerArmed = false;
uint32_t mECMIterations = 0;
uint32_t mECMSaltLength = 0;
uint8_t mECMSalt[Crypto::kSpake2p_Max_PBKDF_Salt_Length];
// For tests only, so that we can test the commissioning window timeout
// without having to wait 3 minutes.
Optional<System::Clock::Seconds32> mMinCommissioningTimeoutOverride;
// The PASE session we are using, so we can handle CloseSession properly.
SessionHolderWithDelegate mPASESession;
// Information about who opened the commissioning window. These will only
// be non-null if the window was opened via the operational credentials
// cluster and the fabric index may be null even then if the fabric has been
// removed.
app::DataModel::Nullable<VendorId> mOpenerVendorId;
app::DataModel::Nullable<FabricIndex> mOpenerFabricIndex;
};
} // namespace chip