Skip to content

Commit

Permalink
[ICD] Add Check-In payload generation (project-chip#27615)
Browse files Browse the repository at this point in the history
* Add Check-In payload generation

* Applied PR comments

* Fix last comment

* address comments

* address comments

* fix comments

* fix payload representation type

* Fix OpenIotSDK failure

* fix last test issue
  • Loading branch information
jepenven-silabs authored and abpoth committed Sep 12, 2023
1 parent 1a37e0d commit c034823
Show file tree
Hide file tree
Showing 6 changed files with 471 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ constexpr size_t kAES_CCM128_Block_Length = kAES_CCM128_Key_Length;
constexpr size_t kAES_CCM128_Nonce_Length = 13;
constexpr size_t kAES_CCM128_Tag_Length = 16;

constexpr size_t CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES = kAES_CCM128_Nonce_Length;

/* These sizes are hardcoded here to remove header dependency on underlying crypto library
* in a public interface file. The validity of these sizes is verified by static_assert in
* the implementation files.
Expand Down
3 changes: 3 additions & 0 deletions src/protocols/secure_channel/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ static_library("secure_channel") {
"CASEServer.h",
"CASESession.cpp",
"CASESession.h",
"CheckinMessage.cpp",
"CheckinMessage.h",
"DefaultSessionResumptionStorage.cpp",
"DefaultSessionResumptionStorage.h",
"PASESession.cpp",
Expand All @@ -50,6 +52,7 @@ static_library("secure_channel") {

public_deps = [
":type_definitions",
"${chip_root}/src/crypto",
"${chip_root}/src/lib/core",
"${chip_root}/src/lib/support",
"${chip_root}/src/messaging",
Expand Down
102 changes: 102 additions & 0 deletions src/protocols/secure_channel/CheckinMessage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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 "CheckinMessage.h"
#include <lib/core/CHIPCore.h>

#include <lib/core/CHIPEncoding.h>
#include <protocols/secure_channel/Constants.h>

namespace chip {
namespace Protocols {
namespace SecureChannel {

CHIP_ERROR CheckinMessage::GenerateCheckinMessagePayload(Crypto::Aes128KeyHandle & key, CounterType counter,
const ByteSpan & appData, MutableByteSpan & output)
{
VerifyOrReturnError(appData.size() <= sMaxAppDataSize, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(output.size() >= (appData.size() + sMinPayloadSize), CHIP_ERROR_INVALID_ARGUMENT);

CHIP_ERROR err = CHIP_NO_ERROR;
uint8_t * appDataStartPtr = output.data() + CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES;
Encoding::LittleEndian::Put32(appDataStartPtr, counter);

chip::Crypto::HMAC_sha shaHandler;
uint8_t nonceWorkBuffer[CHIP_CRYPTO_HASH_LEN_BYTES] = { 0 };

ReturnErrorOnFailure(shaHandler.HMAC_SHA256(key.As<Aes128KeyByteArray>(), sizeof(Aes128KeyByteArray), appDataStartPtr,
sizeof(CounterType), nonceWorkBuffer, CHIP_CRYPTO_HASH_LEN_BYTES));

static_assert(sizeof(nonceWorkBuffer) >= CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, "We're reading off the end of our buffer.");
memcpy(output.data(), nonceWorkBuffer, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);

// In place encryption to save some RAM
memcpy(appDataStartPtr + sizeof(CounterType), appData.data(), appData.size());

uint8_t * micPtr = appDataStartPtr + sizeof(CounterType) + appData.size();
ReturnErrorOnFailure(Crypto::AES_CCM_encrypt(appDataStartPtr, sizeof(CounterType) + appData.size(), nullptr, 0, key,
output.data(), CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, appDataStartPtr, micPtr,
CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES));

output.reduce_size(appData.size() + sMinPayloadSize);

return err;
}

CHIP_ERROR CheckinMessage::ParseCheckinMessagePayload(Crypto::Aes128KeyHandle & key, ByteSpan & payload, CounterType & counter,
MutableByteSpan & appData)
{
VerifyOrReturnError(payload.size() >= sMinPayloadSize, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(payload.size() <= (sMinPayloadSize + sMaxAppDataSize), CHIP_ERROR_INVALID_ARGUMENT);

CHIP_ERROR err = CHIP_NO_ERROR;
size_t appDataSize = GetAppDataSize(payload);

// 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_INVALID_ARGUMENT);

ByteSpan nonce = payload.SubSpan(0, CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES);
ByteSpan encryptedData = payload.SubSpan(CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, sizeof(CounterType) + appDataSize);
ByteSpan mic =
payload.SubSpan(CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES + sizeof(CounterType) + appDataSize, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);

err = Crypto::AES_CCM_decrypt(encryptedData.data(), encryptedData.size(), nullptr, 0, mic.data(), mic.size(), key, nonce.data(),
nonce.size(), appData.data());

ReturnErrorOnFailure(err);

counter = Encoding::LittleEndian::Get32(appData.data());
// Shift to remove the counter from the appData
memmove(appData.data(), sizeof(CounterType) + appData.data(), appDataSize);

appData.reduce_size(appDataSize);
return err;
}

size_t CheckinMessage::GetAppDataSize(ByteSpan & payload)
{
return (payload.size() <= sMinPayloadSize) ? 0 : payload.size() - sMinPayloadSize;
}

} // namespace SecureChannel
} // namespace Protocols
} // namespace chip
90 changes: 90 additions & 0 deletions src/protocols/secure_channel/CheckinMessage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.
*/

#pragma once

#include <crypto/CHIPCryptoPAL.h>
#include <lib/support/Span.h>
#include <stdint.h>

namespace chip {
namespace Protocols {
namespace SecureChannel {
using namespace Crypto;

using CounterType = uint32_t;

/**
* @brief Implement section 4.18.2 of the spec regarding
* Check-in message payload
*
*/
class DLL_EXPORT CheckinMessage
{
public:
~CheckinMessage();
/**
* @brief Generate Check-in Message payload
*
* @param key Key with which to encrypt the check-in payload
* @param counter Check-in counter
* @param appData Application Data to incorporate within the Check-in message. Allowed to be empty.
* @param output Buffer in Which to store the generated payload. SUFFICIENT SPACE MUST BE ALLOCATED by the caller
* Required Buffer Size is : GetCheckinPayloadSize(appData.size())
* @return CHIP_ERROR
*/
static CHIP_ERROR GenerateCheckinMessagePayload(Crypto::Aes128KeyHandle & key, CounterType counter, const ByteSpan & appData,
MutableByteSpan & output);

/**
* @brief Parse Check-in Message payload
*
* @param key Key with which to decrypt the check-in payload
* @param payload The received payload to decrypt and parse
* @param counter The counter value retrieved from the payload
* @param appData The optional application data decrypted. The size of appData must be at least the size of
* GetAppDataSize(payload) + sizeof(CounterType)
* @return CHIP_ERROR
*/
static CHIP_ERROR ParseCheckinMessagePayload(Crypto::Aes128KeyHandle & key, ByteSpan & payload, CounterType & counter,
MutableByteSpan & appData);

static inline size_t GetCheckinPayloadSize(size_t appDataSize) { return appDataSize + sMinPayloadSize; }

/**
* @brief Get the App Data Size
*
* @param payload The undecrypted payload
* @return size_t size in byte of the application data from the payload
*/
static size_t GetAppDataSize(ByteSpan & payload);

static constexpr uint16_t sMinPayloadSize =
CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES + sizeof(CounterType) + CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES;

// Issue #28603
static constexpr uint16_t sMaxAppDataSize = 1024;
};

} // namespace SecureChannel
} // namespace Protocols
} // namespace chip
2 changes: 2 additions & 0 deletions src/protocols/secure_channel/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ chip_test_suite("tests") {

# TODO - Fix Message Counter Sync to use group key
# "TestMessageCounterManager.cpp",
"TestCheckinMsg.cpp",
"TestDefaultSessionResumptionStorage.cpp",
"TestPASESession.cpp",
"TestPairingSession.cpp",
Expand All @@ -22,6 +23,7 @@ chip_test_suite("tests") {

public_deps = [
"${chip_root}/src/credentials/tests:cert_test_vectors",
"${chip_root}/src/crypto/tests:tests_lib",
"${chip_root}/src/lib/core",
"${chip_root}/src/lib/support",
"${chip_root}/src/lib/support:testing",
Expand Down
Loading

0 comments on commit c034823

Please sign in to comment.