Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file implements the CHIP Secure Session object.
22 : *
23 : */
24 :
25 : #include <crypto/CHIPCryptoPAL.h>
26 : #include <crypto/SessionKeystore.h>
27 : #include <lib/core/CHIPEncoding.h>
28 : #include <lib/support/BufferWriter.h>
29 : #include <lib/support/CodeUtils.h>
30 : #include <transport/CryptoContext.h>
31 : #include <transport/raw/MessageHeader.h>
32 :
33 : #include <lib/support/BytesToHex.h>
34 :
35 : #include <string.h>
36 :
37 : namespace chip {
38 :
39 : namespace {
40 :
41 : constexpr size_t kMaxAADLen = 128;
42 :
43 : /* Session Establish Key Info */
44 : constexpr uint8_t SEKeysInfo[] = { 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73 };
45 :
46 : /* Session Resumption Key Info */
47 : constexpr uint8_t RSEKeysInfo[] = { 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75,
48 : 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73 };
49 :
50 : } // namespace
51 :
52 : using namespace Crypto;
53 :
54 135096 : CryptoContext::CryptoContext() : mKeyAvailable(false) {}
55 :
56 135111 : CryptoContext::~CryptoContext()
57 : {
58 135111 : if (mKeystore)
59 : {
60 1317 : mKeystore->DestroyKey(mEncryptionKey);
61 1317 : mKeystore->DestroyKey(mDecryptionKey);
62 : }
63 :
64 135111 : mKeystore = nullptr;
65 135111 : mKeyContext = nullptr;
66 135111 : }
67 :
68 1307 : CHIP_ERROR CryptoContext::InitFromSecret(SessionKeystore & keystore, const ByteSpan & secret, const ByteSpan & salt,
69 : SessionInfoType infoType, SessionRole role)
70 : {
71 1307 : VerifyOrReturnError(mKeyAvailable == false, CHIP_ERROR_INCORRECT_STATE);
72 :
73 1307 : ByteSpan info = (infoType == SessionInfoType::kSessionResumption) ? ByteSpan(RSEKeysInfo) : ByteSpan(SEKeysInfo);
74 :
75 : // If the secure session is created by session initiator, use the I2R key to encrypt
76 : // messages being transmitted. Otherwise, use the R2I key.
77 1307 : auto & i2rKey = (role == SessionRole::kInitiator) ? mEncryptionKey : mDecryptionKey;
78 1307 : auto & r2iKey = (role == SessionRole::kInitiator) ? mDecryptionKey : mEncryptionKey;
79 :
80 : #if CHIP_CONFIG_SECURITY_TEST_MODE
81 : ReturnErrorOnFailure(InitTestMode(keystore, i2rKey, r2iKey));
82 : #else
83 1307 : ReturnErrorOnFailure(keystore.DeriveSessionKeys(secret, salt, info, i2rKey, r2iKey, mAttestationChallenge));
84 : #endif
85 :
86 1307 : mKeyAvailable = true;
87 1307 : mSessionRole = role;
88 1307 : mKeystore = &keystore;
89 :
90 1307 : return CHIP_NO_ERROR;
91 : }
92 :
93 10 : CHIP_ERROR CryptoContext::InitFromSecret(Crypto::SessionKeystore & keystore, const Crypto::HkdfKeyHandle & hkdfKey,
94 : const ByteSpan & salt, SessionInfoType infoType, SessionRole role)
95 : {
96 10 : VerifyOrReturnError(mKeyAvailable == false, CHIP_ERROR_INCORRECT_STATE);
97 :
98 10 : ByteSpan info = (infoType == SessionInfoType::kSessionResumption) ? ByteSpan(RSEKeysInfo) : ByteSpan(SEKeysInfo);
99 :
100 : // If the secure session is created by session initiator, use the I2R key to encrypt
101 : // messages being transmitted. Otherwise, use the R2I key.
102 10 : auto & i2rKey = (role == SessionRole::kInitiator) ? mEncryptionKey : mDecryptionKey;
103 10 : auto & r2iKey = (role == SessionRole::kInitiator) ? mDecryptionKey : mEncryptionKey;
104 :
105 : #if CHIP_CONFIG_SECURITY_TEST_MODE
106 : ReturnErrorOnFailure(InitTestMode(keystore, i2rKey, r2iKey));
107 : #else
108 10 : ReturnErrorOnFailure(keystore.DeriveSessionKeys(hkdfKey, salt, info, i2rKey, r2iKey, mAttestationChallenge));
109 : #endif
110 :
111 10 : mKeyAvailable = true;
112 10 : mSessionRole = role;
113 10 : mKeystore = &keystore;
114 :
115 10 : return CHIP_NO_ERROR;
116 : }
117 :
118 6 : CHIP_ERROR CryptoContext::InitFromKeyPair(SessionKeystore & keystore, const Crypto::P256Keypair & local_keypair,
119 : const Crypto::P256PublicKey & remote_public_key, const ByteSpan & salt,
120 : SessionInfoType infoType, SessionRole role)
121 : {
122 :
123 6 : VerifyOrReturnError(mKeyAvailable == false, CHIP_ERROR_INCORRECT_STATE);
124 :
125 5 : P256ECDHDerivedSecret secret;
126 5 : ReturnErrorOnFailure(local_keypair.ECDH_derive_secret(remote_public_key, secret));
127 :
128 5 : return InitFromSecret(keystore, secret.Span(), salt, infoType, role);
129 5 : }
130 :
131 : #if CHIP_CONFIG_SECURITY_TEST_MODE
132 : CHIP_ERROR CryptoContext::InitTestMode(Crypto::SessionKeystore & keystore, Crypto::Aes128KeyHandle & i2rKey,
133 : Crypto::Aes128KeyHandle & r2iKey)
134 : {
135 : // If enabled, override the generated session key with a known key pair
136 : // to allow man-in-the-middle session key recovery for testing purposes.
137 :
138 : constexpr uint8_t kTestSharedSecret[CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH] = CHIP_CONFIG_TEST_SHARED_SECRET_VALUE;
139 :
140 : #warning \
141 : "Warning: CHIP_CONFIG_SECURITY_TEST_MODE=1 bypassing key negotiation... All sessions will use known, fixed test key, and NodeID=0 in NONCE. Node can only communicate with other nodes built with this flag set. Requires build flag 'treat_warnings_as_errors=false'."
142 : ChipLogError(
143 : SecureChannel,
144 : "Warning: CHIP_CONFIG_SECURITY_TEST_MODE=1 bypassing key negotiation... All sessions will use known, fixed test key, "
145 : "and NodeID=0 in NONCE. "
146 : "Node can only communicate with other nodes built with this flag set.");
147 :
148 : return keystore.DeriveSessionKeys(ByteSpan(kTestSharedSecret), ByteSpan{} /* salt */, ByteSpan(SEKeysInfo), i2rKey, r2iKey,
149 : mAttestationChallenge);
150 : }
151 : #endif // CHIP_CONFIG_SECURITY_TEST_MODE
152 :
153 18889 : CHIP_ERROR CryptoContext::BuildNonce(NonceView nonce, uint8_t securityFlags, uint32_t messageCounter, NodeId nodeId)
154 : {
155 18889 : Encoding::LittleEndian::BufferWriter bbuf(nonce.data(), nonce.size());
156 :
157 18889 : bbuf.Put8(securityFlags);
158 18889 : bbuf.Put32(messageCounter);
159 : #if CHIP_CONFIG_SECURITY_TEST_MODE
160 : bbuf.Put64(0); // Simplifies decryption of CASE sessions when in TEST_MODE.
161 : #else
162 18889 : bbuf.Put64(nodeId);
163 : #endif
164 :
165 18889 : return bbuf.Fit() ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY;
166 : }
167 :
168 4 : CHIP_ERROR CryptoContext::BuildPrivacyNonce(NonceView nonce, uint16_t sessionId, const MessageAuthenticationCode & mac)
169 : {
170 4 : const uint8_t * micFragment = &mac.GetTag()[kPrivacyNonceMicFragmentOffset];
171 4 : Encoding::BigEndian::BufferWriter bbuf(nonce.data(), nonce.size());
172 :
173 4 : bbuf.Put16(sessionId);
174 4 : bbuf.Put(micFragment, kPrivacyNonceMicFragmentLength);
175 4 : return bbuf.Fit() ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY;
176 : }
177 :
178 18891 : CHIP_ERROR CryptoContext::GetAdditionalAuthData(const PacketHeader & header, uint8_t * aad, uint16_t & len)
179 : {
180 18891 : VerifyOrReturnError(len >= header.EncodeSizeBytes(), CHIP_ERROR_INVALID_ARGUMENT);
181 :
182 : // Use unencrypted part of header as AAD. This will help
183 : // integrity protect the whole message
184 : uint16_t actualEncodedHeaderSize;
185 :
186 18891 : ReturnErrorOnFailure(header.Encode(aad, len, &actualEncodedHeaderSize));
187 18891 : VerifyOrReturnError(len >= actualEncodedHeaderSize, CHIP_ERROR_INVALID_ARGUMENT);
188 :
189 18891 : len = actualEncodedHeaderSize;
190 :
191 18891 : return CHIP_NO_ERROR;
192 : }
193 :
194 9493 : CHIP_ERROR CryptoContext::Encrypt(const uint8_t * input, size_t input_length, uint8_t * output, ConstNonceView nonce,
195 : PacketHeader & header, MessageAuthenticationCode & mac) const
196 : {
197 :
198 9493 : const size_t taglen = header.MICTagLength();
199 :
200 9493 : VerifyOrDie(taglen <= kMaxTagLen);
201 :
202 9493 : VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
203 9492 : VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
204 9491 : VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
205 :
206 : uint8_t AAD[kMaxAADLen];
207 9490 : uint16_t aadLen = sizeof(AAD);
208 : uint8_t tag[kMaxTagLen];
209 :
210 9490 : ReturnErrorOnFailure(GetAdditionalAuthData(header, AAD, aadLen));
211 :
212 9490 : if (mKeyContext)
213 : {
214 1 : ByteSpan plaintext(input, input_length);
215 1 : MutableByteSpan ciphertext(output, input_length);
216 1 : MutableByteSpan mic(tag, taglen);
217 :
218 1 : ReturnErrorOnFailure(mKeyContext->MessageEncrypt(plaintext, ByteSpan(AAD, aadLen), nonce, mic, ciphertext));
219 : }
220 : else
221 : {
222 9489 : VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
223 9488 : ReturnErrorOnFailure(
224 : AES_CCM_encrypt(input, input_length, AAD, aadLen, mEncryptionKey, nonce.data(), nonce.size(), output, tag, taglen));
225 : }
226 :
227 9489 : mac.SetTag(&header, tag, taglen);
228 :
229 9489 : return CHIP_NO_ERROR;
230 : }
231 :
232 9404 : CHIP_ERROR CryptoContext::Decrypt(const uint8_t * input, size_t input_length, uint8_t * output, ConstNonceView nonce,
233 : const PacketHeader & header, const MessageAuthenticationCode & mac) const
234 : {
235 9404 : const size_t taglen = header.MICTagLength();
236 9404 : const uint8_t * tag = mac.GetTag();
237 : uint8_t AAD[kMaxAADLen];
238 9404 : uint16_t aadLen = sizeof(AAD);
239 :
240 9404 : VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
241 9403 : VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
242 9402 : VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
243 :
244 9401 : ReturnErrorOnFailure(GetAdditionalAuthData(header, AAD, aadLen));
245 :
246 9401 : if (nullptr != mKeyContext)
247 : {
248 5 : ByteSpan ciphertext(input, input_length);
249 5 : MutableByteSpan plaintext(output, input_length);
250 5 : ByteSpan mic(tag, taglen);
251 :
252 5 : CHIP_ERROR err = mKeyContext->MessageDecrypt(ciphertext, ByteSpan(AAD, aadLen), nonce, mic, plaintext);
253 5 : ReturnErrorOnFailure(err);
254 : }
255 : else
256 : {
257 9396 : VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
258 9395 : ReturnErrorOnFailure(
259 : AES_CCM_decrypt(input, input_length, AAD, aadLen, tag, taglen, mDecryptionKey, nonce.data(), nonce.size(), output));
260 : }
261 9397 : return CHIP_NO_ERROR;
262 : }
263 :
264 0 : CHIP_ERROR CryptoContext::PrivacyEncrypt(const uint8_t * input, size_t input_length, uint8_t * output, PacketHeader & header,
265 : MessageAuthenticationCode & mac) const
266 : {
267 0 : VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
268 0 : VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
269 0 : VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
270 :
271 : // Confirm group key is available. Privacy obfuscation is not supported on unicast session keys.
272 0 : VerifyOrReturnError(mKeyContext != nullptr, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
273 :
274 0 : ByteSpan plaintext(input, input_length);
275 0 : MutableByteSpan privacytext(output, input_length);
276 : CryptoContext::NonceStorage privacyNonce;
277 0 : CryptoContext::BuildPrivacyNonce(privacyNonce, header.GetSessionId(), mac);
278 :
279 0 : return mKeyContext->PrivacyEncrypt(plaintext, privacyNonce, privacytext);
280 : }
281 :
282 3 : CHIP_ERROR CryptoContext::PrivacyDecrypt(const uint8_t * input, size_t input_length, uint8_t * output, const PacketHeader & header,
283 : const MessageAuthenticationCode & mac) const
284 : {
285 3 : VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
286 3 : VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
287 3 : VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
288 :
289 : // Confirm group key is available. Privacy obfuscation is not supported on session keys.
290 3 : VerifyOrReturnError(mKeyContext != nullptr, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
291 :
292 3 : const ByteSpan privacytext(input, input_length);
293 3 : MutableByteSpan plaintext(output, input_length);
294 : CryptoContext::NonceStorage privacyNonce;
295 3 : CryptoContext::BuildPrivacyNonce(privacyNonce, header.GetSessionId(), mac);
296 :
297 3 : return mKeyContext->PrivacyDecrypt(privacytext, privacyNonce, plaintext);
298 : }
299 :
300 : } // namespace chip
|