Coverage Report

Created: 2025-09-02 06:46

/src/connectedhomeip/src/protocols/secure_channel/CheckinMessage.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *    Copyright (c) 2020 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
/**
19
 *    @file
20
 *      This file implements the Matter Checkin protocol.
21
 */
22
23
#include <lib/core/CHIPCore.h>
24
#include <lib/core/CHIPEncoding.h>
25
#include <protocols/secure_channel/CheckinMessage.h>
26
#include <protocols/secure_channel/Constants.h>
27
28
using namespace chip::Crypto;
29
30
namespace chip {
31
namespace Protocols {
32
namespace SecureChannel {
33
34
CHIP_ERROR CheckinMessage::GenerateCheckinMessagePayload(const Crypto::Aes128KeyHandle & aes128KeyHandle,
35
                                                         const Crypto::Hmac128KeyHandle & hmacKeyHandle,
36
                                                         const CounterType & counter, const ByteSpan & appData,
37
                                                         MutableByteSpan & output)
38
0
{
39
0
    VerifyOrReturnError(output.size() >= (appData.size() + kMinPayloadSize), CHIP_ERROR_BUFFER_TOO_SMALL);
40
0
    size_t cursorIndex = 0;
41
42
    // Generate Nonce from Key and counter value
43
0
    {
44
0
        MutableByteSpan nonce = output.SubSpan(0, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);
45
0
        cursorIndex += nonce.size();
46
47
0
        Encoding::LittleEndian::BufferWriter writer(nonce);
48
0
        ReturnErrorOnFailure(GenerateCheckInMessageNonce(hmacKeyHandle, counter, writer));
49
0
    }
50
51
    // Encrypt Counter and Application Data
52
0
    {
53
0
        MutableByteSpan payloadByteSpan = output.SubSpan(cursorIndex, sizeof(CounterType) + appData.size());
54
0
        cursorIndex += payloadByteSpan.size();
55
56
0
        Encoding::LittleEndian::BufferWriter payloadWriter(payloadByteSpan);
57
58
0
        payloadWriter.EndianPut(counter, sizeof(counter));
59
0
        payloadWriter.Put(appData.data(), appData.size());
60
0
        VerifyOrReturnError(payloadWriter.Fit(), CHIP_ERROR_INTERNAL);
61
62
0
        MutableByteSpan mic = output.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
63
0
        cursorIndex += mic.size();
64
65
        // Validate that the cursorIndex is within the available output space
66
0
        VerifyOrReturnError(cursorIndex <= output.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
67
        // Validate that the cursorIndex matchs the message length
68
0
        VerifyOrReturnError(cursorIndex == appData.size() + kMinPayloadSize, CHIP_ERROR_INTERNAL);
69
70
0
        ReturnErrorOnFailure(Crypto::AES_CCM_encrypt(payloadByteSpan.data(), payloadByteSpan.size(), nullptr, 0, aes128KeyHandle,
71
0
                                                     output.data(), CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, payloadByteSpan.data(),
72
0
                                                     mic.data(), mic.size()));
73
0
    }
74
75
0
    output.reduce_size(appData.size() + kMinPayloadSize);
76
0
    return CHIP_NO_ERROR;
77
0
}
78
79
CHIP_ERROR CheckinMessage::ParseCheckinMessagePayload(const Crypto::Aes128KeyHandle & aes128KeyHandle,
80
                                                      const Crypto::Hmac128KeyHandle & hmacKeyHandle, const ByteSpan & payload,
81
                                                      CounterType & counter, MutableByteSpan & appData)
82
0
{
83
0
    size_t appDataSize = GetAppDataSize(payload);
84
85
0
    VerifyOrReturnError(payload.size() >= kMinPayloadSize, CHIP_ERROR_INVALID_MESSAGE_LENGTH);
86
    // To prevent workbuffer usage, appData size needs to be large enough to hold both the appData and the counter
87
0
    VerifyOrReturnError(appData.size() >= sizeof(CounterType) + appDataSize, CHIP_ERROR_BUFFER_TOO_SMALL);
88
89
    // Decrypt received data
90
0
    {
91
0
        size_t cursorIndex = 0;
92
93
0
        ByteSpan nonce = payload.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);
94
0
        cursorIndex += nonce.size();
95
96
0
        ByteSpan encryptedData = payload.SubSpan(cursorIndex, sizeof(CounterType) + appDataSize);
97
0
        cursorIndex += encryptedData.size();
98
99
0
        ByteSpan mic = payload.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
100
0
        cursorIndex += mic.size();
101
102
        // Return Invalid message length since the payload isn't the right size
103
0
        VerifyOrReturnError(cursorIndex == payload.size(), CHIP_ERROR_INVALID_MESSAGE_LENGTH);
104
105
0
        ReturnErrorOnFailure(Crypto::AES_CCM_decrypt(encryptedData.data(), encryptedData.size(), nullptr, 0, mic.data(), mic.size(),
106
0
                                                     aes128KeyHandle, nonce.data(), nonce.size(), appData.data()));
107
0
    }
108
109
    // Read decrypted counter and application data
110
0
    static_assert(sizeof(CounterType) == sizeof(uint32_t), "Expect counter to be 32 bits for correct decoding");
111
0
    CounterType tempCounter = Encoding::LittleEndian::Get32(appData.data());
112
113
    // Validate that the received nonce is correct
114
0
    {
115
0
        uint8_t calculatedNonceBuffer[CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES] = { 0 };
116
0
        Encoding::LittleEndian::BufferWriter writer(calculatedNonceBuffer, sizeof(calculatedNonceBuffer));
117
118
0
        ReturnErrorOnFailure(GenerateCheckInMessageNonce(hmacKeyHandle, tempCounter, writer));
119
120
        // Validate received nonce is the same as the calculated
121
0
        ByteSpan nonce = payload.SubSpan(0, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);
122
0
        VerifyOrReturnError(memcmp(nonce.data(), calculatedNonceBuffer, sizeof(calculatedNonceBuffer)) == 0, CHIP_ERROR_INTERNAL);
123
0
    }
124
125
    // We have successfully decrypted and validated Check-In message
126
    // Set output values
127
128
0
    counter = tempCounter;
129
    // Shift to remove the counter from the appData
130
0
    memmove(appData.data(), sizeof(CounterType) + appData.data(), appDataSize);
131
0
    appData.reduce_size(appDataSize);
132
133
0
    return CHIP_NO_ERROR;
134
0
}
135
136
CHIP_ERROR CheckinMessage::GenerateCheckInMessageNonce(const Crypto::Hmac128KeyHandle & hmacKeyHandle, CounterType counter,
137
                                                       Encoding::LittleEndian::BufferWriter & writer)
138
0
{
139
0
    VerifyOrReturnError(writer.Available() >= CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL);
140
141
0
    uint8_t hashWorkBuffer[CHIP_CRYPTO_HASH_LEN_BYTES] = { 0 };
142
0
    uint8_t counterBuffer[sizeof(CounterType)];
143
144
    // validate that Check-In counter is a uint32_t
145
0
    static_assert(sizeof(CounterType) == sizeof(uint32_t), "Expect counter to be 32 bits for correct encoding");
146
0
    Encoding::LittleEndian::Put32(counterBuffer, counter);
147
148
0
    chip::Crypto::HMAC_sha shaHandler;
149
0
    ReturnErrorOnFailure(
150
0
        shaHandler.HMAC_SHA256(hmacKeyHandle, counterBuffer, sizeof(CounterType), hashWorkBuffer, CHIP_CRYPTO_HASH_LEN_BYTES));
151
152
0
    writer.Put(hashWorkBuffer, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);
153
0
    VerifyOrReturnError(writer.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL);
154
155
0
    return CHIP_NO_ERROR;
156
0
}
157
158
size_t CheckinMessage::GetAppDataSize(const ByteSpan & payload)
159
0
{
160
0
    return (payload.size() <= kMinPayloadSize) ? 0 : payload.size() - kMinPayloadSize;
161
0
}
162
163
} // namespace SecureChannel
164
} // namespace Protocols
165
} // namespace chip