LCOV - code coverage report
Current view: top level - transport - CryptoContext.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 95 104 91.3 %
Date: 2024-02-15 08:20:41 Functions: 11 12 91.7 %

          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

Generated by: LCOV version 1.14