blob: 3f872072f749c9f5ad763be7b8801a54d248f193 [file] [log] [blame]
/*
*
* 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 <core/CHIPEncoding.h>
#include <core/CHIPSafeCasts.h>
#include <platform/internal/DeviceNetworkInfo.h>
#include <support/CodeUtils.h>
#include <support/ErrorStr.h>
#include <support/SafeInt.h>
#include <transport/RendezvousSession.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 = 50000;
static const char * kSpake2pKeyExchangeSalt = "SPAKE2P Key Exchange Salt";
using namespace chip::Inet;
using namespace chip::System;
namespace chip {
CHIP_ERROR RendezvousSession::Init(const RendezvousParameters & params)
{
CHIP_ERROR err = CHIP_NO_ERROR;
mParams = params;
VerifyOrExit(mDelegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mParams.HasLocalNodeId(), err = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(mParams.HasSetupPINCode(), err = CHIP_ERROR_INVALID_ARGUMENT);
err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#if CONFIG_NETWORK_LAYER_BLE
{
Transport::BLE * transport = new Transport::BLE();
err = transport->Init(this, mParams);
mTransport = transport;
mTransport->Retain();
transport->Release();
}
#endif // CONFIG_NETWORK_LAYER_BLE
SuccessOrExit(err);
if (!mParams.IsController())
{
err = WaitForPairing(mParams.GetLocalNodeId(), mParams.GetSetupPINCode());
SuccessOrExit(err);
}
mNetworkProvision.Init(this, mDeviceNetworkProvisionDelegate);
exit:
return err;
}
RendezvousSession::~RendezvousSession()
{
if (mTransport)
{
mTransport->Release();
mTransport = nullptr;
}
mDelegate = nullptr;
}
CHIP_ERROR RendezvousSession::SendMessage(System::PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
switch (mCurrentState)
{
case State::kSecurePairing:
err = SendPairingMessage(msgBuf);
break;
case State::kNetworkProvisioning:
err = SendSecureMessage(Protocols::kChipProtocol_NetworkProvisioning,
NetworkProvisioning::MsgTypes::kWiFiAssociationRequest, msgBuf);
break;
default:
err = CHIP_ERROR_INCORRECT_STATE;
break;
};
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
{
OnRendezvousError(err);
}
return err;
}
CHIP_ERROR RendezvousSession::SendPairingMessage(System::PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PacketHeader header;
uint16_t headerSize = 0;
VerifyOrExit(msgBuf != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(msgBuf->Next() == nullptr, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
err = header.Decode(msgBuf->Start(), msgBuf->DataLength(), &headerSize);
SuccessOrExit(err);
msgBuf->ConsumeHead(headerSize);
err = mTransport->SendMessage(header, Header::Flags(), Transport::PeerAddress::BLE(), msgBuf);
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR RendezvousSession::SendSecureMessage(Protocols::CHIPProtocolId protocol, uint8_t msgType, System::PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PacketHeader packetHeader;
PayloadHeader payloadHeader;
MessageAuthenticationCode mac;
const uint16_t headerSize = payloadHeader.EncodeSizeBytes();
uint16_t actualEncodedHeaderSize;
uint8_t * data = nullptr;
uint16_t totalLen = 0;
uint16_t taglen = 0;
VerifyOrExit(msgBuf != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(msgBuf->Next() == nullptr, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
VerifyOrExit(msgBuf->TotalLength() < kMax_SecureSDU_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
VerifyOrExit(CanCastTo<uint16_t>(headerSize + msgBuf->TotalLength()), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
packetHeader
.SetSourceNodeId(mParams.GetLocalNodeId()) //
.SetMessageId(mSecureMessageIndex) //
.SetEncryptionKeyID(mPairingSession.GetLocalKeyId()) //
.SetPayloadLength(static_cast<uint16_t>(headerSize + msgBuf->TotalLength()));
payloadHeader.SetProtocolID(static_cast<uint16_t>(protocol)).SetMessageType(msgType);
VerifyOrExit(msgBuf->EnsureReservedSize(headerSize), err = CHIP_ERROR_NO_MEMORY);
msgBuf->SetStart(msgBuf->Start() - headerSize);
data = msgBuf->Start();
totalLen = msgBuf->TotalLength();
err = payloadHeader.Encode(data, totalLen, &actualEncodedHeaderSize);
SuccessOrExit(err);
err = mSecureSession.Encrypt(data, totalLen, data, packetHeader, payloadHeader.GetEncodePacketFlags(), mac);
SuccessOrExit(err);
err = mac.Encode(packetHeader, &data[totalLen], kMaxTagLen, &taglen);
SuccessOrExit(err);
VerifyOrExit(CanCastTo<uint16_t>(totalLen + taglen), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
msgBuf->SetDataLength(static_cast<uint16_t>(totalLen + taglen));
err = mTransport->SendMessage(packetHeader, payloadHeader.GetEncodePacketFlags(), Transport::PeerAddress::BLE(), msgBuf);
SuccessOrExit(err);
mSecureMessageIndex++;
msgBuf = nullptr;
exit:
return err;
}
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);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Ble, "Failed to initialize a secure session: %s", ErrorStr(err)));
UpdateState(State::kNetworkProvisioning);
exit:
return;
}
void RendezvousSession::OnNetworkProvisioningError(CHIP_ERROR err)
{
OnRendezvousError(err);
}
void RendezvousSession::OnNetworkProvisioningComplete()
{
UpdateState(State::kRendezvousComplete);
}
void RendezvousSession::OnRendezvousConnectionOpened()
{
if (mParams.IsController())
{
CHIP_ERROR err = Pair(mParams.GetLocalNodeId(), mParams.GetSetupPINCode());
VerifyOrExit(err == CHIP_NO_ERROR, OnPairingError(err));
}
exit:
return;
}
void RendezvousSession::OnRendezvousConnectionClosed()
{
if (!mParams.IsController())
{
mSecureSession.Reset();
CHIP_ERROR err = WaitForPairing(mParams.GetLocalNodeId(), mParams.GetSetupPINCode());
VerifyOrExit(err == CHIP_NO_ERROR, OnPairingError(err));
}
exit:
return;
}
void RendezvousSession::OnRendezvousError(CHIP_ERROR err)
{
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);
}
void RendezvousSession::UpdateState(RendezvousSession::State newState)
{
switch (mCurrentState)
{
case State::kSecurePairing:
mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::SecurePairingSuccess, CHIP_NO_ERROR);
break;
case State::kNetworkProvisioning:
mDelegate->OnRendezvousStatusUpdate(RendezvousSessionDelegate::NetworkProvisioningSuccess, CHIP_NO_ERROR);
break;
default:
break;
};
mCurrentState = newState;
}
void RendezvousSession::OnRendezvousMessageReceived(PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
switch (mCurrentState)
{
case State::kSecurePairing:
err = HandlePairingMessage(msgBuf);
break;
case State::kNetworkProvisioning:
err = HandleSecureMessage(msgBuf);
break;
default:
err = CHIP_ERROR_INCORRECT_STATE;
break;
};
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
{
OnRendezvousError(err);
}
}
CHIP_ERROR RendezvousSession::HandlePairingMessage(PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PacketHeader packetHeader;
uint16_t headerSize = 0;
err = packetHeader.Decode(msgBuf->Start(), msgBuf->DataLength(), &headerSize);
SuccessOrExit(err);
msgBuf->ConsumeHead(headerSize);
err = mPairingSession.HandlePeerMessage(packetHeader, msgBuf);
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR RendezvousSession::HandleSecureMessage(PacketBuffer * msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PacketHeader packetHeader;
PayloadHeader payloadHeader;
MessageAuthenticationCode mac;
uint16_t headerSize = 0;
uint8_t * data = nullptr;
uint8_t * plainText = nullptr;
uint16_t len = 0;
uint16_t decodedSize = 0;
uint16_t taglen = 0;
System::PacketBuffer * origMsg = nullptr;
err = packetHeader.Decode(msgBuf->Start(), msgBuf->DataLength(), &headerSize);
SuccessOrExit(err);
msgBuf->ConsumeHead(headerSize);
headerSize = payloadHeader.EncodeSizeBytes();
data = msgBuf->Start();
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 */
origMsg = msgBuf;
msgBuf = PacketBuffer::NewWithAvailableSize(len);
VerifyOrExit(msgBuf != nullptr, err = CHIP_ERROR_NO_MEMORY);
msgBuf->SetDataLength(len, msgBuf);
#endif
plainText = msgBuf->Start();
// TODO: We need length checks here! https://github.com/project-chip/connectedhomeip/issues/2928
err = mac.Decode(packetHeader, &data[packetHeader.GetPayloadLength()], kMaxTagLen, &taglen);
SuccessOrExit(err);
len = static_cast<uint16_t>(len - taglen);
msgBuf->SetDataLength(len);
err = mSecureSession.Decrypt(data, len, plainText, packetHeader, payloadHeader.GetEncodePacketFlags(), mac);
SuccessOrExit(err);
err = payloadHeader.Decode(packetHeader.GetFlags(), plainText, headerSize, &decodedSize);
SuccessOrExit(err);
VerifyOrExit(headerSize == decodedSize, err = CHIP_ERROR_INCORRECT_STATE);
msgBuf->ConsumeHead(headerSize);
if (payloadHeader.GetProtocolID() == Protocols::kChipProtocol_NetworkProvisioning)
{
err = mNetworkProvision.HandleNetworkProvisioningMessage(payloadHeader.GetMessageType(), msgBuf);
SuccessOrExit(err);
}
// else .. TODO once application dependency on this message has been removed, enable the else condition
{
mDelegate->OnRendezvousMessageReceived(msgBuf);
}
msgBuf = nullptr;
exit:
if (origMsg != nullptr)
{
PacketBuffer::Free(origMsg);
}
return err;
}
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(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::SendOperationalCredentials() {}
} // namespace chip