Coverage Report

Created: 2025-08-28 06:31

/src/connectedhomeip/src/app/server/CommissioningWindowManager.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2021-2022 Project CHIP Authors
4
 *
5
 *    Licensed under the Apache License, Version 2.0 (the "License");
6
 *    you may not use this file except in compliance with the License.
7
 *    You may obtain a copy of the License at
8
 *
9
 *        http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *    Unless required by applicable law or agreed to in writing, software
12
 *    distributed under the License is distributed on an "AS IS" BASIS,
13
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *    See the License for the specific language governing permissions and
15
 *    limitations under the License.
16
 */
17
18
#pragma once
19
20
#include <app/data-model/Nullable.h>
21
#include <app/server/AppDelegate.h>
22
#include <app/server/CommissioningModeProvider.h>
23
#include <crypto/CHIPCryptoPAL.h>
24
#include <lib/core/CHIPVendorIdentifiers.hpp>
25
#include <lib/core/ClusterEnums.h>
26
#include <lib/core/DataModelTypes.h>
27
#include <lib/dnssd/Advertiser.h>
28
#include <messaging/ExchangeDelegate.h>
29
#include <platform/CHIPDeviceConfig.h>
30
#include <protocols/secure_channel/RendezvousParameters.h>
31
#include <system/SystemClock.h>
32
33
namespace chip {
34
35
enum class CommissioningWindowAdvertisement
36
{
37
    kAllSupported,
38
    kDnssdOnly,
39
};
40
41
class Server;
42
43
class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
44
                                   public SessionEstablishmentDelegate,
45
                                   public app::CommissioningModeProvider,
46
                                   public SessionDelegate
47
{
48
public:
49
2
    CommissioningWindowManager() : mPASESession(*this) {}
50
51
    CHIP_ERROR Init(Server * server)
52
1
    {
53
1
        if (server == nullptr)
54
0
        {
55
0
            return CHIP_ERROR_INVALID_ARGUMENT;
56
0
        }
57
1
        mServer = server;
58
1
        return CHIP_NO_ERROR;
59
1
    }
60
61
    System::Clock::Seconds32 MaxCommissioningTimeout() const;
62
63
    System::Clock::Seconds32 MinCommissioningTimeout() const
64
1
    {
65
        // Specification section 5.4.2.3. Announcement Duration says 3 minutes.
66
1
        return mMinCommissioningTimeoutOverride.ValueOr(System::Clock::Seconds32(3 * 60));
67
1
    }
68
69
1
    void SetAppDelegate(AppDelegate * delegate) { mAppDelegate = delegate; }
70
71
    /**
72
     * Open the pairing window using default configured parameters.
73
     */
74
    CHIP_ERROR
75
    OpenBasicCommissioningWindow(
76
        System::Clock::Seconds32 commissioningTimeout      = System::Clock::Seconds32(CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS),
77
        CommissioningWindowAdvertisement advertisementMode = chip::CommissioningWindowAdvertisement::kAllSupported);
78
79
    /**
80
     * Open the pairing window using default configured parameters, triggered by
81
     * the Administrator Commmissioning cluster implementation.
82
     */
83
    CHIP_ERROR
84
    OpenBasicCommissioningWindowForAdministratorCommissioningCluster(System::Clock::Seconds32 commissioningTimeout,
85
                                                                     FabricIndex fabricIndex, VendorId vendorId);
86
87
    CHIP_ERROR OpenEnhancedCommissioningWindow(System::Clock::Seconds32 commissioningTimeout, uint16_t discriminator,
88
                                               Crypto::Spake2pVerifier & verifier, uint32_t iterations, chip::ByteSpan salt,
89
                                               FabricIndex fabricIndex, VendorId vendorId);
90
91
#if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
92
    CHIP_ERROR OpenJointCommissioningWindow(System::Clock::Seconds32 commissioningTimeout, uint16_t discriminator,
93
                                            Crypto::Spake2pVerifier & verifier, uint32_t iterations, ByteSpan salt,
94
                                            FabricIndex fabricIndex, VendorId vendorId);
95
#endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
96
97
    void CloseCommissioningWindow();
98
99
    app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum CommissioningWindowStatusForCluster() const;
100
101
    bool IsCommissioningWindowOpen() const;
102
103
0
    const app::DataModel::Nullable<VendorId> & GetOpenerVendorId() const { return mOpenerVendorId; }
104
105
0
    const app::DataModel::Nullable<FabricIndex> & GetOpenerFabricIndex() const { return mOpenerFabricIndex; }
106
107
    void OnFabricRemoved(FabricIndex removedIndex);
108
109
    // CommissioningModeProvider implementation.
110
    Dnssd::CommissioningMode GetCommissioningMode() const override;
111
112
    //// UnsolicitedMessageHandler Implementation ////
113
    CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader,
114
                                            Messaging::ExchangeDelegate *& newDelegate) override;
115
    void OnExchangeCreationFailed(Messaging::ExchangeDelegate * delegate) override;
116
117
    //////////// SessionEstablishmentDelegate Implementation ///////////////
118
    void OnSessionEstablishmentError(CHIP_ERROR error) override;
119
    void OnSessionEstablishmentStarted() override;
120
    void OnSessionEstablished(const SessionHandle & session) override;
121
122
    void Shutdown();
123
124
    void OnPlatformEvent(const DeviceLayer::ChipDeviceEvent * event);
125
126
    // For tests only, allow overriding the spec-defined minimum value of the
127
    // commissioning window timeout.
128
0
    void OverrideMinCommissioningTimeout(System::Clock::Seconds32 timeout) { mMinCommissioningTimeoutOverride.SetValue(timeout); }
129
130
private:
131
    //////////// SessionDelegate Implementation ///////////////
132
    void OnSessionReleased() override;
133
134
1
    void SetBLE(bool ble) { mIsBLE = ble; }
135
136
    CHIP_ERROR SetTemporaryDiscriminator(uint16_t discriminator);
137
138
    CHIP_ERROR RestoreDiscriminator();
139
140
    CHIP_ERROR StartAdvertisement();
141
142
    CHIP_ERROR StopAdvertisement(bool aShuttingDown);
143
144
    // Start a timer that will call HandleCommissioningWindowTimeout, and then
145
    // start advertising and listen for PASE.
146
    CHIP_ERROR OpenCommissioningWindow(System::Clock::Seconds32 commissioningTimeout);
147
148
    // Start advertising and listening for PASE connections.  Should only be
149
    // called when a commissioning window timeout timer is running.
150
    CHIP_ERROR AdvertiseAndListenForPASE();
151
152
    // Call AdvertiseAndListenForPASE, only if max attempts have not been reached.
153
    // Cleans up and calls app server delegate on failure.
154
    // err gives the current error we're attemping to recover from
155
    void HandleFailedAttempt(CHIP_ERROR err);
156
157
    // Helper for Shutdown and Cleanup.  Does not do anything with
158
    // advertisements, because Shutdown and Cleanup want to handle those
159
    // differently.
160
    void ResetState();
161
162
    void Cleanup();
163
164
    /**
165
     * Function that gets called when our commissioning window timeout timer
166
     * fires.
167
     *
168
     * This timer is started when a commissioning window is initially opened via
169
     * OpenEnhancedCommissioningWindow or OpenBasicCommissioningWindow.
170
     *
171
     * The timer is canceled when a PASE connection is established, because it
172
     * should not affect the actual commissioning process, and after a PASE
173
     * connection is established we will not re-enter commissioning mode without
174
     * a new call to OpenEnhancedCommissioningWindow or
175
     * OpenBasicCommissioningWindow.
176
     */
177
    static void HandleCommissioningWindowTimeout(chip::System::Layer * aSystemLayer, void * aAppState);
178
179
    /**
180
     * Helper to immediately expire the fail-safe if it's currently armed.
181
     */
182
    void ExpireFailSafeIfArmed();
183
184
    /**
185
     * Helpers to ensure the right attribute reporting happens when our state is
186
     * updated.
187
     */
188
    void UpdateWindowStatus(app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum aNewStatus);
189
    void UpdateOpenerVendorId(app::DataModel::Nullable<VendorId> aNewOpenerVendorId);
190
    void UpdateOpenerFabricIndex(app::DataModel::Nullable<FabricIndex> aNewOpenerFabricIndex);
191
192
    AppDelegate * mAppDelegate = nullptr;
193
    Server * mServer           = nullptr;
194
195
    app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum mWindowStatus =
196
        app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen;
197
198
    bool mIsBLE = true;
199
200
#if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
201
    // Boolean that tracks whether we are currently in a Joint Commissioning Mode.
202
    bool mJCM = false;
203
#endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
204
205
    PASESession mPairingSession;
206
207
    uint8_t mFailedCommissioningAttempts = 0;
208
209
    bool mUseECM = false;
210
    Crypto::Spake2pVerifier mECMPASEVerifier;
211
    uint16_t mECMDiscriminator = 0;
212
    // mListeningForPASE is true only when we are listening for
213
    // PBKDFParamRequest messages or when we're in the middle of a PASE
214
    // handshake.
215
    bool mListeningForPASE = false;
216
    // Boolean that tracks whether we have a live commissioning timeout timer.
217
    bool mCommissioningTimeoutTimerArmed = false;
218
    uint32_t mECMIterations              = 0;
219
    uint32_t mECMSaltLength              = 0;
220
    uint8_t mECMSalt[Crypto::kSpake2p_Max_PBKDF_Salt_Length];
221
222
    // For tests only, so that we can test the commissioning window timeout
223
    // without having to wait 3 minutes.
224
    Optional<System::Clock::Seconds32> mMinCommissioningTimeoutOverride;
225
226
    // The PASE session we are using, so we can handle CloseSession properly.
227
    SessionHolderWithDelegate mPASESession;
228
229
    // Information about who opened the commissioning window.  These will only
230
    // be non-null if the window was opened via the operational credentials
231
    // cluster and the fabric index may be null even then if the fabric has been
232
    // removed.
233
    app::DataModel::Nullable<VendorId> mOpenerVendorId;
234
    app::DataModel::Nullable<FabricIndex> mOpenerFabricIndex;
235
};
236
237
} // namespace chip