| /* |
| * Copyright (c) 2022 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. |
| */ |
| |
| #include <protocols/secure_channel/DefaultSessionResumptionStorage.h> |
| |
| #include <lib/support/Base64.h> |
| #include <lib/support/SafeInt.h> |
| |
| namespace chip { |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId, |
| Crypto::P256ECDHDerivedSecret & sharedSecret, CATValues & peerCATs) |
| { |
| ReturnErrorOnFailure(LoadState(node, resumptionId, sharedSecret, peerCATs)); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::FindByResumptionId(ConstResumptionIdView resumptionId, ScopedNodeId & node, |
| Crypto::P256ECDHDerivedSecret & sharedSecret, CATValues & peerCATs) |
| { |
| ReturnErrorOnFailure(FindNodeByResumptionId(resumptionId, node)); |
| ResumptionIdStorage tmpResumptionId; |
| ReturnErrorOnFailure(FindByScopedNodeId(node, tmpResumptionId, sharedSecret, peerCATs)); |
| VerifyOrReturnError(std::equal(tmpResumptionId.begin(), tmpResumptionId.end(), resumptionId.begin(), resumptionId.end()), |
| CHIP_ERROR_KEY_NOT_FOUND); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::FindNodeByResumptionId(ConstResumptionIdView resumptionId, ScopedNodeId & node) |
| { |
| ReturnErrorOnFailure(LoadLink(resumptionId, node)); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::Save(const ScopedNodeId & node, ConstResumptionIdView resumptionId, |
| const Crypto::P256ECDHDerivedSecret & sharedSecret, const CATValues & peerCATs) |
| { |
| SessionIndex index; |
| ReturnErrorOnFailure(LoadIndex(index)); |
| |
| for (size_t i = 0; i < index.mSize; ++i) |
| { |
| if (index.mNodes[i] == node) |
| { |
| // Node already exists in the index. Save in place. |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ResumptionIdStorage oldResumptionId; |
| Crypto::P256ECDHDerivedSecret oldSharedSecret; |
| CATValues oldPeerCATs; |
| // This follows the approach in Delete. Removal of the old |
| // resumption-id-keyed link is best effort. If we cannot load |
| // state to lookup the resumption ID for the key, the entry in |
| // the link table will be leaked. |
| err = LoadState(node, oldResumptionId, oldSharedSecret, oldPeerCATs); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, |
| "LoadState failed; unable to fully delete session resumption record for node " ChipLogFormatX64 |
| ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| else |
| { |
| err = DeleteLink(oldResumptionId); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, |
| "DeleteLink failed; unable to fully delete session resumption record for node " ChipLogFormatX64 |
| ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| } |
| ReturnErrorOnFailure(SaveState(node, resumptionId, sharedSecret, peerCATs)); |
| ReturnErrorOnFailure(SaveLink(resumptionId, node)); |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| if (index.mSize == CHIP_CONFIG_CASE_SESSION_RESUME_CACHE_SIZE) |
| { |
| // TODO: implement LRU for resumption |
| ReturnErrorOnFailure(Delete(index.mNodes[0])); |
| ReturnErrorOnFailure(LoadIndex(index)); |
| } |
| |
| ReturnErrorOnFailure(SaveState(node, resumptionId, sharedSecret, peerCATs)); |
| ReturnErrorOnFailure(SaveLink(resumptionId, node)); |
| |
| index.mNodes[index.mSize++] = node; |
| ReturnErrorOnFailure(SaveIndex(index)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::Delete(const ScopedNodeId & node) |
| { |
| SessionIndex index; |
| ReturnErrorOnFailure(LoadIndex(index)); |
| |
| ResumptionIdStorage resumptionId; |
| Crypto::P256ECDHDerivedSecret sharedSecret; |
| CATValues peerCATs; |
| CHIP_ERROR err = LoadState(node, resumptionId, sharedSecret, peerCATs); |
| if (err == CHIP_NO_ERROR) |
| { |
| err = DeleteLink(resumptionId); |
| if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| ChipLogError(SecureChannel, |
| "Unable to delete session resumption link for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| } |
| else if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| ChipLogError(SecureChannel, |
| "Unable to load session resumption state during session deletion for node " ChipLogFormatX64 |
| ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| |
| err = DeleteState(node); |
| if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| ChipLogError(SecureChannel, "Unable to delete session resumption state for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| |
| bool found = false; |
| for (size_t i = 0; i < index.mSize; ++i) |
| { |
| if (found) |
| { |
| // index.mSize was decreased by 1 when found was set to true. |
| // So the (i+1)th element isn't out of bounds. |
| index.mNodes[i] = index.mNodes[i + 1]; |
| } |
| else |
| { |
| if (index.mNodes[i] == node) |
| { |
| found = true; |
| if (i + 1 < index.mSize) |
| { |
| index.mNodes[i] = index.mNodes[i + 1]; |
| } |
| index.mSize -= 1; |
| } |
| } |
| } |
| |
| if (found) |
| { |
| err = SaveIndex(index); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, "Unable to save session resumption index: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| else |
| { |
| ChipLogError(SecureChannel, |
| "Unable to find session resumption state for node in index" ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(node.GetNodeId()), err.Format()); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR DefaultSessionResumptionStorage::DeleteAll(FabricIndex fabricIndex) |
| { |
| CHIP_ERROR stickyErr = CHIP_NO_ERROR; |
| size_t found = 0; |
| SessionIndex index; |
| ReturnErrorOnFailure(LoadIndex(index)); |
| size_t initialSize = index.mSize; |
| for (size_t i = 0; i < initialSize; ++i) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| size_t cur = i - found; |
| size_t remain = initialSize - i; |
| ResumptionIdStorage resumptionId; |
| Crypto::P256ECDHDerivedSecret sharedSecret; |
| CATValues peerCATs; |
| if (index.mNodes[cur].GetFabricIndex() != fabricIndex) |
| { |
| continue; |
| } |
| err = LoadState(index.mNodes[cur], resumptionId, sharedSecret, peerCATs); |
| stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr; |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, |
| "Session resumption cache deletion partially failed for fabric index %u, " |
| "unable to load node state: %" CHIP_ERROR_FORMAT, |
| fabricIndex, err.Format()); |
| continue; |
| } |
| err = DeleteLink(resumptionId); |
| stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr; |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, |
| "Session resumption cache deletion partially failed for fabric index %u, " |
| "unable to delete node link: %" CHIP_ERROR_FORMAT, |
| fabricIndex, err.Format()); |
| continue; |
| } |
| err = DeleteState(index.mNodes[cur]); |
| stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr; |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SecureChannel, |
| "Session resumption cache is in an inconsistent state! " |
| "Unable to delete node state during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT, |
| fabricIndex, err.Format()); |
| continue; |
| } |
| ++found; |
| --remain; |
| if (remain) |
| { |
| memmove(&index.mNodes[cur], &index.mNodes[cur + 1], remain * sizeof(index.mNodes[0])); |
| } |
| } |
| if (found) |
| { |
| index.mSize -= found; |
| CHIP_ERROR err = SaveIndex(index); |
| stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr; |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError( |
| SecureChannel, |
| "Session resumption cache is in an inconsistent state! " |
| "Unable to save session resumption index during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT, |
| fabricIndex, err.Format()); |
| } |
| } |
| return stickyErr; |
| } |
| |
| } // namespace chip |