Coverage Report

Created: 2026-09-24 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/protocols/secure_channel/MessageCounterManager.cpp
Line
Count
Source
1
/*
2
 *
3
 *    Copyright (c) 2021 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
/**
19
 *    @file
20
 *      This file implements the CHIP message counter messages in secure channel protocol.
21
 *
22
 */
23
24
#include <protocols/secure_channel/MessageCounterManager.h>
25
26
#include <lib/core/CHIPCore.h>
27
#include <lib/core/CHIPEncoding.h>
28
#include <lib/core/CHIPKeyIds.h>
29
#include <lib/support/BufferWriter.h>
30
#include <lib/support/CodeUtils.h>
31
#include <lib/support/logging/CHIPLogging.h>
32
#include <messaging/ExchangeContext.h>
33
#include <messaging/ExchangeMgr.h>
34
#include <messaging/Flags.h>
35
#include <protocols/Protocols.h>
36
#include <protocols/secure_channel/Constants.h>
37
38
namespace chip {
39
namespace secure_channel {
40
41
constexpr System::Clock::Timeout MessageCounterManager::kSyncTimeout;
42
43
CHIP_ERROR MessageCounterManager::Init(Messaging::ExchangeManager * exchangeMgr)
44
2.24k
{
45
2.24k
    VerifyOrReturnError(exchangeMgr != nullptr, CHIP_ERROR_INCORRECT_STATE);
46
2.24k
    mExchangeMgr = exchangeMgr;
47
48
2.24k
    ReturnErrorOnFailure(
49
2.24k
        mExchangeMgr->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, this));
50
51
2.24k
    return CHIP_NO_ERROR;
52
2.24k
}
53
54
void MessageCounterManager::Shutdown()
55
2.24k
{
56
2.24k
    if (mExchangeMgr != nullptr)
57
2.24k
    {
58
2.24k
        TEMPORARY_RETURN_IGNORED mExchangeMgr->UnregisterUnsolicitedMessageHandlerForType(
59
2.24k
            Protocols::SecureChannel::MsgType::MsgCounterSyncReq);
60
2.24k
        mExchangeMgr->CloseAllContextsForDelegate(this);
61
2.24k
        mExchangeMgr = nullptr;
62
2.24k
    }
63
2.24k
}
64
65
CHIP_ERROR MessageCounterManager::StartSync(const SessionHandle & session, Transport::SecureSession * state)
66
0
{
67
    // Initiate message counter synchronization if no message counter synchronization is in progress.
68
0
    Transport::PeerMessageCounter & counter = state->GetSessionMessageCounter().GetPeerMessageCounter();
69
0
    if (!counter.IsSynchronizing() && !counter.IsSynchronized())
70
0
    {
71
0
        ReturnErrorOnFailure(SendMsgCounterSyncReq(session, state));
72
0
    }
73
74
0
    return CHIP_NO_ERROR;
75
0
}
76
77
CHIP_ERROR MessageCounterManager::QueueReceivedMessageAndStartSync(const PacketHeader & packetHeader, const SessionHandle & session,
78
                                                                   Transport::SecureSession * state,
79
                                                                   const Transport::PeerAddress & peerAddress,
80
                                                                   System::PacketBufferHandle && msgBuf)
81
0
{
82
    // Queue the message to be reprocessed when sync completes.
83
0
    ReturnErrorOnFailure(AddToReceiveTable(packetHeader, peerAddress, std::move(msgBuf)));
84
0
    ReturnErrorOnFailure(StartSync(session, state));
85
86
    // After the message that triggers message counter synchronization is stored, and a message counter
87
    // synchronization exchange is initiated, we need to return immediately and re-process the original message
88
    // when the synchronization is completed.
89
90
0
    return CHIP_NO_ERROR;
91
0
}
92
93
CHIP_ERROR MessageCounterManager::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
94
0
{
95
    // MessageCounterManager do not use an extra context to handle messages
96
0
    newDelegate = this;
97
0
    return CHIP_NO_ERROR;
98
0
}
99
100
CHIP_ERROR MessageCounterManager::OnMessageReceived(Messaging::ExchangeContext * exchangeContext,
101
                                                    const PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf)
102
0
{
103
0
    if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq))
104
0
    {
105
0
        return HandleMsgCounterSyncReq(exchangeContext, std::move(msgBuf));
106
0
    }
107
0
    if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
108
0
    {
109
0
        return HandleMsgCounterSyncResp(exchangeContext, std::move(msgBuf));
110
0
    }
111
0
    return CHIP_NO_ERROR;
112
0
}
113
114
void MessageCounterManager::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext)
115
0
{
116
0
    if (exchangeContext->HasSessionHandle())
117
0
    {
118
0
        exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed();
119
0
    }
120
0
    else
121
0
    {
122
0
        ChipLogError(SecureChannel, "MCSP Timeout! On a already released session.");
123
0
    }
124
0
}
125
126
CHIP_ERROR MessageCounterManager::AddToReceiveTable(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress,
127
                                                    System::PacketBufferHandle && msgBuf)
128
0
{
129
0
    ReturnErrorOnFailure(packetHeader.EncodeBeforeData(msgBuf));
130
131
0
    for (ReceiveTableEntry & entry : mReceiveTable)
132
0
    {
133
0
        if (entry.msgBuf.IsNull())
134
0
        {
135
0
            entry.peerAddress = peerAddress;
136
0
            entry.msgBuf      = std::move(msgBuf);
137
138
0
            return CHIP_NO_ERROR;
139
0
        }
140
0
    }
141
142
0
    ChipLogError(SecureChannel, "MCSP ReceiveTable Already Full");
143
0
    return CHIP_ERROR_NO_MEMORY;
144
0
}
145
146
/**
147
 *  Reprocess all pending messages that were encrypted with application
148
 *  group key and were addressed to the specified node id.
149
 *
150
 *  @param[in] peerNodeId    Node ID of the destination node.
151
 *
152
 */
153
void MessageCounterManager::ProcessPendingMessages(NodeId peerNodeId)
154
0
{
155
0
    auto * sessionManager = mExchangeMgr->GetSessionManager();
156
157
    // Find all receive entries matching peerNodeId.  Note that everything in
158
    // this table was using an application group key; that's why it was added.
159
0
    for (ReceiveTableEntry & entry : mReceiveTable)
160
0
    {
161
0
        if (!entry.msgBuf.IsNull())
162
0
        {
163
0
            PacketHeader packetHeader;
164
0
            uint16_t headerSize = 0;
165
166
0
            if (packetHeader.Decode((entry.msgBuf)->Start(), (entry.msgBuf)->DataLength(), &headerSize) != CHIP_NO_ERROR)
167
0
            {
168
0
                ChipLogError(SecureChannel, "MessageCounterManager::ProcessPendingMessages: Failed to decode PacketHeader");
169
0
                entry.msgBuf = nullptr;
170
0
                continue;
171
0
            }
172
173
0
            if (packetHeader.GetSourceNodeId().HasValue() && packetHeader.GetSourceNodeId().Value() == peerNodeId)
174
0
            {
175
                // Reprocess message.
176
0
                sessionManager->OnMessageReceived(entry.peerAddress, std::move(entry.msgBuf));
177
178
                // Explicitly free any buffer owned by this handle.
179
0
                entry.msgBuf = nullptr;
180
0
            }
181
0
        }
182
0
    }
183
0
}
184
185
CHIP_ERROR MessageCounterManager::SendMsgCounterSyncReq(const SessionHandle & session, Transport::SecureSession * state)
186
0
{
187
0
    CHIP_ERROR err = CHIP_NO_ERROR;
188
189
0
    Messaging::ExchangeContext * exchangeContext = nullptr;
190
0
    System::PacketBufferHandle msgBuf;
191
0
    Messaging::SendFlags sendFlags;
192
193
0
    exchangeContext = mExchangeMgr->NewContext(session, this);
194
0
    VerifyOrExit(exchangeContext != nullptr, err = CHIP_ERROR_NO_MEMORY);
195
196
0
    msgBuf = MessagePacketBuffer::New(kChallengeSize);
197
0
    VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
198
199
    // Generate a 64-bit random number to uniquely identify the request.
200
0
    SuccessOrExit(err = Crypto::DRBG_get_bytes(msgBuf->Start(), kChallengeSize));
201
202
0
    msgBuf->SetDataLength(kChallengeSize);
203
204
    // Store generated Challenge value to message counter context to resolve synchronization response.
205
0
    state->GetSessionMessageCounter().GetPeerMessageCounter().SyncStarting(FixedByteSpan<kChallengeSize>(msgBuf->Start()));
206
207
0
    sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck).Set(Messaging::SendMessageFlags::kExpectResponse);
208
209
    // Arm a timer to enforce that a MsgCounterSyncRsp is received before kSyncTimeout.
210
0
    exchangeContext->SetResponseTimeout(kSyncTimeout);
211
212
    // Send the message counter synchronization request in a Secure Channel Protocol::MsgCounterSyncReq message.
213
0
    SuccessOrExit(
214
0
        err = exchangeContext->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, std::move(msgBuf), sendFlags));
215
216
0
exit:
217
0
    if (err != CHIP_NO_ERROR)
218
0
    {
219
0
        if (exchangeContext != nullptr)
220
0
        {
221
0
            exchangeContext->Close();
222
0
        }
223
0
        state->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed();
224
0
        ChipLogError(SecureChannel, "Failed to send message counter synchronization request with error:%" CHIP_ERROR_FORMAT,
225
0
                     err.Format());
226
0
    }
227
228
0
    return err;
229
0
}
230
231
CHIP_ERROR MessageCounterManager::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
232
                                                         FixedByteSpan<kChallengeSize> challenge)
233
0
{
234
0
    System::PacketBufferHandle msgBuf;
235
0
    VerifyOrDie(exchangeContext->HasSessionHandle());
236
237
0
    VerifyOrReturnError(exchangeContext->GetSessionHandle()->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT);
238
239
    // NOTE: not currently implemented. When implementing, the following should be done:
240
    //    - allocate a new buffer: MessagePacketBuffer::New
241
    //    - setup payload and place the local message counter + challange in it
242
    //    - exchangeContext->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp, ...)
243
    //
244
    // You can view the history of this file for a partial implementation that got
245
    // removed due to it using non-group sessions.
246
247
0
    return CHIP_ERROR_NOT_IMPLEMENTED;
248
0
}
249
250
CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext,
251
                                                          System::PacketBufferHandle && msgBuf)
252
0
{
253
0
    CHIP_ERROR err = CHIP_NO_ERROR;
254
255
0
    uint8_t * req = msgBuf->Start();
256
0
    size_t reqlen = msgBuf->DataLength();
257
258
0
    ChipLogDetail(SecureChannel, "Received MsgCounterSyncReq request");
259
260
0
    VerifyOrExit(req != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
261
0
    VerifyOrExit(reqlen == kChallengeSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
262
263
    // Respond with MsgCounterSyncResp
264
0
    err = SendMsgCounterSyncResp(exchangeContext, FixedByteSpan<kChallengeSize>(req));
265
266
0
exit:
267
0
    if (err != CHIP_NO_ERROR)
268
0
    {
269
0
        ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncReq message with error:%" CHIP_ERROR_FORMAT, err.Format());
270
0
    }
271
272
0
    return err;
273
0
}
274
275
CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
276
                                                           System::PacketBufferHandle && msgBuf)
277
0
{
278
0
    CHIP_ERROR err = CHIP_NO_ERROR;
279
280
0
    uint32_t syncCounter = 0;
281
282
0
    const uint8_t * resp = msgBuf->Start();
283
0
    size_t resplen       = msgBuf->DataLength();
284
285
0
    ChipLogDetail(SecureChannel, "Received MsgCounterSyncResp response");
286
287
0
    VerifyOrDie(exchangeContext->HasSessionHandle());
288
289
0
    VerifyOrExit(msgBuf->DataLength() == kSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
290
291
0
    VerifyOrExit(resp != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
292
0
    VerifyOrExit(resplen == kSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
293
294
0
    syncCounter = chip::Encoding::LittleEndian::Read32(resp);
295
0
    VerifyOrExit(syncCounter != 0, err = CHIP_ERROR_READ_FAILED);
296
297
    // Verify that the response field matches the expected Challenge field for the exchange.
298
0
    err =
299
0
        exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().VerifyChallenge(
300
0
            syncCounter, FixedByteSpan<kChallengeSize>(resp));
301
0
    SuccessOrExit(err);
302
303
    // Process all queued incoming messages after message counter synchronization is completed.
304
0
    ProcessPendingMessages(exchangeContext->GetSessionHandle()->AsSecureSession()->GetPeerNodeId());
305
306
0
exit:
307
0
    if (err != CHIP_NO_ERROR)
308
0
    {
309
0
        ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncResp message with error:%" CHIP_ERROR_FORMAT, err.Format());
310
0
    }
311
312
0
    return err;
313
0
}
314
315
} // namespace secure_channel
316
} // namespace chip