/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 |