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