Matter SDK Coverage Report
Current view: top level - transport - CryptoContext.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 91.3 % 104 95
Test Date: 2025-01-17 19:00:11 Functions: 91.7 % 12 11

            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       135239 : CryptoContext::CryptoContext() : mKeyAvailable(false) {}
      55              : 
      56       135254 : CryptoContext::~CryptoContext()
      57              : {
      58       135254 :     if (mKeystore)
      59              :     {
      60         1463 :         mKeystore->DestroyKey(mEncryptionKey);
      61         1463 :         mKeystore->DestroyKey(mDecryptionKey);
      62              :     }
      63              : 
      64       135254 :     mKeystore   = nullptr;
      65       135254 :     mKeyContext = nullptr;
      66       135254 : }
      67              : 
      68         1453 : CHIP_ERROR CryptoContext::InitFromSecret(SessionKeystore & keystore, const ByteSpan & secret, const ByteSpan & salt,
      69              :                                          SessionInfoType infoType, SessionRole role)
      70              : {
      71         1453 :     VerifyOrReturnError(mKeyAvailable == false, CHIP_ERROR_INCORRECT_STATE);
      72              : 
      73         1453 :     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         1453 :     auto & i2rKey = (role == SessionRole::kInitiator) ? mEncryptionKey : mDecryptionKey;
      78         1453 :     auto & r2iKey = (role == SessionRole::kInitiator) ? mDecryptionKey : mEncryptionKey;
      79              : 
      80              : #if CHIP_CONFIG_SECURITY_TEST_MODE
      81              :     ReturnErrorOnFailure(InitTestMode(keystore, i2rKey, r2iKey));
      82              : #else
      83         1453 :     ReturnErrorOnFailure(keystore.DeriveSessionKeys(secret, salt, info, i2rKey, r2iKey, mAttestationChallenge));
      84              : #endif
      85              : 
      86         1453 :     mKeyAvailable = true;
      87         1453 :     mSessionRole  = role;
      88         1453 :     mKeystore     = &keystore;
      89              : 
      90         1453 :     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        19365 : CHIP_ERROR CryptoContext::BuildNonce(NonceView nonce, uint8_t securityFlags, uint32_t messageCounter, NodeId nodeId)
     154              : {
     155        19365 :     Encoding::LittleEndian::BufferWriter bbuf(nonce.data(), nonce.size());
     156              : 
     157        19365 :     bbuf.Put8(securityFlags);
     158        19365 :     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        19365 :     bbuf.Put64(nodeId);
     163              : #endif
     164              : 
     165        19365 :     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        19368 : CHIP_ERROR CryptoContext::GetAdditionalAuthData(const PacketHeader & header, uint8_t * aad, uint16_t & len)
     179              : {
     180        19368 :     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        19368 :     ReturnErrorOnFailure(header.Encode(aad, len, &actualEncodedHeaderSize));
     187        19368 :     VerifyOrReturnError(len >= actualEncodedHeaderSize, CHIP_ERROR_INVALID_ARGUMENT);
     188              : 
     189        19368 :     len = actualEncodedHeaderSize;
     190              : 
     191        19368 :     return CHIP_NO_ERROR;
     192              : }
     193              : 
     194         9731 : 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         9731 :     const size_t taglen = header.MICTagLength();
     199              : 
     200         9731 :     VerifyOrDie(taglen <= kMaxTagLen);
     201              : 
     202         9731 :     VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     203         9730 :     VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
     204         9729 :     VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     205              : 
     206              :     uint8_t AAD[kMaxAADLen];
     207         9728 :     uint16_t aadLen = sizeof(AAD);
     208              :     uint8_t tag[kMaxTagLen];
     209              : 
     210         9728 :     ReturnErrorOnFailure(GetAdditionalAuthData(header, AAD, aadLen));
     211              : 
     212         9728 :     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         9727 :         VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
     223         9726 :         ReturnErrorOnFailure(
     224              :             AES_CCM_encrypt(input, input_length, AAD, aadLen, mEncryptionKey, nonce.data(), nonce.size(), output, tag, taglen));
     225              :     }
     226              : 
     227         9727 :     mac.SetTag(&header, tag, taglen);
     228              : 
     229         9727 :     return CHIP_NO_ERROR;
     230              : }
     231              : 
     232         9643 : 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         9643 :     const size_t taglen = header.MICTagLength();
     236         9643 :     const uint8_t * tag = mac.GetTag();
     237              :     uint8_t AAD[kMaxAADLen];
     238         9643 :     uint16_t aadLen = sizeof(AAD);
     239              : 
     240         9643 :     VerifyOrReturnError(input != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     241         9642 :     VerifyOrReturnError(input_length > 0, CHIP_ERROR_INVALID_ARGUMENT);
     242         9641 :     VerifyOrReturnError(output != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     243              : 
     244         9640 :     ReturnErrorOnFailure(GetAdditionalAuthData(header, AAD, aadLen));
     245              : 
     246         9640 :     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         9635 :         VerifyOrReturnError(mKeyAvailable, CHIP_ERROR_INVALID_USE_OF_SESSION_KEY);
     258         9634 :         ReturnErrorOnFailure(
     259              :             AES_CCM_decrypt(input, input_length, AAD, aadLen, tag, taglen, mDecryptionKey, nonce.data(), nonce.size(), output));
     260              :     }
     261         9636 :     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 2.0-1