/* * Copyright (c) 2020 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * This file implements the Matter Checkin protocol. */ #include #include #include #include using namespace chip::Crypto; namespace chip { namespace Protocols { namespace SecureChannel { CHIP_ERROR CheckinMessage::GenerateCheckinMessagePayload(const Crypto::Aes128KeyHandle & aes128KeyHandle, const Crypto::Hmac128KeyHandle & hmacKeyHandle, const CounterType & counter, const ByteSpan & appData, MutableByteSpan & output) { VerifyOrReturnError(output.size() >= (appData.size() + kMinPayloadSize), CHIP_ERROR_BUFFER_TOO_SMALL); size_t cursorIndex = 0; // Generate Nonce from Key and counter value { MutableByteSpan nonce = output.SubSpan(0, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES); cursorIndex += nonce.size(); Encoding::LittleEndian::BufferWriter writer(nonce); ReturnErrorOnFailure(GenerateCheckInMessageNonce(hmacKeyHandle, counter, writer)); } // Encrypt Counter and Application Data { MutableByteSpan payloadByteSpan = output.SubSpan(cursorIndex, sizeof(CounterType) + appData.size()); cursorIndex += payloadByteSpan.size(); Encoding::LittleEndian::BufferWriter payloadWriter(payloadByteSpan); payloadWriter.EndianPut(counter, sizeof(counter)); payloadWriter.Put(appData.data(), appData.size()); VerifyOrReturnError(payloadWriter.Fit(), CHIP_ERROR_INTERNAL); MutableByteSpan mic = output.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); cursorIndex += mic.size(); // Validate that the cursorIndex is within the available output space VerifyOrReturnError(cursorIndex <= output.size(), CHIP_ERROR_BUFFER_TOO_SMALL); // Validate that the cursorIndex matchs the message length VerifyOrReturnError(cursorIndex == appData.size() + kMinPayloadSize, CHIP_ERROR_INTERNAL); ReturnErrorOnFailure(Crypto::AES_CCM_encrypt(payloadByteSpan.data(), payloadByteSpan.size(), nullptr, 0, aes128KeyHandle, output.data(), CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, payloadByteSpan.data(), mic.data(), mic.size())); } output.reduce_size(appData.size() + kMinPayloadSize); return CHIP_NO_ERROR; } CHIP_ERROR CheckinMessage::ParseCheckinMessagePayload(const Crypto::Aes128KeyHandle & aes128KeyHandle, const Crypto::Hmac128KeyHandle & hmacKeyHandle, const ByteSpan & payload, CounterType & counter, MutableByteSpan & appData) { size_t appDataSize = GetAppDataSize(payload); VerifyOrReturnError(payload.size() >= kMinPayloadSize, CHIP_ERROR_INVALID_MESSAGE_LENGTH); // To prevent workbuffer usage, appData size needs to be large enough to hold both the appData and the counter VerifyOrReturnError(appData.size() >= sizeof(CounterType) + appDataSize, CHIP_ERROR_BUFFER_TOO_SMALL); // Decrypt received data { size_t cursorIndex = 0; ByteSpan nonce = payload.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES); cursorIndex += nonce.size(); ByteSpan encryptedData = payload.SubSpan(cursorIndex, sizeof(CounterType) + appDataSize); cursorIndex += encryptedData.size(); ByteSpan mic = payload.SubSpan(cursorIndex, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); cursorIndex += mic.size(); // Return Invalid message length since the payload isn't the right size VerifyOrReturnError(cursorIndex == payload.size(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); ReturnErrorOnFailure(Crypto::AES_CCM_decrypt(encryptedData.data(), encryptedData.size(), nullptr, 0, mic.data(), mic.size(), aes128KeyHandle, nonce.data(), nonce.size(), appData.data())); } // Read decrypted counter and application data static_assert(sizeof(CounterType) == sizeof(uint32_t), "Expect counter to be 32 bits for correct decoding"); CounterType tempCounter = Encoding::LittleEndian::Get32(appData.data()); // Validate that the received nonce is correct { uint8_t calculatedNonceBuffer[CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES] = { 0 }; Encoding::LittleEndian::BufferWriter writer(calculatedNonceBuffer, sizeof(calculatedNonceBuffer)); ReturnErrorOnFailure(GenerateCheckInMessageNonce(hmacKeyHandle, tempCounter, writer)); // Validate received nonce is the same as the calculated ByteSpan nonce = payload.SubSpan(0, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES); VerifyOrReturnError(memcmp(nonce.data(), calculatedNonceBuffer, sizeof(calculatedNonceBuffer)) == 0, CHIP_ERROR_INTERNAL); } // We have successfully decrypted and validated Check-In message // Set output values counter = tempCounter; // Shift to remove the counter from the appData memmove(appData.data(), sizeof(CounterType) + appData.data(), appDataSize); appData.reduce_size(appDataSize); return CHIP_NO_ERROR; } CHIP_ERROR CheckinMessage::GenerateCheckInMessageNonce(const Crypto::Hmac128KeyHandle & hmacKeyHandle, CounterType counter, Encoding::LittleEndian::BufferWriter & writer) { VerifyOrReturnError(writer.Available() >= CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, CHIP_ERROR_BUFFER_TOO_SMALL); uint8_t hashWorkBuffer[CHIP_CRYPTO_HASH_LEN_BYTES] = { 0 }; uint8_t counterBuffer[sizeof(CounterType)]; // validate that Check-In counter is a uint32_t static_assert(sizeof(CounterType) == sizeof(uint32_t), "Expect counter to be 32 bits for correct encoding"); Encoding::LittleEndian::Put32(counterBuffer, counter); chip::Crypto::HMAC_sha shaHandler; ReturnErrorOnFailure( shaHandler.HMAC_SHA256(hmacKeyHandle, counterBuffer, sizeof(CounterType), hashWorkBuffer, CHIP_CRYPTO_HASH_LEN_BYTES)); writer.Put(hashWorkBuffer, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES); VerifyOrReturnError(writer.Fit(), CHIP_ERROR_BUFFER_TOO_SMALL); return CHIP_NO_ERROR; } size_t CheckinMessage::GetAppDataSize(const ByteSpan & payload) { return (payload.size() <= kMinPayloadSize) ? 0 : payload.size() - kMinPayloadSize; } } // namespace SecureChannel } // namespace Protocols } // namespace chip