/* * Copyright (c) 2021 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. */ #include #include #include namespace chip { namespace Transport { void SecureSessionDeleter::Release(SecureSession * entry) { entry->mTable.ReleaseSession(entry); } void SecureSession::Activate(const ScopedNodeId & localNode, const ScopedNodeId & peerNode, CATValues peerCATs, uint16_t peerSessionId, const SessionParameters & sessionParameters) { VerifyOrDie(mState == State::kEstablishing); VerifyOrDie(peerNode.GetFabricIndex() == localNode.GetFabricIndex()); // PASE sessions must always start unassociated with a Fabric! VerifyOrDie(!((mSecureSessionType == Type::kPASE) && (peerNode.GetFabricIndex() != kUndefinedFabricIndex))); // CASE sessions must always start "associated" a given Fabric! VerifyOrDie(!((mSecureSessionType == Type::kCASE) && (peerNode.GetFabricIndex() == kUndefinedFabricIndex))); // CASE sessions can only be activated against operational node IDs! VerifyOrDie(!((mSecureSessionType == Type::kCASE) && (!IsOperationalNodeId(peerNode.GetNodeId()) || !IsOperationalNodeId(localNode.GetNodeId())))); mPeerNodeId = peerNode.GetNodeId(); mLocalNodeId = localNode.GetNodeId(); mPeerCATs = peerCATs; mPeerSessionId = peerSessionId; mRemoteSessionParams = sessionParameters; SetFabricIndex(peerNode.GetFabricIndex()); MarkActiveRx(); // Initialize SessionTimestamp and ActiveTimestamp per spec. Retain(); // This ref is released inside MarkForEviction MoveToState(State::kActive); if (mSecureSessionType == Type::kCASE) mTable.NewerSessionAvailable(this); ChipLogDetail(Inet, "SecureSession[%p]: Activated - Type:%d LSID:%d", this, to_underlying(mSecureSessionType), mLocalSessionId); } const char * SecureSession::StateToString(State state) const { switch (state) { case State::kEstablishing: return "kEstablishing"; break; case State::kActive: return "kActive"; break; case State::kDefunct: return "kDefunct"; break; case State::kPendingEviction: return "kPendingEviction"; break; default: return "???"; break; } } void SecureSession::MoveToState(State targetState) { if (mState != targetState) { ChipLogProgress(SecureChannel, "SecureSession[%p, LSID:%d]: State change '%s' --> '%s'", this, mLocalSessionId, StateToString(mState), StateToString(targetState)); mState = targetState; } } void SecureSession::MarkAsDefunct() { ChipLogDetail(Inet, "SecureSession[%p]: MarkAsDefunct Type:%d LSID:%d", this, to_underlying(mSecureSessionType), mLocalSessionId); ReferenceCountedHandle ref(*this); switch (mState) { case State::kEstablishing: // // A session can only be marked as defunct from the state of Active. // VerifyOrDie(false); return; case State::kActive: MoveToState(State::kDefunct); return; case State::kDefunct: // // Do nothing // return; case State::kPendingEviction: // // Once a session is headed for eviction, we CANNOT bring it back to either being active or defunct. Let's just // do nothing and return. // return; } } void SecureSession::MarkForEviction() { ChipLogDetail(Inet, "SecureSession[%p]: MarkForEviction Type:%d LSID:%d", this, to_underlying(mSecureSessionType), mLocalSessionId); ReferenceCountedHandle ref(*this); switch (mState) { case State::kEstablishing: MoveToState(State::kPendingEviction); // Interrupt the pairing NotifySessionReleased(); return; case State::kDefunct: FALLTHROUGH; case State::kActive: Release(); // Decrease the ref which is retained at Activate MoveToState(State::kPendingEviction); NotifySessionReleased(); return; case State::kPendingEviction: // Do nothing return; } } Access::SubjectDescriptor SecureSession::GetSubjectDescriptor() const { Access::SubjectDescriptor subjectDescriptor; if (IsOperationalNodeId(mPeerNodeId)) { subjectDescriptor.authMode = Access::AuthMode::kCase; subjectDescriptor.subject = mPeerNodeId; subjectDescriptor.cats = mPeerCATs; subjectDescriptor.fabricIndex = GetFabricIndex(); subjectDescriptor.isCommissioning = IsCommissioningSession(); } else if (IsPAKEKeyId(mPeerNodeId)) { // Responder (aka commissionee) gets subject descriptor filled in. // Initiator (aka commissioner) leaves subject descriptor unfilled. if (GetCryptoContext().IsResponder()) { subjectDescriptor.authMode = Access::AuthMode::kPase; subjectDescriptor.subject = mPeerNodeId; subjectDescriptor.fabricIndex = GetFabricIndex(); subjectDescriptor.isCommissioning = IsCommissioningSession(); } } else { VerifyOrDie(false); } return subjectDescriptor; } bool SecureSession::IsCommissioningSession() const { // PASE session is always a commissioning session. if (IsPASESession()) { return true; } // CASE session is a commissioning session if it was marked as such. // The SessionManager is what keeps track. if (IsCASESession() && mIsCaseCommissioningSession) { return true; } return false; } void SecureSession::Retain() { #if CHIP_CONFIG_SECURE_SESSION_REFCOUNT_LOGGING ChipLogProgress(SecureChannel, "SecureSession[%p]: ++ %d -> %d", this, GetReferenceCount(), GetReferenceCount() + 1); #endif ReferenceCounted::Retain(); } void SecureSession::Release() { #if CHIP_CONFIG_SECURE_SESSION_REFCOUNT_LOGGING ChipLogProgress(SecureChannel, "SecureSession[%p]: -- %d -> %d", this, GetReferenceCount(), GetReferenceCount() - 1); #endif ReferenceCounted::Release(); } void SecureSession::NewerSessionAvailable(const SessionHandle & session) { // Shift to the new session, checks are performed by the the caller SecureSessionTable::NewerSessionAvailable. IntrusiveList::Iterator iter = mHolders.begin(); while (iter != mHolders.end()) { // The iterator can be invalid once the session holder is migrated to another session. So we store its next value before // notifying the holder. IntrusiveList::Iterator next = iter; ++next; iter->ShiftToSession(session); iter = next; } } } // namespace Transport } // namespace chip