| /* |
| * |
| * Copyright (c) 2020 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 <transport/RendezvousSession.h> |
| |
| #include <core/CHIPEncoding.h> |
| #include <core/CHIPSafeCasts.h> |
| #include <platform/internal/DeviceNetworkInfo.h> |
| #include <support/CHIPMem.h> |
| #include <support/CodeUtils.h> |
| #include <support/ErrorStr.h> |
| #include <support/ReturnMacros.h> |
| #include <support/SafeInt.h> |
| #include <transport/RendezvousSession.h> |
| #include <transport/SecureSessionMgr.h> |
| #include <transport/TransportMgr.h> |
| #include <transport/raw/PeerAddress.h> |
| |
| #if CONFIG_NETWORK_LAYER_BLE |
| #include <transport/BLE.h> |
| #endif // CONFIG_NETWORK_LAYER_BLE |
| |
| static const size_t kMax_SecureSDU_Length = 1024; |
| static constexpr uint32_t kSpake2p_Iteration_Count = 100; |
| static const char * kSpake2pKeyExchangeSalt = "SPAKE2P Key Exchange Salt"; |
| |
| using namespace chip::Inet; |
| using namespace chip::System; |
| using namespace chip::Transport; |
| |
| namespace chip { |
| |
| CHIP_ERROR RendezvousSession::Init(const RendezvousParameters & params, TransportMgrBase * transportMgr) |
| { |
| mParams = params; |
| mTransportMgr = transportMgr; |
| VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mParams.HasSetupPINCode(), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // TODO: BLE Should be a transport, in that case, RendezvousSession and BLE should decouple |
| if (params.GetPeerAddress().GetTransportType() == Transport::Type::kBle) |
| #if CONFIG_NETWORK_LAYER_BLE |
| { |
| Transport::BLE * transport = chip::Platform::New<Transport::BLE>(); |
| mTransport = transport; |
| |
| ReturnErrorOnFailure(transport->Init(this, mParams)); |
| } |
| #else |
| { |
| return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| } |
| #endif // CONFIG_NETWORK_LAYER_BLE |
| |
| if (!mParams.IsController()) |
| { |
| ReturnErrorOnFailure(WaitForPairing(mParams.GetLocalNodeId(), mParams.GetSetupPINCode())); |
| } |
| |
| mNetworkProvision.Init(this); |
| // TODO: We should assmue mTransportMgr not null for IP rendezvous. |
| if (mTransportMgr != nullptr) |
| { |
| mTransportMgr->SetRendezvousSession(this); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| RendezvousSession::~RendezvousSession() |
| { |
| if (mTransport) |
| { |
| chip::Platform::Delete(mTransport); |
| mTransport = nullptr; |
| } |
| |
| mDelegate = nullptr; |
| } |
| |
| CHIP_ERROR RendezvousSession::SendPairingMessage(const PacketHeader & header, const Transport::PeerAddress & peerAddress, |
| System::PacketBuffer * msgIn) |
| { |
| if (mCurrentState != State::kSecurePairing) |
| { |
| PacketBuffer::Free(msgIn); |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| if (peerAddress.GetTransportType() == Transport::Type::kBle) |
| { |
| return mTransport->SendMessage(header, peerAddress, msgIn); |
| } |
| else if (mTransportMgr != nullptr) |
| { |
| return mTransportMgr->SendMessage(header, peerAddress, msgIn); |
| } |
| else |
| { |
| PacketBuffer::Free(msgIn); |
| ChipLogError(Ble, "SendPairingMessage dropped since no transport mgr for IP rendezvous"); |
| return CHIP_ERROR_INVALID_ADDRESS; |
| } |
| } |
| |
| CHIP_ERROR RendezvousSession::SendSecureMessage(Protocols::CHIPProtocolId protocol, uint8_t msgType, System::PacketBuffer * msgIn) |
| { |
| System::PacketBufferHandle msgBuf; |
| |
| msgBuf.Adopt(msgIn); |
| |
| PayloadHeader payloadHeader; |
| payloadHeader.SetProtocolID(static_cast<uint16_t>(protocol)).SetMessageType(msgType); |
| |
| const uint16_t headerSize = payloadHeader.EncodeSizeBytes(); |
| |
| VerifyOrReturnError(!msgBuf.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(!msgBuf->HasChainedBuffer(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| VerifyOrReturnError(msgBuf->TotalLength() < kMax_SecureSDU_Length, CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| VerifyOrReturnError(CanCastTo<uint16_t>(headerSize + msgBuf->TotalLength()), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| |
| PacketHeader packetHeader; |
| packetHeader |
| .SetSourceNodeId(mParams.GetLocalNodeId()) // |
| .SetDestinationNodeId(mParams.GetRemoteNodeId()) // |
| .SetMessageId(mSecureMessageIndex) // |
| .SetEncryptionKeyID(mPairingSession.GetLocalKeyId()) // |
| .SetPayloadLength(static_cast<uint16_t>(headerSize + msgBuf->TotalLength())); |
| |
| VerifyOrReturnError(msgBuf->EnsureReservedSize(headerSize), CHIP_ERROR_NO_MEMORY); |
| |
| msgBuf->SetStart(msgBuf->Start() - headerSize); |
| |
| MessageAuthenticationCode mac; |
| uint16_t actualEncodedHeaderSize = 0; |
| uint8_t * data = msgBuf->Start(); |
| uint16_t totalLen = msgBuf->TotalLength(); |
| |
| ReturnErrorOnFailure(payloadHeader.Encode(data, totalLen, &actualEncodedHeaderSize)); |
| ReturnErrorOnFailure(mSecureSession.Encrypt(data, totalLen, data, packetHeader, mac)); |
| |
| uint16_t taglen = 0; |
| ReturnErrorOnFailure(mac.Encode(packetHeader, &data[totalLen], kMaxTagLen, &taglen)); |
| |
| VerifyOrReturnError(CanCastTo<uint16_t>(totalLen + taglen), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| |
| msgBuf->SetDataLength(static_cast<uint16_t>(totalLen + taglen)); |
| |
| ReturnErrorOnFailure(mTransport->SendMessage(packetHeader, Transport::PeerAddress::BLE(), msgBuf.Release_ForNow())); |
| |
| mSecureMessageIndex++; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void RendezvousSession::OnPairingError(CHIP_ERROR err) |
| { |
| OnRendezvousError(err); |
| } |
| |
| void RendezvousSession::OnPairingComplete() |
| { |
| CHIP_ERROR err = mPairingSession.DeriveSecureSession(reinterpret_cast<const unsigned char *>(kSpake2pI2RSessionInfo), |
| strlen(kSpake2pI2RSessionInfo), mSecureSession); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Ble, "Failed to initialize a secure session: %s", ErrorStr(err)); |
| return; |
| } |
| |
| // TODO: This check of BLE transport should be removed in the future, after we have network provisioning cluster and ble becomes |
| // a transport. |
| if (mParams.GetPeerAddress().GetTransportType() != Transport::Type::kBle || // For rendezvous initializer |
| mPeerAddress.GetTransportType() != Transport::Type::kBle) // For rendezvous target |
| { |
| if (mRendezvousRemoteNodeId.HasValue() && !mParams.HasRemoteNodeId()) |
| { |
| ChipLogProgress(Ble, "Completed rendezvous with %llu", mRendezvousRemoteNodeId.Value()); |
| mParams.SetRemoteNodeId(mRendezvousRemoteNodeId.Value()); |
| } |
| UpdateState(State::kRendezvousComplete); |
| if (!mParams.IsController()) |
| { |
| OnRendezvousConnectionClosed(); |
| } |
| } |
| else |
| { |
| UpdateState(State::kNetworkProvisioning); |
| } |
| } |
| |
| void RendezvousSession::OnNetworkProvisioningError(CHIP_ERROR err) |
| { |
| OnRendezvousError(err); |
| } |
| |
| void RendezvousSession::OnNetworkProvisioningComplete() |
| { |
| UpdateState(State::kRendezvousComplete); |
| } |
| |
| void RendezvousSession::OnRendezvousConnectionOpened() |
| { |
| if (!mParams.IsController()) |
| { |
| return; |
| } |
| |
| CHIP_ERROR err = Pair(mParams.GetLocalNodeId(), mParams.GetSetupPINCode()); |
| if (err != CHIP_NO_ERROR) |
| { |
| OnPairingError(err); |
| } |
| } |
| |
| void RendezvousSession::OnRendezvousConnectionClosed() |
| { |
| if (mParams.IsController()) |
| { |
| return; |
| } |
| |
| mSecureSession.Reset(); |
| mRendezvousRemoteNodeId.ClearValue(); |
| |
| CHIP_ERROR err = WaitForPairing(mParams.GetLocalNodeId(), mParams.GetSetupPINCode()); |
| if (err != CHIP_NO_ERROR) |
| { |
| OnPairingError(err); |
| } |
| } |
| |
| void RendezvousSession::OnRendezvousError(CHIP_ERROR err) |
| { |
| if (mDelegate != nullptr) |
| { |
| switch (mCurrentState) |
| { |
| case State::kSecurePairing: |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::SecurePairingFailed, err); |
| break; |
| |
| case State::kNetworkProvisioning: |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::NetworkProvisioningFailed, err); |
| break; |
| |
| default: |
| break; |
| }; |
| mDelegate->OnRendezvousError(err); |
| } |
| UpdateState(State::kInit, err); |
| } |
| |
| void RendezvousSession::UpdateState(RendezvousSession::State newState, CHIP_ERROR err) |
| { |
| if (mDelegate != nullptr) |
| { |
| switch (mCurrentState) |
| { |
| case State::kSecurePairing: |
| if (newState != State::kInit) |
| { |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::SecurePairingSuccess, err); |
| } |
| else |
| { |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::SecurePairingFailed, err); |
| } |
| break; |
| |
| case State::kNetworkProvisioning: |
| if (newState != State::kInit) |
| { |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::NetworkProvisioningSuccess, err); |
| } |
| else |
| { |
| mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::NetworkProvisioningFailed, err); |
| } |
| break; |
| |
| default: |
| break; |
| }; |
| } |
| mCurrentState = newState; |
| |
| if (newState == State::kRendezvousComplete && mDelegate != nullptr) |
| { |
| mDelegate->OnRendezvousComplete(); |
| } |
| } |
| |
| void RendezvousSession::OnRendezvousMessageReceived(const PacketHeader & packetHeader, const PeerAddress & peerAddress, |
| PacketBufferHandle msgBuf) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| mPeerAddress = peerAddress; |
| // TODO: RendezvousSession should handle SecurePairing messages only |
| |
| switch (mCurrentState) |
| { |
| case State::kSecurePairing: |
| if (packetHeader.GetSourceNodeId().HasValue()) |
| { |
| ChipLogProgress(Ble, "Received rendezvous message from %llu", packetHeader.GetSourceNodeId().Value()); |
| mRendezvousRemoteNodeId.SetValue(packetHeader.GetSourceNodeId().Value()); |
| } |
| err = HandlePairingMessage(packetHeader, peerAddress, std::move(msgBuf)); |
| break; |
| |
| case State::kNetworkProvisioning: |
| err = HandleSecureMessage(packetHeader, peerAddress, std::move(msgBuf)); |
| break; |
| |
| default: |
| err = CHIP_ERROR_INCORRECT_STATE; |
| break; |
| }; |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| OnRendezvousError(err); |
| } |
| } |
| |
| void RendezvousSession::OnMessageReceived(const PacketHeader & header, const Transport::PeerAddress & source, |
| System::PacketBufferHandle msgBuf) |
| { |
| // TODO: OnRendezvousMessageReceived can be renamed to OnMessageReceived after BLE becomes a transport. |
| this->OnRendezvousMessageReceived(header, source, std::move(msgBuf)); |
| } |
| |
| CHIP_ERROR RendezvousSession::HandlePairingMessage(const PacketHeader & packetHeader, const PeerAddress & peerAddress, |
| PacketBufferHandle msgBuf) |
| { |
| return mPairingSession.HandlePeerMessage(packetHeader, peerAddress, std::move(msgBuf)); |
| } |
| |
| CHIP_ERROR RendezvousSession::HandleSecureMessage(const PacketHeader & packetHeader, const PeerAddress & peerAddress, |
| PacketBufferHandle msgBuf) |
| { |
| uint16_t headerSize = 0; |
| uint8_t * plainText = nullptr; |
| uint16_t taglen = 0; |
| uint16_t payloadlen = 0; |
| |
| ReturnErrorCodeIf(msgBuf.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // Check if the source and destination node IDs match with what we already know |
| if (packetHeader.GetDestinationNodeId().HasValue() && mParams.HasLocalNodeId()) |
| { |
| VerifyOrReturnError(packetHeader.GetDestinationNodeId().Value() == mParams.GetLocalNodeId().Value(), |
| CHIP_ERROR_WRONG_NODE_ID); |
| } |
| |
| if (packetHeader.GetSourceNodeId().HasValue() && mParams.HasRemoteNodeId()) |
| { |
| VerifyOrReturnError(packetHeader.GetSourceNodeId().Value() == mParams.GetRemoteNodeId().Value(), CHIP_ERROR_WRONG_NODE_ID); |
| } |
| |
| PayloadHeader payloadHeader; |
| headerSize = payloadHeader.EncodeSizeBytes(); |
| |
| uint8_t * data = msgBuf->Start(); |
| uint16_t len = msgBuf->TotalLength(); |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| /* This is a workaround for the case where PacketBuffer payload is not |
| allocated as an inline buffer to PacketBuffer structure */ |
| System::PacketBufferHandle origMsg; |
| |
| origMsg = std::move(msgBuf); |
| msgBuf = PacketBuffer::NewWithAvailableSize(len); |
| |
| ReturnErrorCodeIf(msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY); |
| |
| msgBuf->SetDataLength(len); |
| #endif |
| plainText = msgBuf->Start(); |
| |
| payloadlen = packetHeader.GetPayloadLength(); |
| VerifyOrReturnError(payloadlen <= len, CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
| |
| MessageAuthenticationCode mac; |
| ReturnErrorOnFailure(mac.Decode(packetHeader, &data[payloadlen], static_cast<uint16_t>(len - payloadlen), &taglen)); |
| |
| len = static_cast<uint16_t>(len - taglen); |
| msgBuf->SetDataLength(len); |
| |
| ReturnErrorOnFailure(mSecureSession.Decrypt(data, len, plainText, packetHeader, mac)); |
| |
| // Use the node IDs from the packet header only after it's successfully decrypted |
| if (packetHeader.GetDestinationNodeId().HasValue() && !mParams.HasLocalNodeId()) |
| { |
| ChipLogProgress(Ble, "Received rendezvous message for %llu", packetHeader.GetDestinationNodeId().Value()); |
| mParams.SetLocalNodeId(packetHeader.GetDestinationNodeId().Value()); |
| } |
| |
| if (packetHeader.GetSourceNodeId().HasValue() && !mParams.HasRemoteNodeId()) |
| { |
| ChipLogProgress(Ble, "Received rendezvous message from %llu", packetHeader.GetSourceNodeId().Value()); |
| mParams.SetRemoteNodeId(packetHeader.GetSourceNodeId().Value()); |
| } |
| |
| uint16_t decodedSize = 0; |
| ReturnErrorOnFailure(payloadHeader.Decode(plainText, len, &decodedSize)); |
| |
| ReturnErrorCodeIf(headerSize != decodedSize, CHIP_ERROR_INCORRECT_STATE); |
| |
| msgBuf->ConsumeHead(headerSize); |
| |
| if (payloadHeader.GetProtocolID() == Protocols::kProtocol_NetworkProvisioning) |
| { |
| ReturnErrorOnFailure(mNetworkProvision.HandleNetworkProvisioningMessage(payloadHeader.GetMessageType(), msgBuf)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR RendezvousSession::WaitForPairing(Optional<NodeId> nodeId, uint32_t setupPINCode) |
| { |
| UpdateState(State::kSecurePairing); |
| return mPairingSession.WaitForPairing(setupPINCode, kSpake2p_Iteration_Count, |
| reinterpret_cast<const unsigned char *>(kSpake2pKeyExchangeSalt), |
| strlen(kSpake2pKeyExchangeSalt), nodeId, 0, this); |
| } |
| |
| CHIP_ERROR RendezvousSession::Pair(Optional<NodeId> nodeId, uint32_t setupPINCode) |
| { |
| UpdateState(State::kSecurePairing); |
| return mPairingSession.Pair(mParams.GetPeerAddress(), setupPINCode, kSpake2p_Iteration_Count, |
| reinterpret_cast<const unsigned char *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt), |
| nodeId, mNextKeyId++, this); |
| } |
| |
| void RendezvousSession::SendNetworkCredentials(const char * ssid, const char * passwd) |
| { |
| mNetworkProvision.SendNetworkCredentials(ssid, passwd); |
| } |
| |
| void RendezvousSession::SendThreadCredentials(const DeviceLayer::Internal::DeviceNetworkInfo & threadData) |
| { |
| mNetworkProvision.SendThreadCredentials(threadData); |
| } |
| |
| void RendezvousSession::SendOperationalCredentials() {} |
| } // namespace chip |