| /* |
| * |
| * 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 <protocols/secure_channel/CASEServer.h> |
| |
| #include <core/CHIPError.h> |
| #include <support/CodeUtils.h> |
| #include <support/SafeInt.h> |
| #include <support/logging/CHIPLogging.h> |
| #include <transport/SecureSessionMgr.h> |
| |
| using namespace ::chip::Inet; |
| using namespace ::chip::Transport; |
| using namespace ::chip::Credentials; |
| |
| namespace chip { |
| |
| CHIP_ERROR CASEServer::ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, TransportMgrBase * transportMgr, |
| SecureSessionMgr * sessionMgr, Transport::FabricTable * fabrics, |
| SessionIDAllocator * idAllocator) |
| { |
| VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(sessionMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(fabrics != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mSessionMgr = sessionMgr; |
| mFabrics = fabrics; |
| mExchangeManager = exchangeManager; |
| mIDAllocator = idAllocator; |
| |
| Cleanup(); |
| |
| ReturnErrorOnFailure(GetSession().MessageDispatch().Init(transportMgr)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CASEServer::InitCASEHandshake(Messaging::ExchangeContext * ec) |
| { |
| ReturnErrorCodeIf(ec == nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // TODO - Use section [4.368] and definition of `Destination Identifier` to find fabric ID for CASE SigmaR1 message |
| mFabricIndex = kMinValidFabricIndex; |
| |
| Transport::FabricInfo * fabric = mFabrics->FindFabricWithIndex(mFabricIndex); |
| ReturnErrorCodeIf(fabric == nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| if (!fabric->IsInitialized() || !fabric->AreCredentialsAvailable()) |
| { |
| ReturnErrorOnFailure(mFabrics->LoadFromStorage(mFabricIndex)); |
| fabric = mFabrics->FindFabricWithIndex(mFabricIndex); |
| } |
| ReturnErrorCodeIf(fabric == nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| uint8_t credentialsIndex; |
| ReturnErrorOnFailure(fabric->GetCredentials(mCredentials, mCertificates, mRootKeyId, credentialsIndex)); |
| |
| ReturnErrorOnFailure(mIDAllocator->Allocate(mSessionKeyId)); |
| |
| // Setup CASE state machine using the credentials for the current fabric. |
| ReturnErrorOnFailure(GetSession().ListenForSessionEstablishment(&mCredentials, mSessionKeyId, this)); |
| |
| // Hand over the exchange context to the CASE session. |
| ec->SetDelegate(&GetSession()); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, |
| const PayloadHeader & payloadHeader, System::PacketBufferHandle && payload) |
| { |
| ChipLogProgress(Inet, "CASE Server received SigmaR1 message. Starting handshake. EC %p", ec); |
| CHIP_ERROR err = InitCASEHandshake(ec); |
| SuccessOrExit(err); |
| |
| // TODO - Enable multiple concurrent CASE session establishment |
| // https://github.com/project-chip/connectedhomeip/issues/8342 |
| ChipLogProgress(Inet, "CASE Server disabling CASE session setups"); |
| mExchangeManager->UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1); |
| |
| err = GetSession().OnMessageReceived(ec, packetHeader, payloadHeader, std::move(payload)); |
| SuccessOrExit(err); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| Cleanup(); |
| } |
| return err; |
| } |
| |
| void CASEServer::Cleanup() |
| { |
| // Let's re-register for CASE SigmaR1 message, so that the next CASE session setup request can be processed. |
| // https://github.com/project-chip/connectedhomeip/issues/8342 |
| ChipLogProgress(Inet, "CASE Server enabling CASE session setups"); |
| mExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_SigmaR1, this); |
| |
| mFabricIndex = Transport::kUndefinedFabricIndex; |
| mCredentials.Release(); |
| mCertificates.Release(); |
| GetSession().Clear(); |
| } |
| |
| void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err) |
| { |
| ChipLogProgress(Inet, "CASE Session establishment failed: %s", ErrorStr(err)); |
| mIDAllocator->Free(mSessionKeyId); |
| Cleanup(); |
| } |
| |
| void CASEServer::OnSessionEstablished() |
| { |
| ChipLogProgress(Inet, "CASE Session established. Setting up the secure channel."); |
| mSessionMgr->ExpireAllPairings(GetSession().PeerConnection().GetPeerNodeId(), mFabricIndex); |
| |
| CHIP_ERROR err = mSessionMgr->NewPairing( |
| Optional<Transport::PeerAddress>::Value(GetSession().PeerConnection().GetPeerAddress()), |
| GetSession().PeerConnection().GetPeerNodeId(), &GetSession(), SecureSession::SessionRole::kResponder, mFabricIndex); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Inet, "Failed in setting up secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| ChipLogProgress(Inet, "CASE secure channel is available now."); |
| Cleanup(); |
| } |
| } // namespace chip |