Skip to content

Commit 5a859d0

Browse files
authored
Merge branch 'master' into feature/enable-message-cluster
2 parents 48f3575 + 849f882 commit 5a859d0

7 files changed

+303
-5
lines changed

src/app/icd/client/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ source_set("handler") {
4141
"CheckInHandler.h",
4242
"DefaultCheckInDelegate.cpp",
4343
"DefaultCheckInDelegate.h",
44+
"RefreshKeySender.cpp",
45+
"RefreshKeySender.h",
4446
]
4547
public_deps = [
4648
":manager",

src/app/icd/client/CheckInDelegate.h

+37
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
#pragma once
2020

2121
#include <app/icd/client/ICDClientInfo.h>
22+
#include <app/icd/client/ICDClientStorage.h>
2223

2324
namespace chip {
2425
namespace app {
2526

27+
class RefreshKeySender;
28+
2629
/// Callbacks for check in protocol
2730
/**
2831
* @brief The application implementing an ICD client should inherit the CheckInDelegate and implement the listed callbacks
@@ -39,6 +42,40 @@ class DLL_EXPORT CheckInDelegate
3942
node that sent the check-in message.
4043
*/
4144
virtual void OnCheckInComplete(const ICDClientInfo & clientInfo) = 0;
45+
46+
/**
47+
* @brief Callback used to let the application know that a key refresh is
48+
* needed to avoid counter rollover problems.
49+
*
50+
* The implementer of this function should create a new RefreshKeySender object. This object will be tied to the specific key
51+
* refresh process and will not be used by the caller after that particular key refresh process has ended, regardless of success
52+
* or failure.
53+
*
54+
* The caller guarantees that if a non-null RefreshKeySender pointer is returned, it will call OnKeyRefreshDone
55+
* at some point, and pass it the returned pointer.
56+
*
57+
* If the callee is unable to provide the RefreshKeySender object, that indicates key
58+
* refresh is not possible until the callee is able to provide the required resources.
59+
*
60+
* @param[in] clientInfo - ICDClientInfo object representing the state associated with the
61+
* node that sent the check-in message. The callee can use the clientInfo to determine the type of key
62+
* to generate.
63+
* @param[in] clientStorage - ICDClientStorage object stores the updated ICDClientInfo after re-registration into
64+
* persistent storage.
65+
* @return RefreshKeySender - pointer to RefreshKeySender object
66+
*/
67+
virtual RefreshKeySender * OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage) = 0;
68+
69+
/**
70+
* @brief Callback used to let the application know that the re-registration process is done. This callback will be called for
71+
* both success and failure cases. On failure, the callee should take appropriate corrective action based on the error.
72+
*
73+
* @param[in] refreshKeySender - pointer to the RefreshKeySender object that was used for the key refresh process. The caller
74+
* will NOT use this pointer any more.
75+
* @param[in] error - CHIP_NO_ERROR indicates successful re-registration using the new key
76+
* Other errors indicate the failure reason.
77+
*/
78+
virtual void OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error) = 0;
4279
};
4380

4481
} // namespace app

src/app/icd/client/CheckInHandler.cpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
*/
2424

2525
#include <app/InteractionModelTimeout.h>
26-
#include <app/icd/client/CheckInDelegate.h>
2726
#include <app/icd/client/CheckInHandler.h>
27+
#include <app/icd/client/RefreshKeySender.h>
2828

2929
#include <cinttypes>
3030

@@ -109,8 +109,20 @@ CHIP_ERROR CheckInHandler::OnMessageReceived(Messaging::ExchangeContext * ec, co
109109

110110
if (refreshKey)
111111
{
112-
// TODO: A new CASE session should be established to re-register the client using a new key. The registration will happen in
113-
// CASE session callback
112+
RefreshKeySender * refreshKeySender = mpCheckInDelegate->OnKeyRefreshNeeded(clientInfo, mpICDClientStorage);
113+
if (refreshKeySender == nullptr)
114+
{
115+
ChipLogError(ICD, "Key Refresh failed for node ID:" ChipLogFormatScopedNodeId,
116+
ChipLogValueScopedNodeId(clientInfo.peer_node));
117+
return CHIP_NO_ERROR;
118+
}
119+
err = refreshKeySender->EstablishSessionToPeer();
120+
if (CHIP_NO_ERROR != err)
121+
{
122+
ChipLogError(ICD, "CASE session establishment failed with error : %" CHIP_ERROR_FORMAT, err.Format());
123+
mpCheckInDelegate->OnKeyRefreshDone(refreshKeySender, err);
124+
return CHIP_NO_ERROR;
125+
}
114126
}
115127
else
116128
{

src/app/icd/client/DefaultCheckInDelegate.cpp

+38-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
* limitations under the License.
1616
*/
1717

18-
#include "CheckInHandler.h"
1918
#include <app/InteractionModelEngine.h>
2019
#include <app/icd/client/DefaultCheckInDelegate.h>
20+
#include <app/icd/client/RefreshKeySender.h>
2121
#include <crypto/CHIPCryptoPAL.h>
2222
#include <lib/support/CodeUtils.h>
2323
#include <lib/support/logging/CHIPLogging.h>
24-
#include <memory>
2524

2625
namespace chip {
2726
namespace app {
@@ -44,5 +43,42 @@ void DefaultCheckInDelegate::OnCheckInComplete(const ICDClientInfo & clientInfo)
4443
#endif
4544
}
4645

46+
RefreshKeySender * DefaultCheckInDelegate::OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage)
47+
{
48+
CHIP_ERROR err = CHIP_NO_ERROR;
49+
RefreshKeySender::RefreshKeyBuffer newKey;
50+
51+
err = Crypto::DRBG_get_bytes(newKey.Bytes(), newKey.Capacity());
52+
if (err != CHIP_NO_ERROR)
53+
{
54+
ChipLogError(ICD, "Generation of new key failed: %" CHIP_ERROR_FORMAT, err.Format());
55+
return nullptr;
56+
}
57+
58+
auto refreshKeySender = Platform::New<RefreshKeySender>(this, clientInfo, clientStorage, newKey);
59+
if (refreshKeySender == nullptr)
60+
{
61+
return nullptr;
62+
}
63+
return refreshKeySender;
64+
}
65+
66+
void DefaultCheckInDelegate::OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error)
67+
{
68+
if (error == CHIP_NO_ERROR)
69+
{
70+
ChipLogProgress(ICD, "Re-registration with new key completed successfully");
71+
}
72+
else
73+
{
74+
ChipLogError(ICD, "Re-registration with new key failed with error : %" CHIP_ERROR_FORMAT, error.Format());
75+
// The callee can take corrective action based on the error received.
76+
}
77+
if (refreshKeySender != nullptr)
78+
{
79+
Platform::Delete(refreshKeySender);
80+
refreshKeySender = nullptr;
81+
}
82+
}
4783
} // namespace app
4884
} // namespace chip

src/app/icd/client/DefaultCheckInDelegate.h

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class DefaultCheckInDelegate : public CheckInDelegate
3333
virtual ~DefaultCheckInDelegate() {}
3434
CHIP_ERROR Init(ICDClientStorage * storage);
3535
void OnCheckInComplete(const ICDClientInfo & clientInfo) override;
36+
RefreshKeySender * OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage) override;
37+
void OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error) override;
3638

3739
private:
3840
ICDClientStorage * mpStorage = nullptr;
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
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+
#include "RefreshKeySender.h"
19+
#include "CheckInDelegate.h"
20+
#include "controller/InvokeInteraction.h"
21+
#include <app-common/zap-generated/cluster-objects.h>
22+
#include <app/CommandPathParams.h>
23+
#include <app/InteractionModelEngine.h>
24+
#include <app/OperationalSessionSetup.h>
25+
#include <memory>
26+
27+
namespace chip {
28+
namespace app {
29+
30+
RefreshKeySender::RefreshKeySender(CheckInDelegate * checkInDelegate, const ICDClientInfo & icdClientInfo,
31+
ICDClientStorage * icdClientStorage, const RefreshKeyBuffer & refreshKeyBuffer) :
32+
mICDClientInfo(icdClientInfo),
33+
mpICDClientStorage(icdClientStorage), mpCheckInDelegate(checkInDelegate), mOnConnectedCallback(HandleDeviceConnected, this),
34+
mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
35+
36+
{
37+
mNewKey = refreshKeyBuffer;
38+
}
39+
40+
CHIP_ERROR RefreshKeySender::RegisterClientWithNewKey(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
41+
{
42+
auto onSuccess = [&](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) {
43+
ChipLogProgress(ICD, "RegisterClient command succeeded");
44+
CHIP_ERROR error;
45+
46+
// Update the ICDClientInfo with new key and start counter and store it to persistence
47+
mICDClientInfo.start_icd_counter = dataResponse.ICDCounter;
48+
mICDClientInfo.offset = 0;
49+
mpICDClientStorage->RemoveKey(mICDClientInfo);
50+
error = mpICDClientStorage->SetKey(mICDClientInfo, mNewKey.Span());
51+
if (error != CHIP_NO_ERROR)
52+
{
53+
ChipLogError(ICD, "Failed to set the new key after re-registration: %" CHIP_ERROR_FORMAT, error.Format());
54+
mpCheckInDelegate->OnKeyRefreshDone(this, error);
55+
return;
56+
}
57+
58+
error = mpICDClientStorage->StoreEntry(mICDClientInfo);
59+
if (error != CHIP_NO_ERROR)
60+
{
61+
ChipLogError(ICD, "Failed to store the new key after re-registration: %" CHIP_ERROR_FORMAT, error.Format());
62+
mpCheckInDelegate->OnKeyRefreshDone(this, error);
63+
return;
64+
}
65+
66+
mpCheckInDelegate->OnCheckInComplete(mICDClientInfo);
67+
mpCheckInDelegate->OnKeyRefreshDone(this, CHIP_NO_ERROR);
68+
};
69+
70+
auto onFailure = [&](CHIP_ERROR error) {
71+
ChipLogError(ICD, "RegisterClient command failed: %" CHIP_ERROR_FORMAT, error.Format());
72+
mpCheckInDelegate->OnKeyRefreshDone(this, error);
73+
};
74+
75+
EndpointId endpointId = 0;
76+
77+
Clusters::IcdManagement::Commands::RegisterClient::Type registerClientCommand;
78+
registerClientCommand.checkInNodeID = mICDClientInfo.peer_node.GetNodeId();
79+
registerClientCommand.monitoredSubject = mICDClientInfo.monitored_subject;
80+
registerClientCommand.key = mNewKey.Span();
81+
return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, endpointId, registerClientCommand, onSuccess, onFailure);
82+
}
83+
84+
CHIP_ERROR RefreshKeySender::EstablishSessionToPeer()
85+
{
86+
ChipLogProgress(ICD, "Trying to establish a CASE session for re-registering an ICD client");
87+
auto * caseSessionManager = InteractionModelEngine::GetInstance()->GetCASESessionManager();
88+
VerifyOrReturnError(caseSessionManager != nullptr, CHIP_ERROR_INVALID_CASE_PARAMETER);
89+
caseSessionManager->FindOrEstablishSession(mICDClientInfo.peer_node, &mOnConnectedCallback, &mOnConnectionFailureCallback);
90+
return CHIP_NO_ERROR;
91+
}
92+
93+
void RefreshKeySender::HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr,
94+
const SessionHandle & sessionHandle)
95+
{
96+
RefreshKeySender * const _this = static_cast<RefreshKeySender *>(context);
97+
VerifyOrDie(_this != nullptr);
98+
99+
CHIP_ERROR err = _this->RegisterClientWithNewKey(exchangeMgr, sessionHandle);
100+
if (CHIP_NO_ERROR != err)
101+
{
102+
ChipLogError(ICD, "Failed to send register client command");
103+
_this->mpCheckInDelegate->OnKeyRefreshDone(_this, err);
104+
}
105+
}
106+
107+
void RefreshKeySender::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
108+
{
109+
RefreshKeySender * const _this = static_cast<RefreshKeySender *>(context);
110+
VerifyOrDie(_this != nullptr);
111+
112+
ChipLogError(ICD, "Failed to establish CASE for re-registration with error '%" CHIP_ERROR_FORMAT "'", err.Format());
113+
_this->mpCheckInDelegate->OnKeyRefreshDone(_this, err);
114+
}
115+
} // namespace app
116+
} // namespace chip

src/app/icd/client/RefreshKeySender.h

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 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 "ICDClientInfo.h"
21+
#include "ICDClientStorage.h"
22+
#include <app-common/zap-generated/cluster-objects.h>
23+
#include <app/CommandSender.h>
24+
#include <app/OperationalSessionSetup.h>
25+
26+
#include <crypto/CHIPCryptoPAL.h>
27+
#include <lib/core/CHIPConfig.h>
28+
#include <lib/core/DataModelTypes.h>
29+
#include <lib/core/ScopedNodeId.h>
30+
#include <lib/support/CodeUtils.h>
31+
#include <stddef.h>
32+
33+
namespace chip {
34+
namespace app {
35+
36+
class CheckInDelegate;
37+
38+
/**
39+
* @brief RefreshKeySender contains all the data and methods needed for key refresh and re-registration of an ICD client.
40+
*/
41+
class RefreshKeySender
42+
{
43+
public:
44+
typedef Crypto::SensitiveDataBuffer<Crypto::kAES_CCM128_Key_Length> RefreshKeyBuffer;
45+
46+
RefreshKeySender(CheckInDelegate * checkInDelegate, const ICDClientInfo & icdClientInfo, ICDClientStorage * icdClientStorage,
47+
const RefreshKeyBuffer & refreshKeyBuffer);
48+
49+
/**
50+
* @brief Sets up a CASE session to the peer for re-registering a client with the peer when a key refresh is required to avoid
51+
* ICD counter rollover. Returns error if we did not even manage to kick off a CASE attempt.
52+
*/
53+
CHIP_ERROR EstablishSessionToPeer();
54+
55+
private:
56+
// CASE session callbacks
57+
/**
58+
* @brief Callback received on successfully establishing a CASE session in order to re-register the client with the peer node
59+
* using a new key to avoid counter rollover problems.
60+
*
61+
* @param[in] context - context of the client establishing the CASE session
62+
* @param[in] exchangeMgr - exchange manager to use for the re-registration
63+
* @param[in] sessionHandle - session handle to use for the re-registration
64+
*/
65+
static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr,
66+
const SessionHandle & sessionHandle);
67+
/**
68+
* @brief Callback received on failure to establish a CASE session in order to re-register the client with the peer node using a
69+
* new key to avoid counter rollover problems.
70+
*
71+
* @param[in] context - context of the client establishing the CASE session
72+
* @param[in] peerId - Scoped Node ID of the peer node
73+
* @param[in] err - failure reason
74+
*/
75+
static void HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err);
76+
77+
/**
78+
* @brief Used to send a re-registration command to the peer using a new key.
79+
*
80+
* @param[in] exchangeMgr - exchange manager to use for the re-registration
81+
* @param[in] sessionHandle - session handle to use for the re-registration
82+
*/
83+
CHIP_ERROR RegisterClientWithNewKey(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
84+
85+
ICDClientInfo mICDClientInfo;
86+
ICDClientStorage * mpICDClientStorage = nullptr;
87+
CheckInDelegate * mpCheckInDelegate = nullptr;
88+
RefreshKeyBuffer mNewKey;
89+
Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
90+
Callback::Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback;
91+
};
92+
} // namespace app
93+
} // namespace chip

0 commit comments

Comments
 (0)