blob: f4eaa48b114116b015a1fc04e476de3526e25b6a [file] [log] [blame]
/*
*
* Copyright (c) 2023 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 defines objects for a CHIP ICD handler which handles unsolicited check-in messages.
*
*/
#include <app/AppConfig.h>
#include <app/InteractionModelEngine.h>
#include <app/InteractionModelTimeout.h>
#include <app/icd/client/CheckInHandler.h>
#include <app/icd/client/RefreshKeySender.h>
#include <cinttypes>
#include <lib/core/Global.h>
#include <lib/support/CodeUtils.h>
#include <messaging/Flags.h>
#include <protocols/Protocols.h>
#include <protocols/secure_channel/Constants.h>
using namespace chip::Protocols::SecureChannel;
namespace chip {
namespace app {
inline constexpr uint64_t kCheckInCounterMax = (1ULL << 32);
inline constexpr uint32_t kKeyRefreshLimit = (1U << 31);
CheckInHandler::CheckInHandler() {}
CHIP_ERROR CheckInHandler::Init(Messaging::ExchangeManager * exchangeManager, ICDClientStorage * clientStorage,
CheckInDelegate * delegate, InteractionModelEngine * engine)
{
VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(clientStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(engine != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mpExchangeManager == nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mpICDClientStorage == nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mpCheckInDelegate == nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mpImEngine == nullptr, CHIP_ERROR_INCORRECT_STATE);
mpExchangeManager = exchangeManager;
mpICDClientStorage = clientStorage;
mpCheckInDelegate = delegate;
mpImEngine = engine;
return mpExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::ICD_CheckIn, this);
}
void CheckInHandler::Shutdown()
{
mpICDClientStorage = nullptr;
mpCheckInDelegate = nullptr;
if (mpExchangeManager)
{
mpExchangeManager->UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::ICD_CheckIn);
mpExchangeManager = nullptr;
}
}
CHIP_ERROR CheckInHandler::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
{
// Return error for wrong message type
VerifyOrReturnError(payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::ICD_CheckIn),
CHIP_ERROR_INVALID_MESSAGE_TYPE);
newDelegate = this;
return CHIP_NO_ERROR;
}
CHIP_ERROR CheckInHandler::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
System::PacketBufferHandle && payload)
{
// If the message type is not ICD_CheckIn, return CHIP_NO_ERROR and exit
VerifyOrReturnError(payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::ICD_CheckIn), CHIP_NO_ERROR);
ByteSpan payloadByteSpan{ payload->Start(), payload->DataLength() };
ICDClientInfo clientInfo;
CounterType counter = 0;
// If the check-in message processing fails, return CHIP_NO_ERROR and exit.
CHIP_ERROR err = mpICDClientStorage->ProcessCheckInPayload(payloadByteSpan, clientInfo, counter);
if (CHIP_NO_ERROR != err)
{
ChipLogError(ICD, "ProcessCheckInPayload failed: %" CHIP_ERROR_FORMAT, err.Format());
return CHIP_NO_ERROR;
}
CounterType receivedCheckInCounterOffset = (counter - clientInfo.start_icd_counter) % kCheckInCounterMax;
// Detect duplicate check-in messages and return CHIP_NO_ERROR on receiving a duplicate message
if (receivedCheckInCounterOffset <= clientInfo.offset)
{
ChipLogError(ICD, "A duplicate check-in message was received and discarded");
return CHIP_NO_ERROR;
}
clientInfo.offset = receivedCheckInCounterOffset;
bool refreshKey = (receivedCheckInCounterOffset > kKeyRefreshLimit);
if (refreshKey)
{
ChipLogProgress(ICD, "Key Refresh is required");
RefreshKeySender * refreshKeySender = mpCheckInDelegate->OnKeyRefreshNeeded(clientInfo, mpICDClientStorage);
if (refreshKeySender == nullptr)
{
ChipLogError(ICD, "Key Refresh failed for node ID:" ChipLogFormatScopedNodeId,
ChipLogValueScopedNodeId(clientInfo.peer_node));
return CHIP_NO_ERROR;
}
err = refreshKeySender->EstablishSessionToPeer();
if (CHIP_NO_ERROR != err)
{
ChipLogError(ICD, "CASE session establishment failed with error : %" CHIP_ERROR_FORMAT, err.Format());
mpCheckInDelegate->OnKeyRefreshDone(refreshKeySender, err);
return CHIP_NO_ERROR;
}
}
else
{
mpICDClientStorage->StoreEntry(clientInfo);
mpCheckInDelegate->OnCheckInComplete(clientInfo);
#if CHIP_CONFIG_ENABLE_READ_CLIENT
mpImEngine->OnActiveModeNotification(clientInfo.peer_node);
#endif // CHIP_CONFIG_ENABLE_READ_CLIENT
}
return CHIP_NO_ERROR;
}
void CheckInHandler::OnResponseTimeout(Messaging::ExchangeContext * ec) {}
} // namespace app
} // namespace chip