LCOV - code coverage report
Current view: top level - protocols/secure_channel - CheckinMessage.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 55 55 100.0 %
Date: 2024-02-15 08:20:41 Functions: 4 4 100.0 %

          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

Generated by: LCOV version 1.14