blob: 65e8eac55ed8300e742cf0a7264c412c39e11878 [file] [log] [blame]
/**
* Copyright (c) 2023 Project CHIP Authors
*
* 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.
*/
#import "MTRSessionResumptionStorageBridge.h"
#import "MTRConversion.h"
#import "MTRDeviceControllerFactory_Internal.h"
#import "MTRDeviceController_Internal.h"
#import "MTRLogging_Internal.h"
#import "NSDataSpanConversion.h"
#include <platform/LockTracker.h>
using namespace chip;
MTRSessionResumptionStorageBridge::MTRSessionResumptionStorageBridge(MTRDeviceControllerFactory * factory)
: mFactory(factory)
{
}
CHIP_ERROR MTRSessionResumptionStorageBridge::FindByScopedNodeId(const chip::ScopedNodeId & node,
ResumptionIdStorage & resumptionId, chip::Crypto::P256ECDHDerivedSecret & sharedSecret, chip::CATValues & peerCATs)
{
assertChipStackLockedByCurrentThread();
auto * controller = [mFactory runningControllerForFabricIndex:node.GetFabricIndex()];
if (controller == nil) {
return CHIP_ERROR_KEY_NOT_FOUND;
}
auto * resumptionInfo = [controller.controllerDataStore findResumptionInfoByNodeID:@(node.GetNodeId())];
if (resumptionInfo == nil) {
return CHIP_ERROR_KEY_NOT_FOUND;
}
NodeId ignored;
return DeconstructResumptionInfo(resumptionInfo, ignored, resumptionId, sharedSecret, peerCATs);
}
CHIP_ERROR MTRSessionResumptionStorageBridge::FindByResumptionId(ConstResumptionIdView resumptionId, chip::ScopedNodeId & node,
chip::Crypto::P256ECDHDerivedSecret & sharedSecret, chip::CATValues & peerCATs)
{
assertChipStackLockedByCurrentThread();
auto * resumptionIDData = AsData(resumptionId);
auto * controllerList = [mFactory getRunningControllers];
for (MTRDeviceController * controller in controllerList) {
FabricIndex fabricIndex = controller.fabricIndex;
if (!IsValidFabricIndex(fabricIndex)) {
// This controller is not sufficiently "running"; it does not have a
// fabric index yet. Just skip it.
continue;
}
auto * resumptionInfo = [controller.controllerDataStore findResumptionInfoByResumptionID:resumptionIDData];
if (resumptionInfo != nil) {
NodeId nodeID;
ResumptionIdStorage ignored;
ReturnErrorOnFailure(DeconstructResumptionInfo(resumptionInfo, nodeID, ignored, sharedSecret, peerCATs));
node = ScopedNodeId(nodeID, fabricIndex);
return CHIP_NO_ERROR;
}
}
// None of the controllers matched.
return CHIP_ERROR_KEY_NOT_FOUND;
}
CHIP_ERROR MTRSessionResumptionStorageBridge::Save(const chip::ScopedNodeId & node, ConstResumptionIdView resumptionId,
const chip::Crypto::P256ECDHDerivedSecret & sharedSecret, const chip::CATValues & peerCATs)
{
assertChipStackLockedByCurrentThread();
auto * controller = [mFactory runningControllerForFabricIndex:node.GetFabricIndex()];
if (controller == nil) {
return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
auto * resumptionInfo = [[MTRCASESessionResumptionInfo alloc] init];
resumptionInfo.nodeID = @(node.GetNodeId());
resumptionInfo.resumptionID = AsData(resumptionId);
resumptionInfo.sharedSecret = AsData(ByteSpan(sharedSecret.ConstBytes(), sharedSecret.Length()));
resumptionInfo.caseAuthenticatedTags = CATValuesToSet(peerCATs);
[controller.controllerDataStore storeResumptionInfo:resumptionInfo];
return CHIP_NO_ERROR;
}
CHIP_ERROR MTRSessionResumptionStorageBridge::DeleteAll(chip::FabricIndex fabricIndex)
{
assertChipStackLockedByCurrentThread();
// NOTE: During controller startup and shutdown, the SDK DeviceControllerFactory
// calls this method from ClearCASEResumptionStateOnFabricChange() due to fabric
// update or removal. For us that does not matter, since we don't key resumption
// data on fabric index anyway. But we do want to avoid executing the DeleteAll
// so we can actually store session resumption data persistently.
// And that is the only use of DeleteAll for controllers in practice, in the
// situations where we are using MTRSessionResumptionStorageBridge at all.
// So just no-op this function, but verify that our assumptions hold.
auto * controller = [mFactory runningControllerForFabricIndex:fabricIndex
includeControllerStartingUp:NO
includeControllerShuttingDown:NO];
VerifyOrDieWithMsg(controller == nil, Controller, "ResumptionStorage::DeleteAll called for running controller");
return CHIP_NO_ERROR;
}
CHIP_ERROR MTRSessionResumptionStorageBridge::DeconstructResumptionInfo(MTRCASESessionResumptionInfo * resumptionInfo,
chip::NodeId & nodeID, ResumptionIdStorage & resumptionId, chip::Crypto::P256ECDHDerivedSecret & sharedSecret,
chip::CATValues & peerCATs)
{
if (resumptionInfo.resumptionID.length != resumptionId.size()) {
MTR_LOG_ERROR("Unable to return resumption ID: Stored size %llu does not match required size %llu",
static_cast<unsigned long long>(resumptionInfo.resumptionID.length),
static_cast<unsigned long long>(resumptionId.size()));
return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
if (resumptionInfo.sharedSecret.length > sharedSecret.Capacity()) {
MTR_LOG_ERROR("Unable to return resumption shared secret: Stored size %llu is larger than allowed size %llu",
static_cast<unsigned long long>(resumptionInfo.sharedSecret.length),
static_cast<unsigned long long>(sharedSecret.Capacity()));
return CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
nodeID = resumptionInfo.nodeID.unsignedLongLongValue;
memcpy(resumptionId.data(), resumptionInfo.resumptionID.bytes, resumptionInfo.resumptionID.length);
sharedSecret.SetLength(resumptionInfo.sharedSecret.length);
memcpy(sharedSecret.Bytes(), resumptionInfo.sharedSecret.bytes, resumptionInfo.sharedSecret.length);
ReturnErrorOnFailure(SetToCATValues(resumptionInfo.caseAuthenticatedTags, peerCATs));
return CHIP_NO_ERROR;
}