blob: 53d32d60c4ddb99ec36b3ab6d5ab454f96694254 [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
* A library for manipulating PASE state for testing and fuzzing
*
*/
#include <stdio.h>
#include <vector>
#include "ToolCommon.h"
#include "PASEEngineTest.h"
#include <Weave/Support/ErrorStr.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/security/WeavePASE.h>
#include <Weave/Support/RandUtils.h>
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include "lwip/tcpip.h"
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::Profiles::Security::PASE;
using System::PacketBuffer;
#define TOOL_NAME "TestPASE"
const char * INITIATOR_STEP_1 = "InitiatorStep1";
const char * RESPONDER_RECONFIGURE = "ResponderReconfigure";
const char * RESPONDER_STEP_1 = "ResponderStep1";
const char * RESPONDER_STEP_2 = "ResponderStep2";
const char * INITIATOR_STEP_2 = "InitiatorStep2";
const char * RESPONDER_KEY_CONFIRM = "ResponderKeyConfirm";
#define VerifyOrQuit(TST, MSG) \
do { \
if (!(TST)) \
{ \
fprintf(stderr, "%s FAILED: ", __FUNCTION__); \
fputs(MSG, stderr); \
exit(-1); \
} \
} while (0)
#define SuccessOrQuit(ERR, MSG) \
do { \
if ((ERR) != WEAVE_NO_ERROR) \
{ \
fprintf(stderr, "%s FAILED: ", __FUNCTION__); \
fputs(MSG, stderr); \
fputs(ErrorStr(ERR), stderr); \
fputs("\n", stderr); \
exit(-1); \
} \
} while (0)
void MessageMutator::MutateMessage(const char *msgName, PacketBuffer *msgBuf) { }
static MessageMutator gNullMutator;
MessageExternalFuzzer::MessageExternalFuzzer(const char *msgType)
{
mMsgType = msgType;
mSaveCorpus = false;
}
void MessageExternalFuzzer::MutateMessage(const char *msgType, PacketBuffer *msgBuf)
{
if (strcmp(msgType, mMsgType) == 0)
{
uint8_t *msgStart = msgBuf->Start();
if (mSaveCorpus)
{
MessageExternalFuzzer::SaveCorpus(msgStart, msgBuf->DataLength(), mMsgType);
}
msgBuf->SetDataLength(mFuzzInputSize);
memcpy(msgStart, mFuzzInput, msgBuf->DataLength());
}
}
void MessageExternalFuzzer::SaveCorpus(const uint8_t *inBuf, size_t size, const char *fileName)
{
FILE* file = fopen(fileName, "wb+" );
VerifyOrQuit(file != NULL, "Could not open file");
VerifyOrQuit((fwrite(inBuf, 1, size, file) == size), "Could not write corpus file.");
fclose(file);
}
MessageExternalFuzzer& MessageExternalFuzzer::SaveCorpusFile(bool val) { mSaveCorpus = val; return *this; }
MessageExternalFuzzer& MessageExternalFuzzer::FuzzInput(const uint8_t *val, size_t size) { mFuzzInput = val; mFuzzInputSize = size; return *this; }
//Start PASEEngineTest Initialization
PASEEngineTest::PASEEngineTest(const char *testName)
{
mTestName = testName;
mProposedConfig = mExpectedConfig = kPASEConfig_Unspecified;
mInitPW = mRespPW = "TestPassword";
mInitiatorAllowedConfigs = mResponderAllowedConfigs = kPASEConfig_Config1|kPASEConfig_Config4;
mExpectReconfig = false;
mForceRepeatedReconfig = false;
memset(mExpectedErrors, 0, sizeof(mExpectedErrors));
mMutator = &gNullMutator;
mLogMessageData = false;
}
const char *PASEEngineTest::TestName() const { return mTestName; }
uint32_t PASEEngineTest::ProposedConfig() const { return mProposedConfig; }
PASEEngineTest& PASEEngineTest::ProposedConfig(uint32_t val) { mProposedConfig = val; return *this; }
uint32_t PASEEngineTest::InitiatorAllowedConfigs() const { return mInitiatorAllowedConfigs; }
PASEEngineTest& PASEEngineTest::InitiatorAllowedConfigs(uint32_t val) { mInitiatorAllowedConfigs = val; return *this; }
uint32_t PASEEngineTest::ResponderAllowedConfigs() const { return mResponderAllowedConfigs; }
PASEEngineTest& PASEEngineTest::ResponderAllowedConfigs(uint32_t val) { mResponderAllowedConfigs = val; return *this; }
const char* PASEEngineTest::InitiatorPassword() const { return mInitPW; }
PASEEngineTest& PASEEngineTest::InitiatorPassword(const char* val) { mInitPW = val; return *this; }
const char* PASEEngineTest::ResponderPassword() const { return mRespPW; }
PASEEngineTest& PASEEngineTest::ResponderPassword(const char* val) { mRespPW = val; return *this; }
uint32_t PASEEngineTest::ExpectReconfig() const { return mExpectReconfig; }
PASEEngineTest& PASEEngineTest::ExpectReconfig(uint32_t expectedConfig)
{
mExpectReconfig = true;
mExpectedConfig = expectedConfig;
return *this;
}
uint32_t PASEEngineTest::ExpectedConfig() const { return mExpectedConfig != kPASEConfig_Unspecified ? mExpectedConfig : mProposedConfig; }
bool PASEEngineTest::PerformReconfig() const { return mForceRepeatedReconfig; }
PASEEngineTest& PASEEngineTest::PerformReconfig(bool val) { mForceRepeatedReconfig = val; return *this; }
bool PASEEngineTest::ConfirmKey() const { return mConfirmKey; }
PASEEngineTest& PASEEngineTest::ConfirmKey(bool val) { mConfirmKey = val; return *this; }
PASEEngineTest& PASEEngineTest::ExpectError(WEAVE_ERROR err)
{
return ExpectError(NULL, err);
}
PASEEngineTest& PASEEngineTest::ExpectError(const char *opName, WEAVE_ERROR err)
{
for (size_t i = 0; i < kMaxExpectedErrors; i++)
{
if (mExpectedErrors[i].Error == WEAVE_NO_ERROR)
{
mExpectedErrors[i].Error = err;
mExpectedErrors[i].OpName = opName;
break;
}
}
return *this;
}
bool PASEEngineTest::IsExpectedError(const char *opName, WEAVE_ERROR err) const
{
for (size_t i = 0; i < kMaxExpectedErrors && mExpectedErrors[i].Error != WEAVE_NO_ERROR; i++)
{
if (mExpectedErrors[i].Error == err &&
(mExpectedErrors[i].OpName == NULL || strcmp(mExpectedErrors[i].OpName, opName) == 0))
{
return true;
}
}
return false;
}
bool PASEEngineTest::IsSuccessExpected() const { return mExpectedErrors[0].Error == WEAVE_NO_ERROR; }
PASEEngineTest& PASEEngineTest::Mutator(MessageMutator *mutator) { mMutator = mutator; return *this; }
bool PASEEngineTest::LogMessageData() const { return mLogMessageData; }
PASEEngineTest& PASEEngineTest::LogMessageData(bool val) { mLogMessageData = val; return *this; }
//private
void PASEEngineTest::setAllowedResponderConfigs(WeavePASEEngine &responderEng)
{
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG0_TEST_ONLY
if (mResponderAllowedConfigs == kPASEConfig_Config0_TEST_ONLY)
responderEng.AllowedPASEConfigs = kPASEConfig_SupportConfig0Bit_TEST_ONLY;
else
#endif
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG1
if (mResponderAllowedConfigs == kPASEConfig_Config1)
responderEng.AllowedPASEConfigs = kPASEConfig_SupportConfig1Bit;
else
#endif
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG2
if (mResponderAllowedConfigs == kPASEConfig_Config2)
respEng.AllowedPASEConfigs = kPASEConfig_SupportConfig2Bit;
else
#endif
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG3
if (mResponderAllowedConfigs == kPASEConfig_Config3)
responderEng.AllowedPASEConfigs = kPASEConfig_SupportConfig3Bit;
else
#endif
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG4
if (mResponderAllowedConfigs == kPASEConfig_Config4)
responderEng.AllowedPASEConfigs = kPASEConfig_SupportConfig4Bit;
else
#endif
#if WEAVE_CONFIG_SUPPORT_PASE_CONFIG5
if (mResponderAllowedConfigs == kPASEConfig_Config5)
responderEng.AllowedPASEConfigs = kPASEConfig_SupportConfig5Bit;
else
#endif
responderEng.AllowedPASEConfigs = 0x0;
}
enum
{
kMaxExpectedErrors = 32
};
//end PASEEngineTest Initialization
void PASEEngineTest::Run()
{
WEAVE_ERROR err;
WeavePASEEngine initiatorEng;
WeavePASEEngine responderEng;
PacketBuffer *msgBuf = NULL;
PacketBuffer *msgBuf2 = NULL;
WeaveFabricState initFabricState;
WeaveFabricState respFabricState;
const WeaveEncryptionKey *initiatorKey;
const WeaveEncryptionKey *responderKey;
uint64_t initNodeId = 1;
uint64_t respNodeId = 2;
uint16_t sessionKeyId = sTestDefaultSessionKeyId;
uint16_t encType = kWeaveEncryptionType_AES128CTRSHA1;
uint16_t pwSrc = kPasswordSource_PairingCode;
bool expectSuccess = strcmp(mInitPW, mRespPW) == 0;
if (LogMessageData())
{
printf("========== Starting Test: %s\n", TestName());
printf("Pr: %d\nex: %d\n", ProposedConfig(), ExpectedConfig());
}
initiatorEng.Init();
err = initFabricState.Init();
SuccessOrQuit(err, "initFabricState.Init failed\n");
initiatorEng.Pw = (const uint8_t *)mInitPW;
initiatorEng.PwLen = (uint16_t)strlen(mInitPW);
onReconfig:
responderEng.Init();
err = respFabricState.Init();
setAllowedResponderConfigs(responderEng);
SuccessOrQuit(err, "respFabricState.Init failed\n");
respFabricState.PairingCode = mRespPW;
// =========== Start PASE InitiatorStep1 ==============================
msgBuf = PacketBuffer::New();
VerifyOrQuit(msgBuf != NULL, "PacketBuffer::New() failed");
// Initiator generates and sends PASE Initiator Step 1 message.
err = initiatorEng.GenerateInitiatorStep1(msgBuf, ProposedConfig(), initNodeId, respNodeId, sessionKeyId, encType, pwSrc, &initFabricState, mConfirmKey);
if (IsExpectedError("Initiator:GenerateInitiatorStep1", err))
goto onExpectedError;
SuccessOrQuit(err, "WeavePASEEngine::GenerateInitiatorStep1 failed\n");
// =========== Initiator Sends InitiatorStep1 to Responder ============
mMutator->MutateMessage(INITIATOR_STEP_1, msgBuf);
if (LogMessageData())
{
printf("Initiator->Responder: InitiatorStep1 Message (%d bytes)\n", msgBuf->DataLength());
DumpMemory(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
}
// =========== Responder Processes PASE InitiatorStep1 ================
err = responderEng.ProcessInitiatorStep1(msgBuf, respNodeId, initNodeId, &respFabricState);
if (IsExpectedError(INITIATOR_STEP_1, err))
goto onExpectedError;
if (ExpectReconfig())
{
VerifyOrQuit(err == WEAVE_ERROR_PASE_RECONFIGURE_REQUIRED, "WEAVE_ERROR_PASE_RECONFIG_REQUIRED error expected");
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// =========== Responder generates PASE ResponderReconfigMessage ==
{
msgBuf = PacketBuffer::New();
err = responderEng.GenerateResponderReconfigure(msgBuf);
SuccessOrQuit(err, "WeavePASEEngine::GenerateResponderReconfigure failed\n");
// Reset PASE Engines
responderEng.Reset();
}
// ========== Responder sends ResponderReconfig Message ============
mMutator->MutateMessage(RESPONDER_RECONFIGURE, msgBuf);
if (LogMessageData())
{
printf("Responder->Initiator: ResponderReconfig Message (%d bytes)\n", msgBuf->DataLength());
DumpMemory(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
}
// =========== Initiator processes PASE ResponderReconfig =========
{
uint32_t tempProposedConfig = mProposedConfig;
err = initiatorEng.ProcessResponderReconfigure(msgBuf, mProposedConfig);
if (IsExpectedError("Initiator:ProcessResponderReconfigure", err))
{
mProposedConfig = tempProposedConfig;
goto onExpectedError;
}
SuccessOrQuit(err, "WeavePASEEngine::ProcessResponderReconfigure failed\n");
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
}
respFabricState.Shutdown();
mExpectReconfig = false;
goto onReconfig;
} else {
VerifyOrQuit(err != WEAVE_ERROR_PASE_RECONFIGURE_REQUIRED, "Unexpected reconfig!");
}
SuccessOrQuit(err, "WeavePASEEngine::ProcessInitiatorStep1 failed\n");
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// =========== Responder Generates ResponderStep1 and ResponderStep2 ==
{
msgBuf = PacketBuffer::New();
err = responderEng.GenerateResponderStep1(msgBuf);
SuccessOrQuit(err, "WeavePASEEngine::GenerateResponderStep1 failed\n");
// Responder generates and sends PASE Responder Step 2 message.
msgBuf2 = PacketBuffer::New();
err = responderEng.GenerateResponderStep2(msgBuf2);
SuccessOrQuit(err, "WeavePASEEngine::GenerateResponderStep2 failed\n");
}
// =========== Responder sends ResponderStep1 ==========================
mMutator->MutateMessage(RESPONDER_STEP_1, msgBuf);
if (LogMessageData())
{
printf("Responder->Initiator: ResponderStep1 Message (%d bytes)\n", msgBuf->DataLength());
DumpMemory(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
}
// =========== Responder sends ResponderStep2 ==========================
mMutator->MutateMessage(RESPONDER_STEP_2, msgBuf2);
if (LogMessageData())
{
printf("Responder->Initiator: ResponderStep2 Message (%d bytes)\n", msgBuf2->DataLength());
DumpMemory(msgBuf2->Start(), msgBuf2->DataLength(), " ", 16);
}
// =========== Initator Parses ResponderStep1 and ResponderStep2 ======
{
// Initiator receives and processes PASE Responder Step 1 message.
err = initiatorEng.ProcessResponderStep1(msgBuf);
if (IsExpectedError(RESPONDER_STEP_1, err))
goto onExpectedError;
SuccessOrQuit(err, "WeavePASEEngine::ProcessResponderStep1 failed\n");
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
// Initiator receives and processes PASE Responder Step 2 message.
err = initiatorEng.ProcessResponderStep2(msgBuf2);
if (IsExpectedError(RESPONDER_STEP_2, err))
goto onExpectedError;
SuccessOrQuit(err, "WeavePASEEngine::ProcessResponderStep2 failed\n");
PacketBuffer::Free(msgBuf2);
msgBuf2 = NULL;
}
// =========== Initator Generates InitatorStep2 ===========================
{
msgBuf = PacketBuffer::New();
err = initiatorEng.GenerateInitiatorStep2(msgBuf);
SuccessOrQuit(err, "WeavePASEEngine::GenerateInitiatorStep2 failed\n");
}
//=========== Initator Sends InitatorStep2 ============================
mMutator->MutateMessage(INITIATOR_STEP_2, msgBuf);
if (LogMessageData())
{
printf("Initiator->Responder: InitatorStep2 Message (%d bytes)\n", msgBuf->DataLength());
DumpMemory(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
}
// =========== Responder Parses InitatorStep2 =========================
{
err = responderEng.ProcessInitiatorStep2(msgBuf);
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
if (IsExpectedError(INITIATOR_STEP_2, err))
goto onExpectedError;
if (expectSuccess)
SuccessOrQuit(err, "WeavePASEEngine::ProcessInitiatorStep2 failed\n");
else if (mConfirmKey)
{
VerifyOrQuit(err == WEAVE_ERROR_KEY_CONFIRMATION_FAILED, "Expected error from WeavePASEEngine::ProcessInitiatorStep2\n");
return;
}
}
if (mConfirmKey)
{
// ========== Responder Forms ResponderKeyConfirm =================
{
msgBuf = PacketBuffer::New();
err = responderEng.GenerateResponderKeyConfirm(msgBuf);
SuccessOrQuit(err, "WeavePASEEngine::GenerateResponderKeyConfirm failed\n");
}
// ========== Responder Sends ResponderKeyConfirm to Responder ====
mMutator->MutateMessage(RESPONDER_KEY_CONFIRM, msgBuf);
if (LogMessageData())
{
printf("Responder->Initiator: ResponderKeyConfirm Message (%d bytes)\n", msgBuf->DataLength());
DumpMemory(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
}
// ========== Initiator Processes ResponderKeyConfirm =============
{
err = initiatorEng.ProcessResponderKeyConfirm(msgBuf);
if (IsExpectedError(RESPONDER_KEY_CONFIRM, err))
goto onExpectedError;
SuccessOrQuit(err, "WeavePASEEngine::ProcessResponderKeyConfirm failed\n");
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
}
}
VerifyOrQuit(initiatorEng.State == WeavePASEEngine::kState_InitiatorDone, "Initiator state != Done\n");
VerifyOrQuit(responderEng.State == WeavePASEEngine::kState_ResponderDone, "Responder state != Done\n");
VerifyOrQuit(initiatorEng.SessionKeyId == responderEng.SessionKeyId, "Initiator SessionKeyId != Responder SessionKeyId\n");
VerifyOrQuit(initiatorEng.EncryptionType == responderEng.EncryptionType, "Initiator EncryptionType != Responder EncryptionType\n");
VerifyOrQuit(initiatorEng.PerformKeyConfirmation == responderEng.PerformKeyConfirmation, "Initiator SessionKeyId != Responder SessionKeyId\n");
err = initiatorEng.GetSessionKey(initiatorKey);
SuccessOrQuit(err, "WeavePASEEngine::GetSessionKey() failed\n");
err = responderEng.GetSessionKey(responderKey);
SuccessOrQuit(err, "WeavePASEEngine::GetSessionKey() failed\n");
VerifyOrQuit(memcmp(initiatorKey->AES128CTRSHA1.DataKey, responderKey->AES128CTRSHA1.DataKey, WeaveEncryptionKey_AES128CTRSHA1::DataKeySize) == 0,
"Data key mismatch\n");
VerifyOrQuit(memcmp(initiatorKey->AES128CTRSHA1.IntegrityKey, responderKey->AES128CTRSHA1.IntegrityKey, WeaveEncryptionKey_AES128CTRSHA1::IntegrityKeySize) == 0,
"Integrity key mismatch\n");
// Shutdown the Initiator/Responder FabricState objects
err = initFabricState.Shutdown();
SuccessOrQuit(err, "initFabricState.Shutdown failed\n");
err = respFabricState.Shutdown();
SuccessOrQuit(err, "respFabricState.Shutdown failed\n");
onExpectedError:
PacketBuffer::Free(msgBuf);
msgBuf = NULL;
PacketBuffer::Free(msgBuf2);
msgBuf2 = NULL;
initiatorEng.Shutdown();
responderEng.Shutdown();
initFabricState.Shutdown();
respFabricState.Shutdown();
if (LogMessageData())
printf("Test Complete: %s\n", TestName());
}