blob: 3479ccc26f3f9a4590c8ad90d48144e635c98c36 [file] [log] [blame]
/*
*
* Copyright (c) 2014-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.
*/
// __STDC_LIMIT_MACROS must be defined for UINT8_MAX and INT32_MAX to be defined for pre-C++11 clib
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif // __STDC_LIMIT_MACROS
// __STDC_CONSTANT_MACROS must be defined for INT64_C and UINT64_C to be defined for pre-C++11 clib
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif // __STDC_CONSTANT_MACROS
// it is important for this first inclusion of stdint.h to have all the right switches turned ON
#include <stdint.h>
// __STDC_FORMAT_MACROS must be defined for PRIX64 to be defined for pre-C++11 clib
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
// it is important for this first inclusion of inttypes.h to have all the right switches turned ON
#include <inttypes.h>
#define WEAVE_CONFIG_ENABLE_LOG_FILE_LINE_FUNC_ON_ERROR 1
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/ErrorStr.h>
#include "MockAlarmOriginator.h"
#include "MockAlarmRemote.h"
using namespace nl::Weave;
using namespace nl::Weave::Profiles;
WeaveAlarmServer gAlarmServer;
AlarmHushRequest hushChallenge;
void LogAlarmCondition(const uint8_t alarm_condition);
MockAlarmOriginator::MockAlarmOriginator() :
mAlarmClient(NULL)
{
}
WEAVE_ERROR MockAlarmOriginator::Init(WeaveExchangeManager *exchangeMgr,
const bool shouldAlarm,
const uint8_t encryptionType,
const uint16_t keyId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Alarm alarm;
err = gAlarmServer.Init(exchangeMgr, this);
SuccessOrExit(err);
gAlarmServer.mInterfaceId = INET_NULL_INTERFACEID;
gAlarmServer.mAlarmRebroadcastPeriodMsec = 4000; // ms
gAlarmServer.mAlarmRefreshPeriodMsec = 30000; // ms
gAlarmServer.mAlarmRebroadcastThreshold = 6;
err = hushChallenge.init();
SuccessOrExit(err);
{
// challenge code shall be sent over through BLE advertising, in the hope of saving one round trip
const uint32_t challengeCode = 0xABCD0123;
const uint16_t hushKeyId = 0x0002;
const uint8_t hushKey [16] = { 1 };
hushChallenge.mProximityVerificationCode = 0xDEADBEEF;
err = hushChallenge.sign(challengeCode, hushKeyId, hushKey, int16_t(sizeof(hushKey)));
SuccessOrExit(err);
WeaveLogProgress(Alarm, "Hush challenge: 0x%X keyId: 0x%X, key is %d-byte starting with %d",
challengeCode, hushChallenge.mSignature.mKeyId, sizeof(hushKey), hushKey[0]);
WeaveLogProgress(Alarm, "Hush mSignature part 1: %02X%02X%02X%02X-%02X%02X%02X%02X",
hushChallenge.mSignature.mKeyedHash[0], hushChallenge.mSignature.mKeyedHash[1],
hushChallenge.mSignature.mKeyedHash[2], hushChallenge.mSignature.mKeyedHash[3],
hushChallenge.mSignature.mKeyedHash[4], hushChallenge.mSignature.mKeyedHash[5],
hushChallenge.mSignature.mKeyedHash[6], hushChallenge.mSignature.mKeyedHash[7]);
WeaveLogProgress(Alarm, "Hush mSignature part 2: %02X%02X%02X%02X-%02X%02X%02X%02X",
hushChallenge.mSignature.mKeyedHash[8], hushChallenge.mSignature.mKeyedHash[9],
hushChallenge.mSignature.mKeyedHash[10], hushChallenge.mSignature.mKeyedHash[11],
hushChallenge.mSignature.mKeyedHash[12], hushChallenge.mSignature.mKeyedHash[13],
hushChallenge.mSignature.mKeyedHash[14], hushChallenge.mSignature.mKeyedHash[15]);
WeaveLogProgress(Alarm, "Hush mSignature part 3: %02X%02X%02X%02X",
hushChallenge.mSignature.mKeyedHash[16], hushChallenge.mSignature.mKeyedHash[17],
hushChallenge.mSignature.mKeyedHash[18], hushChallenge.mSignature.mKeyedHash[19]);
}
gAlarmServer.SetAlarmDelegate(this);
mAlarmClient = gAlarmServer.NewClient(kAnyNodeId, encryptionType, keyId);
VerifyOrExit(NULL != mAlarmClient, err = WEAVE_ERROR_NO_MEMORY);
if (shouldAlarm)
{
alarm.AddAlarm(WEAVE_ALARM_CO | WEAVE_ALARM_STATE_HEADS_UP_1);
alarm.AddAlarm(WEAVE_ALARM_SMOKE | WEAVE_ALARM_STATE_ALARM_NONHUSHABLE);
err = mAlarmClient->SendAlarm(&alarm);
SuccessOrExit(err);
}
{
// just a simple test to demo how random number generation utility routines work
uint32_t testRandom = 0;
WeaveAlarmClient::GenerateHushChallenge(&testRandom);
WeaveLogProgress(Alarm, "Random number generated for hush challenge code: 0x%X", testRandom);
WeaveAlarmClient::GenerateProximityVerificationCode(&testRandom);
WeaveLogProgress(Alarm, "Random number generated for proximity verification code: 0x%X", testRandom);
}
exit:
WeaveLogFunctError(err);
return err;
}
WEAVE_ERROR MockAlarmOriginator::Shutdown(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = gAlarmServer.Shutdown();
SuccessOrExit(err);
exit:
WeaveLogFunctError(err);
return err;
}
WEAVE_ERROR MockAlarmOriginator::OnHushRequest(nl::Weave::ExchangeContext *ec, uint32_t proximityVerificationCode, const AlarmHushSignature & signature)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Application code shall verify the content/signature of the hush request and decide if
// this request needs to be further processed
// hush requests coming from channels other than BLE can be ignored
WeaveLogProgress(Alarm, "Req key id: 0x%X", signature.mKeyId);
WeaveLogProgress(Alarm, "Req signature part 1: %02X%02X%02X%02X-%02X%02X%02X%02X",
signature.mKeyedHash[0], signature.mKeyedHash[1],
signature.mKeyedHash[2], signature.mKeyedHash[3],
signature.mKeyedHash[4], signature.mKeyedHash[5],
signature.mKeyedHash[6], signature.mKeyedHash[7]);
WeaveLogProgress(Alarm, "Req signature part 2: %02X%02X%02X%02X-%02X%02X%02X%02X",
signature.mKeyedHash[8], signature.mKeyedHash[9],
signature.mKeyedHash[10], signature.mKeyedHash[11],
signature.mKeyedHash[12], signature.mKeyedHash[13],
signature.mKeyedHash[14], signature.mKeyedHash[15]);
WeaveLogProgress(Alarm, "Req signature part 3: %02X%02X%02X%02X",
signature.mKeyedHash[16], signature.mKeyedHash[17],
signature.mKeyedHash[18], signature.mKeyedHash[19]);
if (!signature.mIsSigned)
{
WeaveLogProgress(Alarm, "Hush: not signed");
// the request is not signed
err = gAlarmServer.SendStatusReport(kWeaveProfile_Alarm,
kAlarmStatusReport_HushRejected_SignatureValidationFailure);
}
else if (hushChallenge.mSignature != signature)
{
WeaveLogProgress(Alarm, "Hush: not signed or signature doesn't match");
// since we use signature comparison, the request is either not signed, or doesn't match with ours
err = gAlarmServer.SendStatusReport(kWeaveProfile_Alarm,
kAlarmStatusReport_HushRejected_SignatureValidationFailure);
}
else if (NULL == mAlarmClient)
{
WeaveLogProgress(Alarm, "Hush: no alarm");
// there is no Alarm at all to hush, but we should still respond with a 'success',
err = gAlarmServer.SendHushResponse(kAlarmHushResult_Success);
SuccessOrExit(err);
}
else
{
WeaveLogProgress(Alarm, "Hush: incoming proximity verification code 0x%X", proximityVerificationCode);
if (hushChallenge.mProximityVerificationCode != proximityVerificationCode)
{
WeaveLogProgress(Alarm, "Hush: expected proximity verification code 0x%X, doesn't match", proximityVerificationCode);
err = gAlarmServer.SendStatusReport(kWeaveProfile_Alarm,
kAlarmStatusReport_HushRejected_ProximityValidationFailure);
SuccessOrExit(err);
}
else
{
// apply hush to each of condition we have
// application code might have other logic to execute for hushing and doesn't have to be
// bound by this particular Alarm object
Alarm & currentAlarm(mAlarmClient->mCurrentAlarm);
WeaveLogProgress(Alarm, "BEFORE Hush: counter [%d] with [%d] conditions",
currentAlarm.mAlarmCtr,
currentAlarm.mLength);
for (int i = 0; i < currentAlarm.mLength; ++i)
{
LogAlarmCondition(currentAlarm.mConditions[i]);
}
for (int i = 0; i < currentAlarm.mLength; ++i)
{
if (currentAlarm.GetAlarmState(i) <= WEAVE_ALARM_STATE_ALARM_HUSHABLE)
{
currentAlarm.SetAlarmState(i, WEAVE_ALARM_STATE_ALARM_REMOTE_HUSH);
}
}
WeaveLogProgress(Alarm, "AFTER Hush: counter [%d] with [%d] conditions",
currentAlarm.mAlarmCtr,
currentAlarm.mLength);
for (int i = 0; i < currentAlarm.mLength; ++i)
{
LogAlarmCondition(currentAlarm.mConditions[i]);
}
// this call 'kills' the alarm and makes the pointer useless
mAlarmClient->Close();
mAlarmClient = NULL;
err = gAlarmServer.SendHushResponse(kAlarmHushResult_Success, currentAlarm.mLength, currentAlarm.mConditions);
SuccessOrExit(err);
}
}
exit:
WeaveLogFunctError(err);
return err;
}
void LogAlarmCondition(const uint8_t alarm_condition)
{
const char * state;
const char * source;
switch (alarm_condition & 0xf0)
{
case WEAVE_ALARM_SMOKE:
source = "smoke";
break;
case WEAVE_ALARM_TEMP:
source = "temperature";
break;
case WEAVE_ALARM_CO:
source = "carbon monoxide";
break;
case WEAVE_ALARM_CH4:
source = "gas";
break;
case WEAVE_ALARM_HUMIDITY:
source = "humidity";
break;
case WEAVE_ALARM_OTHER:
source = "other";
break;
default:
source = "unknown";
break;
}
switch (alarm_condition & 0x0f)
{
case WEAVE_ALARM_STATE_STANDBY:
state = "standby";
break;
case WEAVE_ALARM_STATE_HEADS_UP_1:
state = "heads up 1";
break;
case WEAVE_ALARM_STATE_HEADS_UP_2:
state = "heads up 2";
break;
case WEAVE_ALARM_STATE_HU_HUSH:
state = "heads up hush";
break;
case WEAVE_ALARM_STATE_ALARM_HUSHABLE:
state = "ALARM, hushable";
break;
case WEAVE_ALARM_STATE_ALARM_NONHUSHABLE:
state = "ALARM, NONHUSHABLE";
break;
case WEAVE_ALARM_STATE_ALARM_GLOBAL_HUSH:
state = "global hush";
break;
case WEAVE_ALARM_STATE_ALARM_REMOTE_HUSH:
state = "remote hush";
break;
case WEAVE_ALARM_STATE_SELFTEST:
state = "selftest";
break;
default:
state = "unknown";
break;
}
WeaveLogProgress(Alarm, "[%s]: [%s]", state, source);
}
void MockAlarmOriginator::OnAlarmClientStateChange(WeaveAlarmClient * const aClient)
{
WeaveLogProgress(Alarm, "Alarm from 0x%" PRIx64 " changed", aClient->GetOriginator());
for (int i = 0; i < aClient->mCurrentAlarm.mLength; ++i)
{
LogAlarmCondition(aClient->mCurrentAlarm.GetAlarm(i));
}
}
void MockAlarmOriginator::OnNewRemoteAlarmDropped(const Alarm & aAlarm)
{
WeaveLogProgress(Alarm, "Alarm at where id [%u] has been dropped", aAlarm.mWhere);
for (int i = 0; i < aAlarm.mLength; ++i)
{
LogAlarmCondition(aAlarm.GetAlarm(i));
}
}
int MockAlarmOriginator::CompareSeverity(const Alarm & aAlarm1, const Alarm & aAlarm2)
{
enum
{
kResultSource_Smoke = 0,
kResultSource_CO = 1,
kResultAlarm1 = +1,
kResultTie = 0,
kResultAlarm2 = -1,
kNumRecognizedAlarmSources = 2,
};
int resultTable[kNumRecognizedAlarmSources][2];
resultTable[kResultSource_Smoke][0] = WEAVE_ALARM_STATE_INVALID;
resultTable[kResultSource_Smoke][1] = kResultTie;
resultTable[kResultSource_CO][0] = WEAVE_ALARM_STATE_INVALID;
resultTable[kResultSource_CO][1] = kResultTie;
for (int i = 0; i < aAlarm1.mLength; ++i)
{
switch (aAlarm1.GetAlarmCondition(i))
{
case WEAVE_ALARM_SMOKE:
resultTable[kResultSource_Smoke][0] = aAlarm1.GetAlarmState(i);
break;
case WEAVE_ALARM_CO:
resultTable[kResultSource_CO][0] = aAlarm1.GetAlarmState(i);
break;
default:
WeaveLogError(Alarm, "Ignore unknown alarm source %u", aAlarm1.GetAlarmCondition(i));
}
}
for (int i = 0; i < aAlarm2.mLength; ++i)
{
switch (aAlarm1.GetAlarmCondition(i))
{
case WEAVE_ALARM_SMOKE:
if (WEAVE_ALARM_STATE_INVALID == resultTable[kResultSource_Smoke][0])
{
// alarm1 doesn't have this source, so alarm2 wins here
resultTable[kResultSource_Smoke][0] = aAlarm2.GetAlarmState(i);
resultTable[kResultSource_Smoke][1] = kResultAlarm2;
}
else
{
// both alarm1 and alarm2 have this source. let's compare them
const int state1 = resultTable[kResultSource_Smoke][0];
const int state2 = aAlarm2.GetAlarmState(i);
if (state1 > state2)
{
resultTable[kResultSource_Smoke][1] = kResultAlarm1;
}
else if (state1 == state2)
{
resultTable[kResultSource_Smoke][1] = kResultTie;
}
else
{
resultTable[kResultSource_Smoke][1] = kResultAlarm2;
}
}
break;
case WEAVE_ALARM_CO:
if (WEAVE_ALARM_STATE_INVALID == resultTable[kResultSource_CO][0])
{
// alarm1 doesn't have this source, so alarm2 wins here
resultTable[kResultSource_CO][0] = aAlarm2.GetAlarmState(i);
resultTable[kResultSource_CO][1] = kResultAlarm2;
}
else
{
// both alarm1 and alarm2 have this source. let's compare them
const int state1 = resultTable[kResultSource_CO][0];
const int state2 = aAlarm2.GetAlarmState(i);
if (state1 > state2)
{
resultTable[kResultSource_CO][1] = kResultAlarm1;
}
else if (state1 == state2)
{
resultTable[kResultSource_CO][1] = kResultTie;
}
else
{
resultTable[kResultSource_CO][1] = kResultAlarm2;
}
}
break;
default:
WeaveLogError(Alarm, "Ignore unknown alarm source %u", aAlarm2.GetAlarmCondition(i));
}
}
if (resultTable[kResultSource_Smoke][1] != kResultTie)
{
// Smoke take precedence. Use the result of smoke if possible
return resultTable[kResultSource_Smoke][1];
}
else
{
// otherwise, use CO
return resultTable[kResultSource_CO][1];
}
}