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