blob: 55869d4758ac0c6835422988324cd1cbf735209e [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* 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.
*/
/**
* @file
* This file implements types and objects for managing Weave session
* security state.
*
*/
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveServerBase.h>
#include <Weave/Profiles/WeaveProfiles.h>
#include "WeaveSecurityMgr.h"
#include <Weave/Support/CodeUtils.h>
#include <Weave/Profiles/common/CommonProfile.h>
#include <Weave/Profiles/common/WeaveMessage.h>
#include <Weave/Profiles/echo/WeaveEcho.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/status-report/StatusReportProfile.h>
#include <Weave/Profiles/service-directory/ServiceDirectory.h>
#include <Weave/Support/crypto/WeaveCrypto.h>
#include <Weave/Support/WeaveFaultInjection.h>
namespace nl {
namespace Weave {
#if WEAVE_CONFIG_SECURITY_MGR_TIME_ALERTS_DUMMY
namespace Platform {
namespace Security {
static inline void OnTimeConsumingCryptoStart()
{
}
static inline void OnTimeConsumingCryptoDone()
{
}
} // namespace Platform
} // namespace Security
#endif // WEAVE_CONFIG_SECURITY_MGR_TIME_ALERTS_DUMMY
using namespace nl::Weave::Profiles;
using namespace nl::Weave::Profiles::Common;
using namespace nl::Weave::Profiles::StatusReporting;
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::Profiles::Security::PASE;
using namespace nl::Weave::Profiles::Security::AppKeys;
using namespace nl::Weave::Encoding;
using namespace nl::Weave::Crypto;
WeaveSecurityManager::WeaveSecurityManager(void)
{
State = kState_NotInitialized;
mSystemLayer = NULL;
}
WEAVE_ERROR WeaveSecurityManager::Init(WeaveExchangeManager& aExchangeMgr, System::Layer& aSystemLayer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (State != kState_NotInitialized)
return WEAVE_ERROR_INCORRECT_STATE;
ExchangeManager = &aExchangeMgr;
mSystemLayer = &aSystemLayer;
SessionEstablishTimeout = WEAVE_CONFIG_DEFAULT_SECURITY_SESSION_ESTABLISHMENT_TIMEOUT;
IdleSessionTimeout = WEAVE_CONFIG_DEFAULT_SECURITY_SESSION_IDLE_TIMEOUT;
FabricState = aExchangeMgr.FabricState;
OnSessionEstablished = NULL;
OnSessionError = NULL;
OnKeyErrorMsgRcvd = NULL;
mEC = NULL;
mCon = NULL;
#if WEAVE_CONFIG_ENABLE_PASE_INITIATOR || WEAVE_CONFIG_ENABLE_PASE_RESPONDER
mPASEEngine = NULL;
#endif
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR || WEAVE_CONFIG_ENABLE_CASE_RESPONDER
mCASEEngine = NULL;
mDefaultAuthDelegate = NULL;
#endif
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR
InitiatorCASEConfig = CASE::kCASEConfig_Config2;
InitiatorCASECurveId = WEAVE_CONFIG_DEFAULT_CASE_CURVE_ID;
InitiatorAllowedCASEConfigs = CASE::kCASEAllowedConfig_Config2|CASE::kCASEAllowedConfig_Config1;
InitiatorAllowedCASECurves = WEAVE_CONFIG_DEFAULT_CASE_ALLOWED_CURVES;
#endif
#if WEAVE_CONFIG_ENABLE_CASE_RESPONDER
ResponderAllowedCASEConfigs = CASE::kCASEAllowedConfig_Config2|CASE::kCASEAllowedConfig_Config1;
ResponderAllowedCASECurves = WEAVE_CONFIG_DEFAULT_CASE_ALLOWED_CURVES;
#endif
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR || WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
mTAKEEngine = NULL;
#endif
#if WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
mDefaultTAKETokenAuthDelegate = NULL;
#endif
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR
mDefaultTAKEChallengerAuthDelegate = NULL;
#endif
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
mKeyExport = NULL;
InitiatorKeyExportConfig = KeyExport::kKeyExportConfig_Config1;
InitiatorAllowedKeyExportConfigs = KeyExport::kKeyExportSupportedConfig_All;
#endif
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
ResponderAllowedKeyExportConfigs = KeyExport::kKeyExportSupportedConfig_All;
#endif
#if WEAVE_CONFIG_SECURITY_TEST_MODE
CASEUseKnownECDHKey = false;
#endif
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR || WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
mDefaultKeyExportDelegate = NULL;
#endif
mStartSecureSession_OnComplete = NULL;
mStartSecureSession_OnError = NULL;
mStartSecureSession_ReqState = NULL;
mRequestedAuthMode = kWeaveAuthMode_NotSpecified;
mSessionKeyId = WeaveKeyId::kNone;
mEncType = kWeaveEncryptionType_None;
mFlags = 0;
err = ExchangeManager->RegisterUnsolicitedMessageHandler(kWeaveProfile_Security, HandleUnsolicitedMessage, this);
SuccessOrExit(err);
aExchangeMgr.MessageLayer->SecurityMgr = this;
State = kState_Idle;
exit:
return err;
}
#if WEAVE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
WEAVE_ERROR WeaveSecurityManager::Init(WeaveExchangeManager* aExchangeMgr, InetLayer* aInetLayer)
{
return aExchangeMgr != NULL && aInetLayer != NULL
? Init(*aExchangeMgr, *aInetLayer->SystemLayer())
: WEAVE_ERROR_INVALID_ARGUMENT;
}
#endif // WEAVE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
WEAVE_ERROR WeaveSecurityManager::Shutdown(void)
{
if (State != kState_NotInitialized)
{
ExchangeManager->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Security);
ExchangeManager = NULL;
// TODO: clean-up in-progress session establishment
Reset();
State = kState_NotInitialized;
}
return WEAVE_NO_ERROR;
}
void WeaveSecurityManager::HandleUnsolicitedMessage(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo,
uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
// Handle Key Error Messages.
if (profileId == kWeaveProfile_Security && msgType == kMsgType_KeyError)
{
secMgr->HandleKeyErrorMsg(ec, msgBuf);
msgBuf = NULL;
ec = NULL;
ExitNow();
}
// Verify that we don't already have a session establishment in progress.
VerifyOrExit(secMgr->State == kState_Idle, err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_SecMgrBusy,
{
secMgr->AsyncNotifySecurityManagerAvailable();
ExitNow(err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
});
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (!ec->HasPeerRequestedAck())
#endif
{
// Reject the request if it did not arrive over a connection.
VerifyOrExit(ec->Con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
}
// Handle messages that mark the beginning of a PASE interaction...
if (profileId == kWeaveProfile_Security && msgType == kMsgType_PASEInitiatorStep1)
{
#if WEAVE_CONFIG_ENABLE_PASE_RESPONDER
// Reject the request if it did not arrive over a connection.
// PASE is not supported over WRMP.
VerifyOrExit(ec->Con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
secMgr->HandlePASESessionStart(ec, pktInfo, msgInfo, msgBuf);
msgBuf = NULL;
#else
ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
#endif
}
// Handle messages that mark the beginning of a CASE interaction...
else if (profileId == kWeaveProfile_Security && msgType == kMsgType_CASEBeginSessionRequest)
{
#if WEAVE_CONFIG_ENABLE_CASE_RESPONDER
secMgr->HandleCASESessionStart(ec, pktInfo, msgInfo, msgBuf);
msgBuf = NULL;
#else
ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
#endif
}
// Handle messages that mark the beginning of a TAKE interaction...
else if (profileId == kWeaveProfile_Security && msgType == kMsgType_TAKEIdentifyToken)
{
#if WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
// Reject the request if it did not arrive over a connection.
// TAKE is not supported over WRMP.
VerifyOrExit(ec->Con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
secMgr->HandleTAKESessionStart(ec, pktInfo, msgInfo, msgBuf);
msgBuf = NULL;
#else
ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
#endif
}
// Handle messages that requests the secret key export...
else if (profileId == kWeaveProfile_Security && msgType == kMsgType_KeyExportRequest)
{
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
secMgr->HandleKeyExportRequest(ec, pktInfo, msgInfo, msgBuf);
msgBuf = NULL;
#else
ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
#endif
}
// Reject all other message types.
else
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (ec != NULL)
{
if (err != WEAVE_NO_ERROR)
SendStatusReport(err, ec);
ec->Release();
}
}
#if WEAVE_CONFIG_ENABLE_PASE_INITIATOR
/**
* This method is called to establish secure PASE session.
*
* @param[in] con A pointer to the WeaveConnection object.
* @param[in] requestedAuthMode The desired means by which the peer should be authenticated.
* This must be one of the PASE auth modes.
* @param[in] reqState A pointer to the requester state.
* @param[in] onComplete A pointer to the callback function, which will be called once
* requested secure session is established.
* @param[in] onError A pointer to the callback function, which will be called if
* requested session establishment fails.
* @param[in] pw A pointer to the PASE secret password.
* @param[in] pwLen Length of the PASE secret password.
*
* @retval #WEAVE_NO_ERROR On success.
*
*/
WEAVE_ERROR WeaveSecurityManager::StartPASESession(WeaveConnection *con, WeaveAuthMode requestedAuthMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
const uint8_t *pw, uint16_t pwLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSessionKey *sessionKey;
bool clearStateOnError = false;
// Verify security manager has been initialized.
VerifyOrExit(State != kState_NotInitialized, err = WEAVE_ERROR_INCORRECT_STATE);
// Verify there is no session in process.
VerifyOrExit(State == kState_Idle, err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_SecMgrBusy,
{
AsyncNotifySecurityManagerAvailable();
ExitNow(err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
});
// Verify correct authentication mode.
VerifyOrExit(IsPASEAuthMode(requestedAuthMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Reject the request if no connection has been specified.
// PASE is not yet supported over WRMP.
VerifyOrExit(con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
State = kState_PASEInProgress;
mRequestedAuthMode = requestedAuthMode;
mEncType = kWeaveEncryptionType_AES128CTRSHA1;
mCon = con;
mStartSecureSession_OnComplete = onComplete;
mStartSecureSession_OnError = onError;
mStartSecureSession_ReqState = reqState;
mSessionKeyId = WeaveKeyId::kNone;
// Any error after this point requires call to the Reset() function.
clearStateOnError = true;
// Allocate an entry in the session key table identified by a random key id. The actual
// key itself will be set once the session is established.
err = FabricState->AllocSessionKey(con->PeerNodeId, WeaveKeyId::kNone, con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(true);
mSessionKeyId = sessionKey->MsgEncKey.KeyId;
// Create a new exchange context.
err = NewSessionExchange(mCon->PeerNodeId, mCon->PeerAddr, mCon->PeerPort);
SuccessOrExit(err);
// Initialize Weave platform memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Allocate and initialize PASE engine object.
mPASEEngine = (WeavePASEEngine *)Platform::Security::MemoryAlloc(sizeof(WeavePASEEngine), true);
VerifyOrExit(mPASEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mPASEEngine->Init();
// Initialize PASE password if provided.
if (pw != NULL)
{
mPASEEngine->Pw = pw;
mPASEEngine->PwLen = pwLen;
}
// Start PASE session.
StartPASESession();
exit:
if (err != WEAVE_NO_ERROR && clearStateOnError)
{
if (mSessionKeyId != WeaveKeyId::kNone)
FabricState->RemoveSessionKey(mSessionKeyId, con->PeerNodeId);
Reset();
}
return err;
}
void WeaveSecurityManager::StartPASESession(void)
{
WEAVE_ERROR err;
err = SendPASEInitiatorStep1(kPASEConfig_ConfigDefault);
SuccessOrExit(err);
mEC->OnMessageReceived = HandlePASEMessageInitiator;
mEC->OnConnectionClosed = HandleConnectionClosed;
// Time limit overall PASE duration.
StartSessionTimer();
exit:
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
}
void WeaveSecurityManager::HandlePASEMessageInitiator(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo,
uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the PASE interaction immediately if we receive a status report message from the responder.
// This is a signal that the responder does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
{
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG1
// Check if Error Status is coming from the old version of PASE code, which only supports Config1
StatusReport rcvdStatusReport;
err = StatusReport::parse(msgBuf, rcvdStatusReport);
SuccessOrExit(err);
if (rcvdStatusReport.mStatusCode == Security::kStatusCode_PASESupportsOnlyConfig1)
{
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendPASEInitiatorStep1(kPASEConfig_Config1);
ExitNow();
}
else
#endif
{
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
}
}
// All other messages must be part of the Security profile.
VerifyOrExit(profileId == kWeaveProfile_Security, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
// Handle the responder's message...
switch (msgType)
{
case kMsgType_PASEResponderReconfigure:
uint32_t newConfig;
err = secMgr->ProcessPASEResponderReconfigure(msgBuf, newConfig);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendPASEInitiatorStep1(newConfig);
SuccessOrExit(err);
break;
case kMsgType_PASEResponderStep1:
err = secMgr->ProcessPASEResponderStep1(msgBuf);
SuccessOrExit(err);
break;
case kMsgType_PASEResponderStep2:
err = secMgr->ProcessPASEResponderStep2(msgBuf);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendPASEInitiatorStep2();
SuccessOrExit(err);
if (secMgr->mPASEEngine->State == WeavePASEEngine::kState_InitiatorDone)
{
err = secMgr->HandleSessionEstablished();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
}
break;
case kMsgType_PASEResponderKeyConfirm:
err = secMgr->ProcessPASEResponderKeyConfirm(msgBuf);
SuccessOrExit(err);
err = secMgr->HandleSessionEstablished();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
break;
}
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEInitiatorStep1(uint32_t paseConfig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
uint8_t pwSource;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Extract the password source from the requested auth mode.
pwSource = PasswordSourceFromAuthMode(mRequestedAuthMode);
// Generate and encode PASE step 1 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->GenerateInitiatorStep1(msgBuf, paseConfig, FabricState->LocalNodeId, mEC->PeerNodeId, mSessionKeyId, kWeaveEncryptionType_AES128CTRSHA1, pwSource, FabricState, true);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Send PASE step 1 message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEInitiatorStep1, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEResponderReconfigure(PacketBuffer* msgBuf, uint32_t &newConfig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Decode and process the responder's reconfigure message.
err = mPASEEngine->ProcessResponderReconfigure(msgBuf, newConfig);
SuccessOrExit(err);
exit:
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEResponderStep1(PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Decode and process the responder's step 1 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->ProcessResponderStep1(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
exit:
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEResponderStep2(PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Decode and process the responder's step 2 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->ProcessResponderStep2(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
exit:
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEInitiatorStep2(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate and encode PASE step 1 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->GenerateInitiatorStep2(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Send PASE step 2 message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEInitiatorStep2, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEResponderKeyConfirm(PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Decode and process the responder's key confirmation message.
err = mPASEEngine->ProcessResponderKeyConfirm(msgBuf);
SuccessOrExit(err);
exit:
return err;
}
#else // !WEAVE_CONFIG_ENABLE_PASE_INITIATOR
WEAVE_ERROR WeaveSecurityManager::StartPASESession(WeaveConnection *con, WeaveAuthMode requestedAuthMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
const uint8_t *pw, uint16_t pwLen)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
#endif // WEAVE_CONFIG_ENABLE_PASE_INITIATOR
#if WEAVE_CONFIG_ENABLE_PASE_RESPONDER
void WeaveSecurityManager::HandlePASESessionStart(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Setup state for the new PASE exchange.
State = kState_PASEInProgress;
mEC = ec;
mCon = ec->Con;
ec->OnMessageReceived = HandlePASEMessageResponder;
ec->OnConnectionClosed = HandleConnectionClosed;
// Ensure the exchange context stays around until we're done with it.
ec->AddRef();
// TODO: rate limit unsuccessful PASE exchanges (WEAVE_ERROR_SECURITY_RATE_LIMIT_EXCEEDED)
// Time limit overall PASE duration.
StartSessionTimer();
// Initialize Weave Platform Memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Prepare PASE engine and start session
mPASEEngine = (WeavePASEEngine *)Platform::Security::MemoryAlloc(sizeof(WeavePASEEngine), true);
VerifyOrExit(mPASEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mPASEEngine->Init();
err = ProcessPASEInitiatorStep1(ec, msgBuf);
// Free the received message buffer so that it can be reused to send the outgoing messages.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Check if ProcessPASEInitiatorStep1 generated Reconfiguration Request
if (err == WEAVE_ERROR_PASE_RECONFIGURE_REQUIRED)
{
err = SendPASEResponderReconfigure();
SuccessOrExit(err);
// Reset state.
Reset();
}
else
{
SuccessOrExit(err);
err = SendPASEResponderStep1();
SuccessOrExit(err);
err = SendPASEResponderStep2();
SuccessOrExit(err);
}
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
}
void WeaveSecurityManager::HandlePASEMessageResponder(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the PASE interaction immediately if we receive a status report message from the initiator.
// This is a signal that the initiator does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
// Otherwise, the only other message expected is an InitiatorStep2.
VerifyOrExit(profileId == kWeaveProfile_Security && msgType == kMsgType_PASEInitiatorStep2,
err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
err = secMgr->ProcessPASEInitiatorStep2(msgBuf);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing messages.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// If performing key confirmation send a responder key confirmation message.
if (secMgr->mPASEEngine->PerformKeyConfirmation)
{
err = secMgr->SendPASEResponderKeyConfirm();
SuccessOrExit(err);
}
// If we've successfully establish a session, go perform the appropriate actions.
if (secMgr->mPASEEngine->State == WeavePASEEngine::kState_ResponderDone)
{
err = secMgr->HandleSessionEstablished();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
}
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEInitiatorStep1(ExchangeContext *ec, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSessionKey *sessionKey;
// Generate and encode PASE step 1 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->ProcessInitiatorStep1(msgBuf, FabricState->LocalNodeId, ec->PeerNodeId, FabricState);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Allocate an entry in the session key table using the key id proposed by the peer.
// Arrange for the session key to be bound to the connection, such that when the connection
// closes, the key is removed.
//
// If the initiator has proposed a key id that already exists, make sure we don't remove the
// existing key during the error clean-up process.
err = FabricState->AllocSessionKey(ec->PeerNodeId, mPASEEngine->SessionKeyId, ec->Con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(false);
sessionKey->SetRemoveOnIdle(false); // TODO FUTURE: Set this to true when support for PASE over WRM is implemented.
// Save the proposed session key id and encryption type.
mSessionKeyId = mPASEEngine->SessionKeyId;
mEncType = mPASEEngine->EncryptionType;
exit:
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEResponderReconfigure(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate PASE reconfigure message.
err = mPASEEngine->GenerateResponderReconfigure(msgBuf);
SuccessOrExit(err);
// Send PASE reconfigure message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEResponderReconfigure, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEResponderStep1(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate PASE step 1 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->GenerateResponderStep1(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Send PASE step 1 message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEResponderStep1, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEResponderStep2(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate PASE step 2 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->GenerateResponderStep2(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Send PASE step 2 message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEResponderStep2, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::ProcessPASEInitiatorStep2(PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Decode and process the initiator's step 2 message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mPASEEngine->ProcessInitiatorStep2(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
exit:
return err;
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendPASEResponderKeyConfirm(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate and encode a key confirmation message.
err = mPASEEngine->GenerateResponderKeyConfirm(msgBuf);
SuccessOrExit(err);
// Send a key confirmation message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_PASEResponderKeyConfirm, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
#endif // WEAVE_CONFIG_ENABLE_PASE_RESPONDER
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR
/**
* This method is called to establish new or find existing CASE session.
*
* @param[in] con A pointer to the WeaveConnection object.
* @param[in] peerNodeId The node identifier of the peer.
* @param[in] peerAddr The IP address of the peer node.
* @param[in] peerPort The port of the peer node.
* @param[in] requestedAuthMode The desired means by which the peer should be authenticated.
* This must be one of the CASE auth modes.
* @param[in] reqState A pointer to the requester state.
* @param[in] onComplete A pointer to the callback function, which will be called once
* requested secure session is established.
* @param[in] onError A pointer to the callback function, which will be called if
* requested session establishment fails.
* @param[in] authDelegate A pointer to the CASE authentication delegate object.
* @param[in] terminatingNodeId The node identifier of the session terminating node.
* When this input is different from kNodeIdNotSpecified that
* indicates that shared secure session was requested.
*
* @retval #WEAVE_NO_ERROR On success.
*
*/
WEAVE_ERROR WeaveSecurityManager::StartCASESession(WeaveConnection *con, uint64_t peerNodeId, const IPAddress &peerAddr,
uint16_t peerPort, WeaveAuthMode requestedAuthMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
WeaveCASEAuthDelegate *authDelegate, uint64_t terminatingNodeId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSessionKey *sessionKey = NULL;
bool clearStateOnError = false;
bool isSharedSession = (terminatingNodeId != kNodeIdNotSpecified);
const uint8_t encType = kWeaveEncryptionType_AES128CTRSHA1; // Only one encryption type supported for now.
// Verify security manager has been initialized.
VerifyOrExit(State != kState_NotInitialized, err = WEAVE_ERROR_INCORRECT_STATE);
// Verify correct authentication mode.
VerifyOrExit(IsCASEAuthMode(requestedAuthMode), err = WEAVE_ERROR_INVALID_ARGUMENT);
// If requested session is shared...
if (isSharedSession)
{
// Search for an established shared session to the specified terminating node that matches
// the requested auth mode and encryption type. If such a session exists...
sessionKey = FabricState->FindSharedSession(terminatingNodeId, requestedAuthMode, encType);
if (sessionKey != NULL)
{
// Add a new end node to the list of end nodes associated with the session.
err = FabricState->AddSharedSessionEndNode(sessionKey, peerNodeId);
SuccessOrExit(err);
// Add a reservation for the session.
ReserveSessionKey(sessionKey);
// Immediately notify the application that the session has been established.
onComplete(this, con, reqState, sessionKey->MsgEncKey.KeyId, peerNodeId, encType);
ExitNow();
}
}
// Verify there is no session in process.
VerifyOrExit(State == kState_Idle, err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_SecMgrBusy,
{
AsyncNotifySecurityManagerAvailable();
ExitNow(err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
});
State = kState_CASEInProgress;
mRequestedAuthMode = requestedAuthMode;
mEncType = encType;
mCon = con;
mStartSecureSession_OnComplete = onComplete;
mStartSecureSession_OnError = onError;
mStartSecureSession_ReqState = reqState;
mSessionKeyId = WeaveKeyId::kNone;
// Any error after that would require state clearing in case of error.
clearStateOnError = true;
// Allocate an entry in the session key table identified by a random key id. The actual
// key itself will be set once the session is established.
err = FabricState->AllocSessionKey((isSharedSession ? terminatingNodeId : peerNodeId),
WeaveKeyId::kNone, con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(true);
sessionKey->SetSharedSession(isSharedSession);
mSessionKeyId = sessionKey->MsgEncKey.KeyId;
// If requested session is shared.
if (isSharedSession)
{
// Add new shared session end node to the record.
err = FabricState->AddSharedSessionEndNode(sessionKey, peerNodeId);
SuccessOrExit(err);
}
// Create a new exchange context.
err = NewSessionExchange((isSharedSession ? terminatingNodeId : peerNodeId), peerAddr, peerPort);
SuccessOrExit(err);
// Initialize Weave Platform Memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Allocate and Initialize CASE Engine object
mCASEEngine = (WeaveCASEEngine *)Platform::Security::MemoryAlloc(sizeof(WeaveCASEEngine), true);
VerifyOrExit(mCASEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mCASEEngine->Init();
// Initialize CASE Authentication Delegate
if (authDelegate == NULL)
authDelegate = mDefaultAuthDelegate;
VerifyOrExit(authDelegate != NULL, err = WEAVE_ERROR_NO_CASE_AUTH_DELEGATE);
mCASEEngine->AuthDelegate = authDelegate;
// Set the allowed CASE configs and ECDH curves.
mCASEEngine->SetAllowedConfigs(InitiatorAllowedCASEConfigs);
mCASEEngine->SetAllowedCurves(InitiatorAllowedCASECurves);
// Set the expected peer certificate type based on the requested authentication mode.
mCASEEngine->SetCertType(CertTypeFromAuthMode(requestedAuthMode));
#if WEAVE_CONFIG_SECURITY_TEST_MODE
mCASEEngine->SetUseKnownECDHKey(CASEUseKnownECDHKey);
#endif
// Start CASE Session using specified initiator parameters.
StartCASESession(InitiatorCASEConfig, InitiatorCASECurveId);
exit:
if (err != WEAVE_NO_ERROR && clearStateOnError)
{
if (sessionKey != NULL)
FabricState->RemoveSessionKey(sessionKey);
Reset();
}
return err;
}
void WeaveSecurityManager::StartCASESession(uint32_t config, uint32_t curveId)
{
WEAVE_ERROR err;
CASE::BeginSessionRequestMessage req;
PacketBuffer* msgBuf = NULL;
uint16_t sendFlags = 0;
// Allocate a buffer to hold the Begin Session message.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate the CASE Begin Session message.
req.Reset();
req.PeerNodeId = mEC->PeerNodeId;
req.ProtocolConfig = config;
mCASEEngine->SetAlternateConfigs(req);
req.CurveId = curveId;
mCASEEngine->SetAlternateCurves(req);
req.PerformKeyConfirm = true;
req.SessionKeyId = mSessionKeyId;
req.EncryptionType = mEncType;
Platform::Security::OnTimeConsumingCryptoStart();
err = mCASEEngine->GenerateBeginSessionRequest(req, msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (mCon == NULL)
{
sendFlags = ExchangeContext::kSendFlag_RequestAck;
}
#endif
// Send the message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_CASEBeginSessionRequest, msgBuf, sendFlags);
msgBuf = NULL;
SuccessOrExit(err);
mEC->OnMessageReceived = HandleCASEMessageInitiator;
mEC->OnConnectionClosed = HandleConnectionClosed;
// Time limit overall CASE duration.
StartSessionTimer();
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
}
void WeaveSecurityManager::HandleCASEMessageInitiator(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
uint16_t sendFlags = 0;
VerifyOrDie(ec == secMgr->mEC);
// Abort the CASE interaction immediately if we receive a status report message from the responder.
// This is a signal that the responder does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
// All other messages must be part of the Security profile.
VerifyOrExit(profileId == kWeaveProfile_Security, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
// If the message is a BeginSessionResponse...
if (msgType == kMsgType_CASEBeginSessionResponse)
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Flush any pending WRM ACKs before we begin the long crypto operation,
// to prevent the peer from re-transmitting the Begin Session response.
err = secMgr->mEC->WRMPFlushAcks();
SuccessOrExit(err);
#endif
// Decode and process the BeginSessionResponse.
{
CASE::BeginSessionResponseMessage resp;
resp.Reset();
resp.PeerNodeId = ec->PeerNodeId;
Platform::Security::OnTimeConsumingCryptoStart();
err = secMgr->mCASEEngine->ProcessBeginSessionResponse(msgBuf, resp);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
}
// Release the buffer containing the response.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// If performing key confirmation...
if (secMgr->mCASEEngine->PerformingKeyConfirm())
{
// Generate and encode an InitiatorKeyConfirm message.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = secMgr->mCASEEngine->GenerateInitiatorKeyConfirm(msgBuf);
SuccessOrExit(err);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (secMgr->mCon == NULL)
{
sendFlags = ExchangeContext::kSendFlag_RequestAck;
}
#endif
// Send the InitiatorKeyConfirm message to the peer.
err = secMgr->mEC->SendMessage(kWeaveProfile_Security, kMsgType_CASEInitiatorKeyConfirm, msgBuf, sendFlags);
msgBuf = NULL;
SuccessOrExit(err);
}
// Initialize the newly established security session.
err = secMgr->HandleSessionEstablished();
SuccessOrExit(err);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Complete the session when any of these is true:
// - session establishment was done over a Weave connection
// - key confirmation wasn't required
// For WRMP when key confirmation is required, the session will be completed
// on one of these events:
// - Received Ack from the peer for the last message on this exchange (CASEInitiatorKeyConfirm)
// - Received first message from the peer encrypted with established session key (mSessionKeyId)
if (secMgr->mCon || !secMgr->mCASEEngine->PerformingKeyConfirm())
#endif
{
secMgr->HandleSessionComplete();
}
}
// Otherwise, if the message is a Reconfigure...
else if (msgType == kMsgType_CASEReconfigure)
{
// Process the reconfigure message. If this proposed alternate configuration is not acceptable,
// the call will fail with an error.
CASE::ReconfigureMessage reconfMsg;
err = secMgr->mCASEEngine->ProcessReconfigure(msgBuf, reconfMsg);
SuccessOrExit(err);
// Release the buffer containing the response.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Create a new exchange context for the new CASE session. This will result in the old exchange context
// being closed. (NOTE: We cannot re-use the initial exchange for the new CASE session because the peer
// believes the exchange ended when the Reconfigure message was sent).
err = secMgr->NewSessionExchange(ec->PeerNodeId, ec->PeerAddr, ec->PeerPort);
SuccessOrExit(err);
// Restart the CASE session using the peer's propose parameters.
secMgr->StartCASESession(reconfMsg.ProtocolConfig, reconfMsg.CurveId);
}
// Fail if the message is unrecognized.
else
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
#else // !WEAVE_CONFIG_ENABLE_CASE_INITIATOR
WEAVE_ERROR WeaveSecurityManager::StartCASESession(WeaveConnection *con, uint64_t peerNodeId, const IPAddress &peerAddr,
uint16_t peerPort, WeaveAuthMode requestedAuthMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
WeaveCASEAuthDelegate *authDelegate, uint64_t terminatingNodeId)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
#endif // WEAVE_CONFIG_ENABLE_CASE_INITIATOR
#if WEAVE_CONFIG_ENABLE_CASE_RESPONDER
void WeaveSecurityManager::HandleCASESessionStart(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, PacketBuffer* msgBuf)
{
WEAVE_ERROR err;
WeaveSessionKey *sessionKey;
CASE::BeginSessionRequestMessage req;
CASE::ReconfigureMessage reconf;
PacketBuffer *respMsgBuf = NULL;
uint16_t sendFlags = 0;
State = kState_CASEInProgress;
mEC = ec;
mCon = ec->Con;
ec->OnMessageReceived = HandleCASEMessageResponder;
ec->OnConnectionClosed = HandleConnectionClosed;
// Ensure the exchange context stays around until we're done with it.
ec->AddRef();
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (mCon == NULL)
{
mEC->OnAckRcvd = WRMPHandleAckRcvd;
mEC->OnSendError = WRMPHandleSendError;
// Flush any pending WRM ACKs before we begin the long crypto operation,
// to prevent the peer from re-transmitting the Begin Session request.
err = mEC->WRMPFlushAcks();
SuccessOrExit(err);
sendFlags |= ExchangeContext::kSendFlag_RequestAck;
}
#endif
// Initialize Weave Platform Memory
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Allocate and initialize a CASE engine.
mCASEEngine = (WeaveCASEEngine *)Platform::Security::MemoryAlloc(sizeof(WeaveCASEEngine), true);
VerifyOrExit(mCASEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mCASEEngine->Init();
// Since this session is being initiated by a remote node, use the default auth delegate.
// Reject the request if no auth delegate has been set.
VerifyOrExit(mDefaultAuthDelegate != NULL, err = WEAVE_ERROR_NO_CASE_AUTH_DELEGATE);
mCASEEngine->AuthDelegate = mDefaultAuthDelegate;
// Set the allowed protocol options for a responder.
mCASEEngine->SetAllowedConfigs(ResponderAllowedCASEConfigs);
mCASEEngine->SetAllowedCurves(ResponderAllowedCASECurves);
mCASEEngine->SetResponderRequiresKeyConfirm(true);
#if WEAVE_CONFIG_SECURITY_TEST_MODE
mCASEEngine->SetUseKnownECDHKey(CASEUseKnownECDHKey);
#endif
// Process the BeginSessionRequest
req.Reset();
req.PeerNodeId = ec->PeerNodeId;
reconf.Reset();
Platform::Security::OnTimeConsumingCryptoStart();
err = mCASEEngine->ProcessBeginSessionRequest(msgBuf, req, reconf);
Platform::Security::OnTimeConsumingCryptoDone();
if (err != WEAVE_ERROR_CASE_RECONFIG_REQUIRED)
SuccessOrExit(err);
// If a reconfigure is required...
if (err == WEAVE_ERROR_CASE_RECONFIG_REQUIRED)
{
// Discard the request buffer.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Encode a CASE Reconfigure message into a new buffer.
respMsgBuf = PacketBuffer::New();
VerifyOrExit(respMsgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = reconf.Encode(respMsgBuf);
SuccessOrExit(err);
// Send the Reconfigure message to the peer.
err = ec->SendMessage(kWeaveProfile_Security, kMsgType_CASEReconfigure, respMsgBuf, sendFlags);
respMsgBuf = NULL;
SuccessOrExit(err);
// Reset the security manager.
Reset();
}
// Otherwise the proposed protocol parameters are acceptable, so...
else
{
// Allocate an entry in the session key table using the key id proposed by the peer.
// If the session is being established over a Weave connection, arrange for the session key to
// be bound to the connection, such that when the connection closes, the key is removed.
// Set the RemoveOnIdle flag so that the session will be automatically removed after a period of
// inactivity (note that this only applies to sessions that are NOT bound to connections).
err = FabricState->AllocSessionKey(ec->PeerNodeId, req.SessionKeyId, ec->Con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(false);
sessionKey->SetRemoveOnIdle(true);
// Save the proposed session key id and encryption type.
mSessionKeyId = req.SessionKeyId;
mEncType = req.EncryptionType;
// Prepare the contents of a BeginSessionResponse message to be sent to the initiator.
CASE::BeginSessionResponseMessage resp;
resp.Reset();
resp.PeerNodeId = ec->PeerNodeId;
resp.ProtocolConfig = req.ProtocolConfig;
resp.CurveId = req.CurveId;
resp.PerformKeyConfirm = true;
// Allocate a buffer to hold the encoded BeginSessionResponse message.
respMsgBuf = PacketBuffer::New();
VerifyOrExit(respMsgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate the BeginSessionResponse message.
Platform::Security::OnTimeConsumingCryptoStart();
err = mCASEEngine->GenerateBeginSessionResponse(resp, respMsgBuf, req);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
// Send the BeginSessionResponse message to the peer.
err = ec->SendMessage(kWeaveProfile_Security, kMsgType_CASEBeginSessionResponse, respMsgBuf, sendFlags);
respMsgBuf = NULL;
SuccessOrExit(err);
// Start a timer to limit the overall duration of session establishment.
StartSessionTimer();
// If the CASE interaction is complete...
// (NOTE: this will only be true if the initiator didn't request key confirmation).
if (mCASEEngine->State == CASE::WeaveCASEEngine::kState_Complete)
{
// Initialize the new session.
err = HandleSessionEstablished();
SuccessOrExit(err);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// 1. Complete the session now if it was established over a connection.
// 2. For WRMP the session will be completed on one of these events:
// - Received Ack from the peer for the last message on this exchange (CASEBeginSessionResponse)
// - Received first message from the peer encrypted with established session key (mSessionKeyId)
if (mCon)
#endif
{
HandleSessionComplete();
}
}
}
exit:
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (respMsgBuf != NULL)
PacketBuffer::Free(respMsgBuf);
}
void WeaveSecurityManager::HandleCASEMessageResponder(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the CASE interaction immediately if we receive a status report message from the initiator.
// This is a signal that the initiator does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
// Otherwise, the only other message expected is an InitiatorKeyConfirm.
VerifyOrExit(profileId == kWeaveProfile_Security && msgType == kMsgType_CASEInitiatorKeyConfirm,
err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Flush any pending WRM ACKs to give sooner notification to the peer that current
// CASE session establishment can be finalized.
err = secMgr->mEC->WRMPFlushAcks();
SuccessOrExit(err);
#endif
// Process the initiator's key confirm message.
// NOTE: No need to initialize crypto memory for this call.
err = secMgr->mCASEEngine->ProcessInitiatorKeyConfirm(msgBuf);
SuccessOrExit(err);
// At this point the session is established.
err = secMgr->HandleSessionEstablished();
SuccessOrExit(err);
// Complete the session and notify the user.
secMgr->HandleSessionComplete();
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
#endif // WEAVE_CONFIG_ENABLE_CASE_RESPONDER
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR
/**
* This method is called to establish secure TAKE session.
*
* @param[in] con A pointer to the WeaveConnection object.
* @param[in] requestedAuthMode The desired means by which the peer should be authenticated.
* This must be one of the TAKE authentication modes.
* @param[in] reqState A pointer to the requester state.
* @param[in] onComplete A pointer to the callback function, which will be called once
* requested secure session is established.
* @param[in] onError A pointer to the callback function, which will be called if
* requested session establishment fails.
* @param[in] encryptAuthPhase A boolean flag that indicates whether protocol authentication
* phase should be encrypted.
* @param[in] encryptCommPhase A boolean flag that indicates whether protocol communication
* phase should be encrypted.
* @param[in] timeLimitedIK A boolean flag that indicates whether Identification Key (IK)
* is time limited.
* @param[in] sendChallengerId A boolean flag that indicates whether challenger identification
* should be included in the message. If it is not included the
* Weave node ID value is used as a challenger ID.
* @param[in] authDelegate A pointer to the TAKE challenger authentication delegate object.
*
* @retval #WEAVE_NO_ERROR On success.
*
*/
WEAVE_ERROR WeaveSecurityManager::StartTAKESession(WeaveConnection *con, WeaveAuthMode requestedAuthMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
bool encryptAuthPhase, bool encryptCommPhase,
bool timeLimitedIK, bool sendChallengerId,
WeaveTAKEChallengerAuthDelegate *authDelegate)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
bool useSessionKeyID = encryptAuthPhase || encryptCommPhase;
bool clearStateOnError = false;
// Verify security manager has been initialized.
VerifyOrExit(State != kState_NotInitialized, err = WEAVE_ERROR_INCORRECT_STATE);
// Verify there is no session in process.
VerifyOrExit(State == kState_Idle, err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_SecMgrBusy,
{
AsyncNotifySecurityManagerAvailable();
ExitNow(err = WEAVE_ERROR_SECURITY_MANAGER_BUSY);
});
// Verify correct authentication mode (only one supported currently).
VerifyOrExit(requestedAuthMode == kWeaveAuthMode_TAKE_IdentificationKey, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Reject the request if no connection has been specified.
VerifyOrExit(con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
State = kState_TAKEInProgress;
mRequestedAuthMode = requestedAuthMode;
mEncType = kWeaveEncryptionType_AES128CTRSHA1;
mCon = con;
mStartSecureSession_OnComplete = onComplete;
mStartSecureSession_OnError = onError;
mStartSecureSession_ReqState = reqState;
mSessionKeyId = WeaveKeyId::kNone;
// Any error after this point requires call to the Reset() function.
clearStateOnError = true;
// Allocate an entry in the session key table identified by a random key id. The actual
// key itself will be set once the session is established.
if (useSessionKeyID)
{
WeaveSessionKey *sessionKey;
err = FabricState->AllocSessionKey(con->PeerNodeId, WeaveKeyId::kNone, con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(true);
mSessionKeyId = sessionKey->MsgEncKey.KeyId;
}
// Create a new exchange context.
err = NewSessionExchange(mCon->PeerNodeId, mCon->PeerAddr, mCon->PeerPort);
SuccessOrExit(err);
// Initialize Weave platform memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Allocate and initialize TAKE engine object.
mTAKEEngine = (WeaveTAKEEngine *)Platform::Security::MemoryAlloc(sizeof(WeaveTAKEEngine), true);
VerifyOrExit(mTAKEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mTAKEEngine->Init();
if (authDelegate == NULL)
authDelegate = mDefaultTAKEChallengerAuthDelegate;
VerifyOrExit(authDelegate != NULL, err = WEAVE_ERROR_NO_TAKE_AUTH_DELEGATE);
mTAKEEngine->ChallengerAuthDelegate = authDelegate;
// Start TAKE session.
StartTAKESession(encryptAuthPhase, encryptCommPhase, timeLimitedIK, sendChallengerId);
exit:
if (err != WEAVE_NO_ERROR && clearStateOnError)
{
FabricState->RemoveSessionKey(mSessionKeyId, con->PeerNodeId);
Reset();
}
return err;
}
void WeaveSecurityManager::StartTAKESession(bool encryptAuthPhase, bool encryptCommPhase, bool timeLimitedIK, bool sendChallengerId)
{
WEAVE_ERROR err;
err = SendTAKEIdentifyToken(TAKE::kTAKEConfig_Config1, encryptAuthPhase, encryptCommPhase, timeLimitedIK, sendChallengerId);
SuccessOrExit(err);
mEncType = mTAKEEngine->GetEncryptionType();
mEC->OnMessageReceived = HandleTAKEMessageInitiator;
mEC->OnConnectionClosed = HandleConnectionClosed;
// Using a smaller timeout may help prevent Relay Attack.
// TODO: consider reducing the timeout, and using different values of timeout
// for first and subsequent authentication.
StartSessionTimer();
exit:
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
}
void WeaveSecurityManager::HandleTAKEMessageInitiator(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo,
uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the TAKE interaction immediately if we receive a status report message from the responder.
// This is a signal that the responder does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
{
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
}
// All other messages must be part of the Security profile.
VerifyOrExit(profileId == kWeaveProfile_Security, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
// Handle the responder's message...
switch (msgType)
{
case kMsgType_TAKEIdentifyTokenResponse:
{
err = secMgr->ProcessTAKEIdentifyTokenResponse(msgBuf);
bool doReauth = err == WEAVE_ERROR_TAKE_REAUTH_POSSIBLE;
if (!doReauth)
SuccessOrExit(err);
if (secMgr->mTAKEEngine->IsEncryptAuthPhase())
{
err = secMgr->CreateTAKESecureSession();
SuccessOrExit(err);
}
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
if (doReauth)
{
err = secMgr->SendTAKEReAuthenticateToken();
}
else
{
err = secMgr->SendTAKEAuthenticateToken();
}
SuccessOrExit(err);
break;
}
case kMsgType_TAKETokenReconfigure:
uint8_t newConfig;
err = secMgr->ProcessTAKETokenReconfigure(newConfig, msgBuf);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendTAKEIdentifyToken(newConfig, secMgr->mTAKEEngine->IsEncryptAuthPhase(),
secMgr->mTAKEEngine->IsEncryptCommPhase(), secMgr->mTAKEEngine->IsTimeLimitedIK(), secMgr->mTAKEEngine->HasSentChallengerId());
SuccessOrExit(err);
break;
case kMsgType_TAKEAuthenticateTokenResponse:
err = secMgr->ProcessTAKEAuthenticateTokenResponse(msgBuf);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->FinishTAKESetUp();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
break;
case kMsgType_TAKEReAuthenticateTokenResponse:
err = secMgr->ProcessTAKEReAuthenticateTokenResponse(msgBuf);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->FinishTAKESetUp();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
break;
}
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
WEAVE_ERROR WeaveSecurityManager::SendTAKEIdentifyToken(uint8_t takeConfig, bool encryptAuthPhase, bool encryptCommPhase, bool timeLimitedIK, bool sendChallengerId)
{
WEAVE_ERROR err;
PacketBuffer* msgBuf = NULL;
/// Generate, encode and send our Token Identify message.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mTAKEEngine->GenerateIdentifyTokenMessage(mSessionKeyId, takeConfig, encryptAuthPhase, encryptCommPhase, timeLimitedIK, sendChallengerId, kWeaveEncryptionType_AES128CTRSHA1, FabricState->LocalNodeId, msgBuf);
SuccessOrExit(err);
// Send the message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKEIdentifyToken, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKEIdentifyTokenResponse(const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = mTAKEEngine->ProcessIdentifyTokenResponseMessage(msgBuf);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKETokenReconfigure(uint8_t& config, const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = mTAKEEngine->ProcessTokenReconfigureMessage(config, msgBuf);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::SendTAKEAuthenticateToken(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
Platform::Security::OnTimeConsumingCryptoStart();
err = mTAKEEngine->GenerateAuthenticateTokenMessage(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKEAuthenticateToken, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKEAuthenticateTokenResponse(const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Platform::Security::OnTimeConsumingCryptoStart();
err = mTAKEEngine->ProcessAuthenticateTokenResponseMessage(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::SendTAKEReAuthenticateToken(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mTAKEEngine->GenerateReAuthenticateTokenMessage(msgBuf);
SuccessOrExit(err);
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKEReAuthenticateToken, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKEReAuthenticateTokenResponse(const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = mTAKEEngine->ProcessReAuthenticateTokenResponseMessage(msgBuf);
SuccessOrExit(err);
exit:
return err;
}
#else // !WEAVE_CONFIG_ENABLE_TAKE_INITIATOR
WEAVE_ERROR WeaveSecurityManager::StartTAKESession(WeaveConnection *con, WeaveAuthMode authMode, void *reqState,
SessionEstablishedFunct onComplete, SessionErrorFunct onError,
bool encryptAuthPhase, bool encryptCommPhase,
bool timeLimitedIK, bool sendChallengerId,
WeaveTAKEChallengerAuthDelegate *authDelegate)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
#endif // WEAVE_CONFIG_ENABLE_TAKE_INITIATOR
#if WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
void WeaveSecurityManager::HandleTAKESessionStart(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* respMsgBuf = NULL;
VerifyOrExit(mDefaultTAKETokenAuthDelegate != NULL, err = WEAVE_ERROR_NO_TAKE_AUTH_DELEGATE);
// Setup state for the new TAKE exchange.
State = kState_TAKEInProgress;
mEC = ec;
mCon = ec->Con;
ec->OnMessageReceived = HandleTAKEMessageResponder;
ec->OnConnectionClosed = HandleConnectionClosed;
// Ensure the exchange context stays around until we're done with it.
ec->AddRef();
StartSessionTimer();
// Initialize Weave Platform Memory
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Prepare TAKE engine and start session
mTAKEEngine = (WeaveTAKEEngine *)Platform::Security::MemoryAlloc(sizeof(WeaveTAKEEngine), true);
VerifyOrExit(mTAKEEngine != NULL, err = WEAVE_ERROR_NO_MEMORY);
mTAKEEngine->Init();
mTAKEEngine->TokenAuthDelegate = mDefaultTAKETokenAuthDelegate;
err = mTAKEEngine->ProcessIdentifyTokenMessage(ec->PeerNodeId, msgBuf);
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
if (err == WEAVE_ERROR_TAKE_RECONFIGURE_REQUIRED)
{
err = SendTAKETokenReconfigure();
SuccessOrExit(err);
// Reset state.
Reset();
ExitNow();
}
SuccessOrExit(err);
if (mTAKEEngine->UseSessionKey())
{
WeaveSessionKey *sessionKey;
err = FabricState->AllocSessionKey(ec->PeerNodeId, mTAKEEngine->SessionKeyId, ec->Con, sessionKey);
SuccessOrExit(err);
sessionKey->SetLocallyInitiated(false);
sessionKey->SetRemoveOnIdle(true);
mSessionKeyId = mTAKEEngine->SessionKeyId;
mEncType = mTAKEEngine->GetEncryptionType();
}
respMsgBuf = PacketBuffer::New();
VerifyOrExit(respMsgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mTAKEEngine->GenerateIdentifyTokenResponseMessage(respMsgBuf);
SuccessOrExit(err);
err = ec->SendMessage(kWeaveProfile_Security, kMsgType_TAKEIdentifyTokenResponse, respMsgBuf);
SuccessOrExit(err);
if (mTAKEEngine->IsEncryptAuthPhase())
{
err = CreateTAKESecureSession();
SuccessOrExit(err);
}
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (err != WEAVE_NO_ERROR)
HandleSessionError(err, NULL);
}
void WeaveSecurityManager::HandleTAKEMessageResponder(ExchangeContext *ec, const IPPacketInfo *pktInfo,
const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the TAKE interaction immediately if we receive a status report message from the initiator.
// This is a signal that the initiator does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
// Otherwise, the only other message expected is of profile security
VerifyOrExit(profileId == kWeaveProfile_Security, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
switch (msgType)
{
case kMsgType_TAKEAuthenticateToken:
err = secMgr->ProcessTAKEAuthenticateToken(msgBuf);
SuccessOrExit(err);
err = secMgr->SendTAKEAuthenticateTokenResponse();
SuccessOrExit(err);
// freeing the buffer after the generation of the next message in order to not copy the gx array
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->FinishTAKESetUp();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
break;
case kMsgType_TAKEReAuthenticateToken:
err = secMgr->ProcessTAKEReAuthenticateToken(msgBuf);
SuccessOrExit(err);
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendTAKEReAuthenticateTokenResponse();
SuccessOrExit(err);
err = secMgr->FinishTAKESetUp();
SuccessOrExit(err);
secMgr->HandleSessionComplete();
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
}
// Free the received message buffer so that it can be reused to send the outgoing messages.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleSessionError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKEAuthenticateToken(const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Platform::Security::OnTimeConsumingCryptoStart();
err = mTAKEEngine->ProcessAuthenticateTokenMessage(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::SendTAKETokenReconfigure(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
// Generate, encode and send reconfigure message.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mTAKEEngine->GenerateTokenReconfigureMessage(msgBuf);
SuccessOrExit(err);
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKETokenReconfigure, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
WEAVE_ERROR WeaveSecurityManager::SendTAKEAuthenticateTokenResponse(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
Platform::Security::OnTimeConsumingCryptoStart();
err = mTAKEEngine->GenerateAuthenticateTokenResponseMessage(msgBuf);
Platform::Security::OnTimeConsumingCryptoDone();
SuccessOrExit(err);
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKEAuthenticateTokenResponse, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
WEAVE_ERROR WeaveSecurityManager::ProcessTAKEReAuthenticateToken(const PacketBuffer* msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = mTAKEEngine->ProcessReAuthenticateTokenMessage(msgBuf);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::SendTAKEReAuthenticateTokenResponse(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* msgBuf = NULL;
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mTAKEEngine->GenerateReAuthenticateTokenResponseMessage(msgBuf);
SuccessOrExit(err);
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_TAKEReAuthenticateTokenResponse, msgBuf, 0);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
#endif // WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR || WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
WEAVE_ERROR WeaveSecurityManager::CreateTAKESecureSession(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = HandleSessionEstablished();
SuccessOrExit(err);
mEC->KeyId = mSessionKeyId;
mEC->EncryptionType = mEncType;
exit:
return err;
}
WEAVE_ERROR WeaveSecurityManager::FinishTAKESetUp(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (mTAKEEngine->IsEncryptCommPhase())
{
err = HandleSessionEstablished();
SuccessOrExit(err);
}
else
{
if (mTAKEEngine->IsEncryptAuthPhase())
{
err = FabricState->RemoveSessionKey(mSessionKeyId, mEC->PeerNodeId);
SuccessOrExit(err);
}
mEncType = kWeaveEncryptionType_None;
mSessionKeyId = WeaveKeyId::kNone;
}
exit:
return err;
}
#endif // WEAVE_CONFIG_ENABLE_TAKE_INITIATOR || WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
WEAVE_ERROR WeaveSecurityManager::StartKeyExport(WeaveConnection *con, uint64_t peerNodeId, const IPAddress &peerAddr, uint16_t peerPort,
uint32_t keyId, bool signMessage, void *reqState,
KeyExportCompleteFunct onComplete, KeyExportErrorFunct onError, WeaveKeyExportDelegate *keyExportDelegate)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Verify we've been initialized and that we don't already have a session in process.
if (State == kState_NotInitialized)
return WEAVE_ERROR_INCORRECT_STATE;
if (State != kState_Idle)
return WEAVE_ERROR_SECURITY_MANAGER_BUSY;
State = kState_KeyExportInProgress;
mCon = con;
// Create a new exchange context.
err = NewSessionExchange(peerNodeId, peerAddr, peerPort);
SuccessOrExit(err);
// Initialize key export delegate.
if (keyExportDelegate == NULL)
keyExportDelegate = mDefaultKeyExportDelegate;
// Initialize Weave Platform Memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Allocate and initialize KeyExport object.
mKeyExport = (WeaveKeyExport *)Platform::Security::MemoryAlloc(sizeof(WeaveKeyExport), true);
VerifyOrExit(mKeyExport != NULL, err = WEAVE_ERROR_NO_MEMORY);
mKeyExport->Init(keyExportDelegate);
// Set the allowed key export protocol configurations.
mKeyExport->SetAllowedConfigs(InitiatorAllowedKeyExportConfigs);
// Send key export request message.
err = SendKeyExportRequest(InitiatorKeyExportConfig, keyId, signMessage);
SuccessOrExit(err);
mStartKeyExport_OnComplete = onComplete;
mStartKeyExport_OnError = onError;
mStartKeyExport_ReqState = reqState;
mEC->OnMessageReceived = HandleKeyExportMessageInitiator;
mEC->OnConnectionClosed = HandleConnectionClosed;
// Time limit overall Key Export duration.
StartSessionTimer();
exit:
if (err != WEAVE_NO_ERROR)
HandleKeyExportError(err, NULL);
return err;
}
void WeaveSecurityManager::HandleKeyExportMessageInitiator(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo,
uint32_t profileId, uint8_t msgType, PacketBuffer *msgBuf)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
VerifyOrDie(ec == secMgr->mEC);
// Abort the key export interaction immediately if we receive a status report message from the responder.
// This is a signal that the responder does not want to continue.
if (profileId == kWeaveProfile_Common && msgType == kMsgType_StatusReport)
{
ExitNow(err = WEAVE_ERROR_STATUS_REPORT_RECEIVED);
}
// All other messages must be part of the Security profile.
VerifyOrExit(profileId == kWeaveProfile_Security, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
// Flush any pending WRM ACKs before we begin the long crypto operation,
// to prevent the peer from re-transmitting message.
err = secMgr->mEC->WRMPFlushAcks();
SuccessOrExit(err);
#endif
// Handle the responder's message...
switch (msgType)
{
case kMsgType_KeyExportReconfigure:
uint8_t newConfig;
err = secMgr->mKeyExport->ProcessKeyExportReconfigure(msgBuf->Start(), msgBuf->DataLength(), newConfig);
SuccessOrExit(err);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
err = secMgr->SendKeyExportRequest(newConfig, secMgr->mKeyExport->KeyId, secMgr->mKeyExport->SignMessages);
SuccessOrExit(err);
break;
case kMsgType_KeyExportResponse:
uint32_t exportedKeyId;
uint16_t exportedKeyLen;
uint8_t exportedKey[kWeaveFabricSecretSize];
err = secMgr->mKeyExport->ProcessKeyExportResponse(msgBuf->Start(), msgBuf->DataLength(), pktInfo, msgInfo,
exportedKey, sizeof(exportedKey), exportedKeyLen, exportedKeyId);
SuccessOrExit(err);
// Call the user's completion function.
if (secMgr->mStartKeyExport_OnComplete != NULL)
{
secMgr->mStartKeyExport_OnComplete(secMgr, secMgr->mCon, secMgr->mStartKeyExport_ReqState, exportedKeyId, exportedKey, exportedKeyLen);
}
// Reset state.
secMgr->Reset();
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
break;
}
exit:
if (err != WEAVE_NO_ERROR)
secMgr->HandleKeyExportError(err, (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? msgBuf : NULL);
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
}
void WeaveSecurityManager::HandleKeyExportError(WEAVE_ERROR err, PacketBuffer *statusReportMsgBuf)
{
// If session establishment in progress...
//
// NOTE: This check is necessary because it is possible for HandleKeyExportError() to be
// called twice in certain situations: If a call to ExchangeContext::SendMessage() fails
// because the underlying connection has been closed, it will trigger a callback, while
// in the SendMessage() function, to HandleConnectionClosed() which calls this function.
// Then when SendMessage() returns, the function that called it will also call this
// function with the error returned by SendMessage().
//
if (State != kState_Idle)
{
WeaveConnection *con = mCon;
KeyExportErrorFunct userOnError = mStartKeyExport_OnError;
void *reqState = mStartKeyExport_ReqState;
StatusReport rcvdStatusReport;
StatusReport *statusReportPtr = NULL;
// If a status report was received from the peer, parse it and arrange to pass it
// to the callbacks.
if (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED)
{
WEAVE_ERROR parseErr = StatusReport::parse(statusReportMsgBuf, rcvdStatusReport);
if (parseErr == WEAVE_NO_ERROR)
statusReportPtr = &rcvdStatusReport;
else
err = parseErr;
}
// Reset state.
Reset();
// Call the user's error handler.
if (userOnError != NULL)
userOnError(this, con, reqState, err, statusReportPtr);
}
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendKeyExportRequest(uint8_t keyExportConfig, uint32_t keyId, bool signMessage)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer *msgBuf = NULL;
uint16_t dataLen;
uint16_t sendFlags = 0;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate key export request.
err = mKeyExport->GenerateKeyExportRequest(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen, keyExportConfig, keyId, signMessage);
SuccessOrExit(err);
// Set message length.
msgBuf->SetDataLength(dataLen);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (mCon == NULL)
{
sendFlags = ExchangeContext::kSendFlag_RequestAck;
}
#endif
// Send key export request message.
err = mEC->SendMessage(kWeaveProfile_Security, kMsgType_KeyExportRequest, msgBuf, sendFlags);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
#endif // WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
void WeaveSecurityManager::HandleKeyExportRequest(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{
WEAVE_ERROR err;
WeaveKeyExport keyExport;
State = kState_KeyExportInProgress;
mEC = ec;
mCon = ec->Con;
// Ensure the exchange context stays around until we're done with it.
ec->AddRef();
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (mCon == NULL)
{
// Do nothing on the Ack received from the requestor.
// mEC->OnAckRcvd is not initialized.
// Do nothing on the message send error.
// mEC->OnSendError is not initialized.
// Flush any pending WRM ACKs before we begin the long crypto operation,
// to prevent the peer from re-transmitting the Key Export request.
err = mEC->WRMPFlushAcks();
SuccessOrExit(err);
}
#endif
// Initialize Weave Platform Memory.
err = Platform::Security::MemoryInit();
SuccessOrExit(err);
// Prepare key export engine.
keyExport.Init(mDefaultKeyExportDelegate, FabricState->GroupKeyStore);
// Set the allowed key export protocol configurations.
keyExport.SetAllowedConfigs(ResponderAllowedKeyExportConfigs);
// Process key export request message.
err = keyExport.ProcessKeyExportRequest(msgBuf->Start(), msgBuf->DataLength(), pktInfo, msgInfo);
// Free the received message buffer so that it can be reused to send the outgoing message.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Check if reconfiguration was requested.
if (err == WEAVE_ERROR_KEY_EXPORT_RECONFIGURE_REQUIRED)
{
err = SendKeyExportResponse(keyExport, kMsgType_KeyExportReconfigure);
}
else if (err == WEAVE_NO_ERROR)
{
err = SendKeyExportResponse(keyExport, kMsgType_KeyExportResponse);
}
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
// Send status report message in case of an error.
if (err != WEAVE_NO_ERROR)
SendStatusReport(err, ec);
// Shutdown and clean key export engine memory.
keyExport.Shutdown();
// Reset state.
Reset();
}
__attribute__((noinline))
WEAVE_ERROR WeaveSecurityManager::SendKeyExportResponse(WeaveKeyExport& keyExport, uint8_t msgType)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer *msgBuf = NULL;
uint16_t dataLen;
uint16_t sendFlags = 0;
// Allocate buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Generate key export reconfigure or response message.
if (msgType == kMsgType_KeyExportReconfigure)
err = keyExport.GenerateKeyExportReconfigure(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen);
else if (msgType == kMsgType_KeyExportResponse)
err = keyExport.GenerateKeyExportResponse(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen);
else
err = WEAVE_ERROR_INVALID_MESSAGE_TYPE;
SuccessOrExit(err);
// Set message length.
msgBuf->SetDataLength(dataLen);
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
if (mCon == NULL)
{
sendFlags = ExchangeContext::kSendFlag_RequestAck;
}
#endif
// Send key export response message.
err = mEC->SendMessage(kWeaveProfile_Security, msgType, msgBuf, sendFlags);
msgBuf = NULL;
SuccessOrExit(err);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
return err;
}
#endif // WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
/**
* Checks if the specified Weave error code is one of the key error codes.
* This function is called to determine whether key error message should be sent
* to the initiator of the message that failed to find a correct key during decoding.
*
* @param[in] err A Weave error code.
*
* @retval true If specified Weave error code is a key error.
* @retval false Otherwise.
*
*/
bool WeaveSecurityManager::IsKeyError(WEAVE_ERROR err)
{
return (err == WEAVE_ERROR_KEY_NOT_FOUND ||
err == WEAVE_ERROR_WRONG_ENCRYPTION_TYPE ||
err == WEAVE_ERROR_UNKNOWN_KEY_TYPE ||
err == WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY ||
err == WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE);
}
/**
* Send key error message.
* This function is called when received Weave message decoding fails due to key error.
*
* @param[in] rcvdMsgInfo A pointer to the message information for the received Weave message.
*
* @param[in] rcvdMsgPacketInfo A pointer to the IPPacketInfo object of the received Weave message.
*
* @param[in] con A pointer to the WeaveConnection object.
*
* @param[in] keyErr Weave key error code.
*
* @retval #WEAVE_ERROR_NO_MEMORY If memory could not be allocated for the new
* exchange context or new message buffer.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL If buffer is too small
* @retval #WEAVE_NO_ERROR If the method succeeded.
*
*/
WEAVE_ERROR WeaveSecurityManager::SendKeyErrorMsg(WeaveMessageInfo *rcvdMsgInfo, const IPPacketInfo *rcvdMsgPacketInfo, WeaveConnection *con, WEAVE_ERROR keyErr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext* ec = NULL;
PacketBuffer* msgBuf = NULL;
uint8_t* p;
uint16_t keyErrCode;
// Create new exchange context.
if (con == NULL)
{
VerifyOrExit(rcvdMsgPacketInfo != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
ec = ExchangeManager->NewContext(rcvdMsgInfo->SourceNodeId, rcvdMsgPacketInfo->SrcAddress, rcvdMsgPacketInfo->SrcPort, rcvdMsgPacketInfo->Interface, this);
}
else
{
ec = ExchangeManager->NewContext(con, this);
}
VerifyOrExit(ec != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Encode key error status code.
switch (keyErr)
{
case WEAVE_ERROR_KEY_NOT_FOUND:
keyErrCode = kStatusCode_KeyNotFound;
break;
case WEAVE_ERROR_WRONG_ENCRYPTION_TYPE:
keyErrCode = kStatusCode_WrongEncryptionType;
break;
case WEAVE_ERROR_UNKNOWN_KEY_TYPE:
keyErrCode = kStatusCode_UnknownKeyType;
break;
case WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY:
keyErrCode = kStatusCode_InvalidUseOfSessionKey;
break;
case WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE:
keyErrCode = kStatusCode_UnsupportedEncryptionType;
break;
default:
keyErrCode = kStatusCode_InternalKeyError;
break;
}
// Allocate new buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Verify that the buffer is big enough for this message.
VerifyOrExit(msgBuf->AvailableDataLength() >= kWeaveKeyErrorMessageSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Initialize pointer to the buffer payload start.
p = msgBuf->Start();
// Write the key Id field (2 bytes).
LittleEndian::Write16(p, rcvdMsgInfo->KeyId);
// Write the encryption type field (1 byte).
Write8(p, rcvdMsgInfo->EncryptionType);
// Write the message Id field (4 bytes).
LittleEndian::Write32(p, rcvdMsgInfo->MessageId);
// Write the key error code field (2 bytes).
LittleEndian::Write16(p, keyErrCode);
// Set message length.
msgBuf->SetDataLength(kWeaveKeyErrorMessageSize);
// Send key error message.
err = ec->SendMessage(kWeaveProfile_Security, kMsgType_KeyError, msgBuf, 0);
msgBuf = NULL;
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (ec != NULL)
ec->Close();
return err;
}
void WeaveSecurityManager::HandleKeyErrorMsg(ExchangeContext *ec, PacketBuffer* msgBuf)
{
WEAVE_ERROR err;
WeaveSessionKey *sessionKey;
uint8_t *p = msgBuf->Start();
uint64_t srcNodeId = ec->PeerNodeId;
uint16_t keyId;
uint8_t encType;
uint16_t keyErrCode;
uint32_t messageId;
WEAVE_ERROR keyErr;
uint64_t endNodeIds[WEAVE_CONFIG_MAX_END_NODES_PER_SHARED_SESSION + 1];
uint8_t endNodeIdsCount = 0;
// Verify correct message size.
if (msgBuf->DataLength() != kWeaveKeyErrorMessageSize)
ExitNow();
// Read the message fields.
keyId = LittleEndian::Read16(p);
encType = Read8(p);
messageId = LittleEndian::Read32(p);
keyErrCode = LittleEndian::Read16(p);
// Free the received message buffer so that it can be reused to initiate additional communication.
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Close exchange context that delivered key error message.
ec->Close();
ec = NULL;
// Decode error status code.
switch (keyErrCode)
{
case kStatusCode_KeyNotFound:
keyErr = WEAVE_ERROR_KEY_NOT_FOUND_FROM_PEER;
break;
case kStatusCode_WrongEncryptionType:
keyErr = WEAVE_ERROR_WRONG_ENCRYPTION_TYPE_FROM_PEER;
break;
case kStatusCode_UnknownKeyType:
keyErr = WEAVE_ERROR_UNKNOWN_KEY_TYPE_FROM_PEER;
break;
case kStatusCode_InvalidUseOfSessionKey:
keyErr = WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY_FROM_PEER;
break;
case kStatusCode_UnsupportedEncryptionType:
keyErr = WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE_FROM_PEER;
break;
default:
keyErr = WEAVE_ERROR_INTERNAL_KEY_ERROR_FROM_PEER;
break;
}
// If the failed key is a session key...
if (WeaveKeyId::IsSessionKey(keyId))
{
// Attempt to find the referenced session key. If found...
err = FabricState->FindSessionKey(keyId, srcNodeId, false, sessionKey);
if (err == WEAVE_NO_ERROR)
{
// If the key refers to a shared session, add the shared session end nodes to the list
// of peer nodes associated with the key.
if (sessionKey->IsSharedSession())
{
FabricState->GetSharedSessionEndNodeIds(sessionKey, endNodeIds, sizeof(endNodeIds) / sizeof(uint64_t), endNodeIdsCount);
}
// Add the terminating node to the list of peer nodes associated with the key.
endNodeIds[endNodeIdsCount++] = sessionKey->NodeId;
// Discard the failed session key.
FabricState->RemoveSessionKey(keyId, srcNodeId);
}
}
// Otherwise, the key is some other form of key (e.g. a group key), so add the node that sent the
// key error to the list of peers associated with the key.
else
{
endNodeIds[endNodeIdsCount++] = srcNodeId;
}
// For each peer node associated with the key, notify the exchange manager that the key has failed
// with respect to that peer.
for (int i = 0; i < endNodeIdsCount; i++)
ExchangeManager->NotifyKeyFailed(endNodeIds[i], keyId, keyErr);
// TODO: fail the current in-progress session if the key error identifies the proposed key.
// Call the general key error message handler.
if (OnKeyErrorMsgRcvd != NULL)
OnKeyErrorMsgRcvd(keyId, encType, messageId, srcNodeId, keyErr);
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (ec != NULL)
ec->Close();
return;
}
WEAVE_ERROR WeaveSecurityManager::NewSessionExchange(uint64_t peerNodeId, IPAddress peerAddr, uint16_t peerPort)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (mEC != NULL)
{
mEC->Close();
mEC = NULL;
}
// Create a new exchange context.
if (mCon)
{
mEC = ExchangeManager->NewContext(mCon, this);
VerifyOrExit(mEC != NULL, err = WEAVE_ERROR_NO_MEMORY);
}
else
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
VerifyOrExit(peerNodeId != kNodeIdNotSpecified && peerNodeId != kAnyNodeId, err = WEAVE_ERROR_INVALID_ARGUMENT);
mEC = ExchangeManager->NewContext(peerNodeId, peerAddr, peerPort, INET_NULL_INTERFACEID, this);
VerifyOrExit(mEC != NULL, err = WEAVE_ERROR_NO_MEMORY);
mEC->OnAckRcvd = WRMPHandleAckRcvd;
mEC->OnSendError = WRMPHandleSendError;
#else
// Reject the request if no connection has been specified.
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
#endif
}
exit:
return err;
}
#if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC
// Create and initialize new exchange for the message counter synchronization request/response messages.
WEAVE_ERROR WeaveSecurityManager::NewMsgCounterSyncExchange(const WeaveMessageInfo *rcvdMsgInfo, const IPPacketInfo *rcvdMsgPacketInfo, ExchangeContext *& ec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Verify input arguments.
VerifyOrExit(rcvdMsgInfo != NULL && rcvdMsgPacketInfo != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Message counter synchronization protocol is only applicable for application group keys.
VerifyOrExit(WeaveKeyId::IsAppGroupKey(rcvdMsgInfo->KeyId), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Create new exchange context.
ec = ExchangeManager->NewContext(rcvdMsgInfo->SourceNodeId, rcvdMsgPacketInfo->SrcAddress, rcvdMsgPacketInfo->SrcPort, rcvdMsgPacketInfo->Interface, this);
VerifyOrExit(ec != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Set encryption type and key Id.
ec->EncryptionType = rcvdMsgInfo->EncryptionType;
ec->KeyId = rcvdMsgInfo->KeyId;
exit:
return err;
}
/**
* Send peer message counter synchronization request.
* This function is called while processing a message encrypted with an application key from a peer whose message counter is not synchronized.
* This message is sent on a newly created exchange, which is closed immediately after.
*
* @param[in] rcvdMsgInfo A pointer to the message information for the received Weave message.
*
* @param[in] rcvdMsgPacketInfo A pointer to the IPPacketInfo object of the received Weave message.
*
* @retval #WEAVE_ERROR_INVALID_ARGUMENT If keyId input has invalid value.
* @retval #WEAVE_ERROR_NO_MEMORY If memory could not be allocated for the new
* exchange context or new message buffer.
* @retval #WEAVE_NO_ERROR On success.
*
*/
WEAVE_ERROR WeaveSecurityManager::SendSolitaryMsgCounterSyncReq(const WeaveMessageInfo *rcvdMsgInfo, const IPPacketInfo *rcvdMsgPacketInfo)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *ec = NULL;
// Create and initialize new exchange.
err = NewMsgCounterSyncExchange(rcvdMsgInfo, rcvdMsgPacketInfo, ec);
SuccessOrExit(err);
// Send the message counter synchronization request in a Common::Null message.
err = ec->SendCommonNullMessage();
SuccessOrExit(err);
exit:
if (ec != NULL)
ec->Close();
return err;
}
/**
* Send message counter synchronization response message.
* This function is called when Weave node receives message with message counter synchronization request flag.
* This message is sent on a newly created exchange, which is closed immediately after.
*
* @param[in] rcvdMsgInfo A pointer to the message information for the received Weave message.
*
* @param[in] rcvdMsgPacketInfo A pointer to the IPPacketInfo object of the received Weave message.
*
* @retval #WEAVE_ERROR_INVALID_ARGUMENT If received message with message counter synchronization
* request was unencrypted.
* @retval #WEAVE_ERROR_NO_MEMORY If memory could not be allocated for the new
* exchange context or new message buffer.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL If allocated message buffer is too small.
* @retval #WEAVE_NO_ERROR On success.
*
*/
WEAVE_ERROR WeaveSecurityManager::SendMsgCounterSyncResp(const WeaveMessageInfo *rcvdMsgInfo, const IPPacketInfo *rcvdMsgPacketInfo)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExchangeContext *ec = NULL;
PacketBuffer *msgBuf = NULL;
// Create and initialize new exchange.
err = NewMsgCounterSyncExchange(rcvdMsgInfo, rcvdMsgPacketInfo, ec);
SuccessOrExit(err);
// Allocate new buffer.
msgBuf = PacketBuffer::New();
VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Verify that the buffer is big enough for this message.
VerifyOrExit(msgBuf->AvailableDataLength() >= kWeaveMsgCounterSyncRespMsgSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Write the requester message id (counter) field.
LittleEndian::Put32(msgBuf->Start(), rcvdMsgInfo->MessageId);
// Set message length.
msgBuf->SetDataLength(kWeaveMsgCounterSyncRespMsgSize);
// Send message counter synchronization response message.
err = ec->SendMessage(kWeaveProfile_Security, kMsgType_MsgCounterSyncResp, msgBuf);
msgBuf = NULL;
exit:
if (msgBuf != NULL)
PacketBuffer::Free(msgBuf);
if (ec != NULL)
ec->Close();
return err;
}
/**
* Handle message counter synchronization response message.
*
* @param[in] msgInfo A pointer to the message information for the received Weave message.
*
* @param[in] msgBuf A pointer to the PacketBuffer object holding the received Weave message.
*
* @retval None.
*
*/
void WeaveSecurityManager::HandleMsgCounterSyncRespMsg(WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{
// Verify correct message size and that the message was encrypted with application group key.
VerifyOrExit(msgBuf->DataLength() == kWeaveMsgCounterSyncRespMsgSize && WeaveKeyId::IsAppGroupKey(msgInfo->KeyId), /* no-op */);
// Initialize/synchronize peer's message counter.
FabricState->OnMsgCounterSyncRespRcvd(msgInfo->SourceNodeId, msgInfo->MessageId, LittleEndian::Get32(msgBuf->Start()));
exit:
PacketBuffer::Free(msgBuf);
return;
}
#endif // WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC
WEAVE_ERROR WeaveSecurityManager::HandleSessionEstablished(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint64_t peerNodeId = mEC->PeerNodeId;
uint16_t sessionKeyId = mSessionKeyId;
uint8_t encType = mEncType;
const WeaveEncryptionKey *sessionKey;
WeaveAuthMode authMode;
switch (State)
{
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR || WEAVE_CONFIG_ENABLE_CASE_RESPONDER
case kState_CASEInProgress:
// Get the derived session key.
err = mCASEEngine->GetSessionKey(sessionKey);
SuccessOrExit(err);
// Form the key auth mode based on the type of certificate that was used by the peer.
//
// Note that, in the initiator case, the key mode may be different than the auth mode that
// was requested by the application. For example, if the app requested kWeaveAuthMode_CASE_AnyCert
// then the final key auth mode will reflect the actual certificate type used by the peer.
//
authMode = CASEAuthMode(mCASEEngine->CertType());
break;
#endif
#if WEAVE_CONFIG_ENABLE_PASE_INITIATOR || WEAVE_CONFIG_ENABLE_PASE_RESPONDER
case kState_PASEInProgress:
// Get the derived session key.
err = mPASEEngine->GetSessionKey(sessionKey);
SuccessOrExit(err);
// Form the key auth mode based on the password source.
authMode = PASEAuthMode(mPASEEngine->PwSource);
break;
#endif
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR || WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
case kState_TAKEInProgress:
// Get the derived session key.
err = mTAKEEngine->GetSessionKey(sessionKey);
SuccessOrExit(err);
// Currently only one key auth mode is supported for TAKE.
authMode = kWeaveAuthMode_TAKE_IdentificationKey;
break;
#endif
default:
ExitNow(err = WEAVE_ERROR_INCORRECT_STATE);
}
// Save the session key into the session key table.
err = FabricState->SetSessionKey(sessionKeyId, peerNodeId, encType, authMode, sessionKey);
SuccessOrExit(err);
exit:
return err;
}
void WeaveSecurityManager::HandleSessionComplete(void)
{
WeaveConnection *con = mCon;
uint64_t peerNodeId = mEC->PeerNodeId;
uint16_t sessionKeyId = mSessionKeyId;
uint8_t encType = mEncType;
SessionEstablishedFunct userOnComplete = mStartSecureSession_OnComplete;
void *reqState = mStartSecureSession_ReqState;
// Reset state.
Reset();
// Call the general session established handler.
if (OnSessionEstablished != NULL)
OnSessionEstablished(this, con, NULL, sessionKeyId, peerNodeId, encType);
// Call the user's completion function.
if (userOnComplete != NULL)
userOnComplete(this, con, reqState, sessionKeyId, peerNodeId, encType);
// If the session was initiated the remote party, release the reservation that was
// made when the session key record was allocated. Provided that the application
// hasn't increased the reservation count during one of the above callbacks,
// this will result in the reservation count going to zero, which will make
// eligible for removal if it remains idle past the idle timeout period.
{
WeaveSessionKey *sessionKey;
if (FabricState->FindSessionKey(sessionKeyId, peerNodeId, false, sessionKey) == WEAVE_NO_ERROR &&
!sessionKey->IsLocallyInitiated())
{
ReleaseSessionKey(sessionKey);
}
}
// Asynchronously notify other subsystems that the security manager is now available
// for initiating additional sessions.
AsyncNotifySecurityManagerAvailable();
}
void WeaveSecurityManager::HandleSessionError(WEAVE_ERROR err, PacketBuffer* statusReportMsgBuf)
{
// If session establishment in progress...
//
// NOTE: This check is necessary because it is possible for HandleSessionError() to be
// called twice in certain situations: If a call to ExchangeContext::SendMessage() fails
// because the underlying connection has been closed, it will trigger a callback, while
// in the SendMessage() function, to HandleConnectionClosed() which calls this function.
// Then when SendMessage() returns, the function that called it will also call this
// function with the error returned by SendMessage().
//
if (State != kState_Idle)
{
WeaveConnection *con = mCon;
uint64_t peerNodeId = mEC->PeerNodeId;
uint16_t sessionKeyId = mSessionKeyId;
SessionErrorFunct userOnError = mStartSecureSession_OnError;
void *reqState = mStartSecureSession_ReqState;
StatusReport rcvdStatusReport;
StatusReport *statusReportPtr = NULL;
// If a status report was received from the peer, parse it and arrange to pass it
// to the callbacks.
if (err == WEAVE_ERROR_STATUS_REPORT_RECEIVED)
{
WEAVE_ERROR parseErr = StatusReport::parse(statusReportMsgBuf, rcvdStatusReport);
if (parseErr == WEAVE_NO_ERROR)
statusReportPtr = &rcvdStatusReport;
else
err = parseErr;
}
// Otherwise, send a status report to the peer with our reason for the failure.
else
SendStatusReport(err, mEC);
// Remove the session key from the key table.
FabricState->RemoveSessionKey(sessionKeyId, peerNodeId);
// Reset state.
Reset();
// Call the general session error handler.
if (OnSessionError != NULL)
OnSessionError(this, con, NULL, err, peerNodeId, statusReportPtr);
// Call the user's error handler.
if (userOnError != NULL)
userOnError(this, con, reqState, err, peerNodeId, statusReportPtr);
// Asynchronously notify other subsystems that the security manager is now available
// for initiating another session.
AsyncNotifySecurityManagerAvailable();
}
}
void WeaveSecurityManager::HandleConnectionClosed(ExchangeContext *ec, WeaveConnection *con, WEAVE_ERROR conErr)
{
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
if (conErr == WEAVE_NO_ERROR)
conErr = WEAVE_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
// Clean-up the local state and invoke the appropriate callbacks.
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
if (secMgr->State == kState_KeyExportInProgress)
secMgr->HandleKeyExportError(conErr, NULL);
else
#endif
secMgr->HandleSessionError(conErr, NULL);
}
WEAVE_ERROR WeaveSecurityManager::SendStatusReport(WEAVE_ERROR localErr, ExchangeContext *ec)
{
WEAVE_ERROR err;
uint32_t profileId;
uint16_t statusCode;
uint16_t sendFlags;
// Verify that specified exchange isn't closed.
VerifyOrExit(ec != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
if (ec->Con != NULL)
{
// Verify that underlying connection isn't closed.
VerifyOrExit(!ec->IsConnectionClosed(), err = WEAVE_ERROR_INVALID_ARGUMENT);
sendFlags = 0;
}
else
{
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
sendFlags = ExchangeContext::kSendFlag_RequestAck;
#else
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
#endif
}
// TODO: map CASE errors
switch (localErr)
{
case WEAVE_ERROR_INCORRECT_STATE:
case WEAVE_ERROR_INVALID_MESSAGE_TYPE:
profileId = kWeaveProfile_Common;
statusCode = kStatus_UnexpectedMessage;
break;
case WEAVE_ERROR_NOT_IMPLEMENTED:
profileId = kWeaveProfile_Common;
statusCode = kStatus_UnsupportedMessage;
break;
case WEAVE_ERROR_SECURITY_MANAGER_BUSY:
case WEAVE_ERROR_RATE_LIMIT_EXCEEDED:
profileId = kWeaveProfile_Common;
statusCode = kStatus_Busy;
break;
case WEAVE_ERROR_TIMEOUT:
profileId = kWeaveProfile_Common;
statusCode = kStatus_Timeout;
break;
case WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_UnsupportedEncryptionType;
break;
case WEAVE_ERROR_WRONG_KEY_TYPE:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_InvalidKeyId;
break;
case WEAVE_ERROR_DUPLICATE_KEY_ID:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_DuplicateKeyId;
break;
case WEAVE_ERROR_KEY_CONFIRMATION_FAILED:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_KeyConfirmationFailed;
break;
case WEAVE_ERROR_INVALID_PASE_PARAMETER:
case WEAVE_ERROR_CERT_USAGE_NOT_ALLOWED:
case WEAVE_ERROR_CERT_PATH_LEN_CONSTRAINT_EXCEEDED:
case WEAVE_ERROR_CERT_NOT_VALID_YET:
case WEAVE_ERROR_CERT_EXPIRED:
case WEAVE_ERROR_CERT_PATH_TOO_LONG:
case WEAVE_ERROR_CA_CERT_NOT_FOUND:
case WEAVE_ERROR_INVALID_SIGNATURE:
case WEAVE_ERROR_CERT_NOT_TRUSTED:
case WEAVE_ERROR_WRONG_CERT_SUBJECT:
case WEAVE_ERROR_WRONG_CERT_TYPE:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_AuthenticationFailed;
break;
case WEAVE_ERROR_PASE_SUPPORTS_ONLY_CONFIG1:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_PASESupportsOnlyConfig1;
break;
case WEAVE_ERROR_NO_COMMON_PASE_CONFIGURATIONS:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_NoCommonPASEConfigurations;
break;
case WEAVE_ERROR_UNSUPPORTED_CASE_CONFIGURATION:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_UnsupportedCASEConfiguration;
break;
case WEAVE_ERROR_UNSUPPORTED_CERT_FORMAT:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_UnsupportedCertificate;
break;
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
case WEAVE_ERROR_NO_COMMON_KEY_EXPORT_CONFIGURATIONS:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_NoCommonKeyExportConfiguration;
break;
case WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_REQUEST:
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_UnathorizedKeyExportRequest;
break;
#endif // WEAVE_CONFIG_ENABLE_KEY_EXPORT_RESPONDER
default:
WeaveLogError(SecurityManager, "Internal security error %d", localErr);
profileId = kWeaveProfile_Security;
statusCode = kStatusCode_InternalError;
break;
}
// TODO: add support for conveying system error
// (HOWEVER, be careful to suppress error information that should not be revealed to the peer).
err = nl::Weave::WeaveServerBase::SendStatusReport(ec, profileId, statusCode, WEAVE_NO_ERROR, sendFlags);
SuccessOrExit(err);
exit:
return err;
}
void WeaveSecurityManager::Reset(void)
{
if (mEC != NULL)
{
mEC->Abort();
mEC = NULL;
}
switch (State)
{
#if WEAVE_CONFIG_ENABLE_PASE_INITIATOR || WEAVE_CONFIG_ENABLE_PASE_RESPONDER
case kState_PASEInProgress:
if (mPASEEngine != NULL)
{
mPASEEngine->Shutdown();
Platform::Security::MemoryFree(mPASEEngine);
mPASEEngine = NULL;
}
break;
#endif
#if WEAVE_CONFIG_ENABLE_TAKE_INITIATOR || WEAVE_CONFIG_ENABLE_TAKE_RESPONDER
case kState_TAKEInProgress:
if (mTAKEEngine != NULL)
{
mTAKEEngine->Shutdown();
Platform::Security::MemoryFree(mTAKEEngine);
mTAKEEngine = NULL;
}
break;
#endif
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR || WEAVE_CONFIG_ENABLE_CASE_RESPONDER
case kState_CASEInProgress:
if (mCASEEngine != NULL)
{
mCASEEngine->Shutdown();
Platform::Security::MemoryFree(mCASEEngine);
mCASEEngine = NULL;
}
break;
#endif
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
case kState_KeyExportInProgress:
if (mKeyExport != NULL)
{
mKeyExport->Shutdown();
Platform::Security::MemoryFree(mKeyExport);
mKeyExport = NULL;
}
break;
#endif
default:
break;
}
Platform::Security::MemoryShutdown();
CancelSessionTimer();
State = kState_Idle;
mCon = NULL;
mRequestedAuthMode = kWeaveAuthMode_NotSpecified;
mSessionKeyId = WeaveKeyId::kNone;
mEncType = kWeaveEncryptionType_None;
mStartSecureSession_OnComplete = NULL;
mStartSecureSession_OnError = NULL;
mStartSecureSession_ReqState = NULL;
}
void WeaveSecurityManager::StartSessionTimer(void)
{
WeaveLogProgress(SecurityManager, "%s", __FUNCTION__);
if (SessionEstablishTimeout != 0)
{
mSystemLayer->StartTimer(SessionEstablishTimeout, HandleSessionTimeout, this);
}
}
void WeaveSecurityManager::CancelSessionTimer(void)
{
WeaveLogProgress(SecurityManager, "%s", __FUNCTION__);
mSystemLayer->CancelTimer(HandleSessionTimeout, this);
}
void WeaveSecurityManager::HandleSessionTimeout(System::Layer* aSystemLayer, void* aAppState, System::Error aError)
{
WeaveLogProgress(SecurityManager, "%s", __FUNCTION__);
WeaveSecurityManager* securityMgr = reinterpret_cast<WeaveSecurityManager*>(aAppState);
if (securityMgr)
{
securityMgr->HandleSessionError(WEAVE_ERROR_TIMEOUT, NULL);
}
}
void WeaveSecurityManager::StartIdleSessionTimer(void)
{
if (IdleSessionTimeout != 0 && !GetFlag(mFlags, kFlag_IdleSessionTimerRunning))
{
System::Layer *systemLayer = FabricState->MessageLayer->SystemLayer;
System::Error err = systemLayer->StartTimer(IdleSessionTimeout, HandleIdleSessionTimeout, this);
if (err == WEAVE_SYSTEM_NO_ERROR)
{
WeaveLogDetail(SecurityManager, "Session idle timer started");
SetFlag(mFlags, kFlag_IdleSessionTimerRunning);
}
}
}
void WeaveSecurityManager::StopIdleSessionTimer(void)
{
System::Layer *systemLayer = FabricState->MessageLayer->SystemLayer;
systemLayer->CancelTimer(HandleIdleSessionTimeout, this);
ClearFlag(mFlags, kFlag_IdleSessionTimerRunning);
WeaveLogDetail(SecurityManager, "Session idle timer stopped");
}
void WeaveSecurityManager::HandleIdleSessionTimeout(System::Layer* aLayer, void* aAppState, System::Error aError)
{
WeaveSecurityManager *_this = (WeaveSecurityManager *)aAppState;
bool unreservedSessionsExist;
ClearFlag(_this->mFlags, kFlag_IdleSessionTimerRunning);
unreservedSessionsExist = _this->FabricState->RemoveIdleSessionKeys();
if (unreservedSessionsExist)
{
_this->StartIdleSessionTimer();
}
}
void WeaveSecurityManager::OnEncryptedMsgRcvd(uint16_t sessionKeyId, uint64_t peerNodeId, uint8_t encType)
{
// Check if corresponding secure session is established but not completed - if true then complete this session.
// This function is needed in WRMP case when first message from the peer encrypted with sessionKeyId
// is received before the Ack for the last message on the session establishment exchange.
// In that case there is no need to wait for the Ack and the session can be completed.
#if WEAVE_CONFIG_ENABLE_CASE_INITIATOR || WEAVE_CONFIG_ENABLE_CASE_RESPONDER
if (State == kState_CASEInProgress &&
mCASEEngine->State == WeaveCASEEngine::kState_Complete &&
mSessionKeyId == sessionKeyId &&
mEC->PeerNodeId == peerNodeId &&
mEncType == encType)
{
HandleSessionComplete();
}
#endif
}
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
void WeaveSecurityManager::WRMPHandleAckRcvd(ExchangeContext *ec, void *msgCtxt)
{
WeaveLogProgress(SecurityManager, "%s", __FUNCTION__);
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
if (secMgr->State == kState_CASEInProgress &&
secMgr->mCASEEngine->State == WeaveCASEEngine::kState_Complete)
{
secMgr->HandleSessionComplete();
}
}
void WeaveSecurityManager::WRMPHandleSendError(ExchangeContext *ec, WEAVE_ERROR err, void *msgCtxt)
{
WeaveLogProgress(SecurityManager, "%s", __FUNCTION__);
WeaveSecurityManager *secMgr = (WeaveSecurityManager *)ec->AppState;
#if WEAVE_CONFIG_ENABLE_KEY_EXPORT_INITIATOR
if (secMgr->State == kState_KeyExportInProgress)
{
secMgr->HandleKeyExportError(err, NULL);
}
else
#endif
{
secMgr->HandleSessionError(err, NULL);
}
}
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
void WeaveSecurityManager::AsyncNotifySecurityManagerAvailable()
{
mSystemLayer->ScheduleWork(DoNotifySecurityManagerAvailable, this);
}
void WeaveSecurityManager::DoNotifySecurityManagerAvailable(System::Layer *systemLayer, void *appState, System::Error err)
{
WeaveSecurityManager *_this = (WeaveSecurityManager *)appState;
if (_this->State == kState_Idle)
{
_this->ExchangeManager->NotifySecurityManagerAvailable();
}
}
/**
* Cancel an in-progress session establishment.
*
* @param[in] reqState A pointer value that matches the value supplied by the application
* when the session was started.
*
* @retval #WEAVE_NO_ERROR If a matching in-progress session establishment was found and canceled.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If there was no session establishment in progress, or the
* in-progress session did not match the supplied request state pointer.
*/
WEAVE_ERROR WeaveSecurityManager::CancelSessionEstablishment(void *reqState)
{
// If a session establishment is in progress and the supplied request state matches what was provided
// when the session was started...
if ((State == kState_CASEInProgress || State == kState_PASEInProgress || State == kState_TAKEInProgress) &&
reqState == mStartSecureSession_ReqState)
{
// Clear the application's OnError handler to prevent a callback.
mStartSecureSession_OnError = NULL;
// Fail the session with a canceled error.
HandleSessionError(WEAVE_ERROR_TRANSACTION_CANCELED, NULL);
return WEAVE_NO_ERROR;
}
// Otherwise, tell the caller there was no match.
else
{
return WEAVE_ERROR_INCORRECT_STATE;
}
}
/**
* Place a reservation on a message encryption key.
*
* Key reservations are used to signal that a particular key is actively in use and should be retained.
* Note that placing reservation on a key does not guarantee that the key wont be removed by an explicit
* action such as the reception of a KeyError message.
*
* For every reservation placed on a particular key, a corresponding call to ReleaseKey() must be made.
*
* This method accepts any form of key id, including None. Key ids that do not name actual keys are ignored.
*
* @param[in] peerNodeId The Weave node id of the peer with which the key shared.
*
* @param[in] keyId The id of the key to be reserved.
*
*/
void WeaveSecurityManager::ReserveKey(uint64_t peerNodeId, uint16_t keyId)
{
// If the key is a session key, attempt to locate the specified key and increase its reservation count.
// (Currently reservations only apply to session keys).
if (WeaveKeyId::IsSessionKey(keyId))
{
WeaveSessionKey *sessionKey;
if (FabricState->FindSessionKey(keyId, peerNodeId, false, sessionKey) == WEAVE_NO_ERROR)
{
ReserveSessionKey(sessionKey);
}
}
}
/**
* Release a message encryption key reservation.
*
* Release a reservations that was previously placed on a message encryption key.
*
* For every reservation placed on a particular key, the ReleaseKey() method must be called no more than once.
*
* This method accepts any form of key id, including None. Key ids that do not name actual keys are ignored.
*
* @param[in] peerNodeId The Weave node id of the peer with which the key shared.
*
* @param[in] keyId The id of the key whose reservation should be released.
*
*/
void WeaveSecurityManager::ReleaseKey(uint64_t peerNodeId, uint16_t keyId)
{
// If the key is a session key, attempt to locate the specified key and decrease its reservation count.
// (Currently reservations only apply to session keys).
if (WeaveKeyId::IsSessionKey(keyId))
{
WeaveSessionKey *sessionKey;
if (FabricState->FindSessionKey(keyId, peerNodeId, false, sessionKey) == WEAVE_NO_ERROR)
{
ReleaseSessionKey(sessionKey);
}
}
}
/**
* Place a reservation on a session key.
*
* @param[in] sessionKey A pointer to the session key to be reserved.
*
*/
void WeaveSecurityManager::ReserveSessionKey(WeaveSessionKey *sessionKey)
{
VerifyOrDie(sessionKey->ReserveCount < UINT8_MAX);
sessionKey->ReserveCount++;
sessionKey->MarkRecentlyActive();
WeaveLogDetail(SecurityManager, "Reserve session key: Id=%04" PRIX16 " Peer=%016" PRIX64 " Reserve=%" PRId8,
sessionKey->MsgEncKey.KeyId, sessionKey->NodeId, sessionKey->ReserveCount);
}
/**
* Release a reservation on a session key.
*
* @param[in] sessionKey A pointer to the session key to be released.
*
*/
void WeaveSecurityManager::ReleaseSessionKey(WeaveSessionKey *sessionKey)
{
VerifyOrDie(sessionKey->ReserveCount > 0);
sessionKey->ReserveCount--;
WeaveLogDetail(SecurityManager, "Release session key: Id=%04" PRIX16 " Peer=%016" PRIX64 " Reserve=%" PRId8,
sessionKey->MsgEncKey.KeyId, sessionKey->NodeId, sessionKey->ReserveCount);
// If the session key is subject to automatic removal and its reserve count is now zero...
if (sessionKey->BoundCon == NULL &&
sessionKey->IsKeySet() &&
sessionKey->ReserveCount == 0)
{
// If the session key is marked remove-on-idle, enable the idle session timer and mark the key as
// recently active. This will give it the maximum lifetime before it gets removed for inactivity.
if (sessionKey->IsRemoveOnIdle())
{
StartIdleSessionTimer();
sessionKey->MarkRecentlyActive();
}
// Otherwise remove the session key immediately.
else
{
FabricState->RemoveSessionKey(sessionKey);
}
}
}
} // namespace Weave
} // namespace nl