1 //-----------------------------------------------------------------------------
2 //
3 //	Security.cpp
4 //
5 //	Implementation of the Z-Wave COMMAND_CLASS_Security
6 //
7 //	Copyright (c) 2011 Mal Lansell <openzwave@lansell.org>
8 //
9 //	SOFTWARE NOTICE AND LICENSE
10 //
11 //	This file is part of OpenZWave.
12 //
13 //	OpenZWave is free software: you can redistribute it and/or modify
14 //	it under the terms of the GNU Lesser General Public License as published
15 //	by the Free Software Foundation, either version 3 of the License,
16 //	or (at your option) any later version.
17 //
18 //	OpenZWave is distributed in the hope that it will be useful,
19 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
20 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 //	GNU Lesser General Public License for more details.
22 //
23 //	You should have received a copy of the GNU Lesser General Public License
24 //	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
25 //
26 //-----------------------------------------------------------------------------
27 
28 #include <ctime>
29 
30 
31 #include "command_classes/CommandClasses.h"
32 #include "command_classes/Security.h"
33 #include "Defs.h"
34 #include "Msg.h"
35 #include "Node.h"
36 #include "Driver.h"
37 #include "platform/Log.h"
38 #include "Utils.h"
39 
40 #include "value_classes/ValueBool.h"
41 
42 
43 using namespace OpenZWave;
44 
45 
46 
Security(uint32 const _homeId,uint8 const _nodeId)47 Security::Security
48 (
49 		uint32 const _homeId,
50 		uint8 const _nodeId
51 ):
52 CommandClass( _homeId, _nodeId ),
53 m_schemeagreed(false),
54 m_secured(false)
55 
56 {
57 	/* We don't want the Driver to route "Security" messages back to us for Encryption,
58 	 * so disable SecureSupport for the Security Command Class
59 	 * (This stops this Command Class getting Marked as as IsSecured() if its listed
60 	 * in the SecurityCmd_SupportedReport from the device - Which some devices do)
61 	 */
62 	ClearSecureSupport();
63 
64 }
65 
~Security()66 Security::~Security
67 (
68 )
69 {
70 }
71 
72 //-----------------------------------------------------------------------------
73 // <Version::ReadXML>
74 // Read configuration.
75 //-----------------------------------------------------------------------------
ReadXML(TiXmlElement const * _ccElement)76 void Security::ReadXML
77 (
78 		TiXmlElement const* _ccElement
79 )
80 {
81 	CommandClass::ReadXML( _ccElement );
82 }
83 
84 //-----------------------------------------------------------------------------
85 // <Version::WriteXML>
86 // Save changed configuration
87 //-----------------------------------------------------------------------------
WriteXML(TiXmlElement * _ccElement)88 void Security::WriteXML
89 (
90 		TiXmlElement* _ccElement
91 )
92 {
93 	CommandClass::WriteXML( _ccElement );
94 }
95 
96 
97 
Init()98 bool Security::Init
99 (
100 )
101 {
102 	Msg* msg = new Msg( "SecurityCmd_SupportedGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
103 	msg->Append( GetNodeId() );
104 	msg->Append( 2 );
105 	msg->Append( GetCommandClassId() );
106 	msg->Append( SecurityCmd_SupportedGet );
107 	msg->Append( GetDriver()->GetTransmitOptions() );
108 	msg->setEncrypted();
109 	GetDriver()->SendMsg( msg, Driver::MsgQueue_Security);
110 	return true;
111 }
112 //-----------------------------------------------------------------------------
113 // <Security::RequestState>
114 // Request current state from the device
115 //-----------------------------------------------------------------------------
ExchangeNetworkKeys()116 bool Security::ExchangeNetworkKeys
117 (
118 )
119 {
120 	if (GetNodeUnsafe()->IsAddingNode()) {
121 		Msg * msg = new Msg ("SecurityCmd_SchemeGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
122 		msg->Append( GetNodeId() );
123 		msg->Append( 3 );
124 		msg->Append( GetCommandClassId() );
125 		msg->Append( SecurityCmd_SchemeGet );
126 		msg->Append( 0 );
127 		msg->Append( GetDriver()->GetTransmitOptions() );
128 		/* SchemeGet is unencrypted */
129 		GetDriver()->SendMsg(msg, Driver::MsgQueue_Security);
130 		return true;
131 	}
132 	return false;
133 }
134 
135 
136 //-----------------------------------------------------------------------------
137 // <Security::RequestState>
138 // Request current state from the device
139 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)140 bool Security::RequestState
141 (
142 		uint32 const _requestFlags,
143 		uint8 const _instance,
144 		Driver::MsgQueue const _queue
145 )
146 {
147 #if 0
148 	if( _requestFlags & RequestFlag_Static )
149 	{
150 
151 	}
152 	return false;
153 #endif
154 	return true;
155 }
156 
157 //-----------------------------------------------------------------------------
158 // <Security::RequestValue>
159 // Request current state from the device
160 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _index,uint8 const _instance,Driver::MsgQueue const _queue)161 bool Security::RequestValue
162 (
163 		uint32 const _requestFlags,
164 		uint8 const _index,
165 		uint8 const _instance,
166 		Driver::MsgQueue const _queue
167 )
168 {
169 	Log::Write(LogLevel_Info, GetNodeId(), "Got a RequestValue Call");
170 	return true;
171 }
172 
173 
HandleSupportedReport(uint8 const * _data,uint32 const _length)174 bool Security::HandleSupportedReport
175 (
176 		uint8 const* _data,
177 		uint32 const _length
178 )
179 {
180 
181 #ifdef DEBUG
182 	PrintHex("Security Classes", _data, _length);
183 #endif
184 	GetNodeUnsafe()->SetSecuredClasses(_data, _length);
185 	return true;
186 }
187 
188 //-----------------------------------------------------------------------------
189 // <Security::HandleMsg>
190 // Handle a message from the Z-Wave network
191 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)192 bool Security::HandleMsg
193 (
194 		uint8 const* _data,
195 		uint32 const _length,
196 		uint32 const _instance	// = 1
197 )
198 {
199 	switch( (SecurityCmd)_data[0] )
200 	{
201 		case SecurityCmd_SupportedReport:
202 		{
203 			/* this is a list of CommandClasses that should be Encrypted.
204 			 * and it might contain new command classes that were not present in the NodeInfoFrame
205 			 * so we have to run through, mark existing Command Classes as SetSecured (so SendMsg in the Driver
206 			 * class will route the unecrypted messages to our SendMsg) and for New Command
207 			 * Classes, create them, and of course, also do a SetSecured on them.
208 			 *
209 			 * This means we must do a SecurityCmd_SupportedGet request ASAP so we dont have
210 			 * Command Classes created after the Discovery Phase is completed!
211 			 */
212 			Log::Write(LogLevel_Info, GetNodeId(), "Received SecurityCmd_SupportedReport from node %d", GetNodeId() );
213 			m_secured = true;
214 			if( ValueBool* value = static_cast<ValueBool*>( GetValue( _instance, 0 ) ) )
215 			{
216 				value->OnValueRefreshed( m_secured );
217 				value->Release();
218 			}
219 			HandleSupportedReport(&_data[2], _length-2);
220 			break;
221 		}
222 		case SecurityCmd_SchemeReport:
223 		{
224 			Log::Write(LogLevel_Info, GetNodeId(), "Received SecurityCmd_SchemeReport from node %d: %d", GetNodeId(), _data[1]);
225 			uint8 schemes = _data[1];
226 			if (m_schemeagreed == true) {
227 				Log::Write(LogLevel_Warning, GetNodeId(), "   Already Received a SecurityCmd_SchemeReport from the node. Ignoring");
228 				break;
229 			}
230 			if( schemes == SecurityScheme_Zero )
231 			{
232 				/* We're good to go.  We now should send our NetworkKey to the device if this is the first
233 				 * time we have seen it
234 				 */
235 				Log::Write(LogLevel_Info, GetNodeId(), "    Security scheme agreed." );
236 				/* create the NetworkKey Packet. EncryptMessage will encrypt it for us (And request the NONCE) */
237 				Msg * msg = new Msg ("SecurityCmd_NetworkKeySet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
238 				msg->Append( GetNodeId() );
239 				msg->Append( 18 );
240 				msg->Append( GetCommandClassId() );
241 				msg->Append( SecurityCmd_NetworkKeySet );
242 				for (int i = 0; i < 16; i++)
243 					msg->Append(GetDriver()->GetNetworkKey()[i]);
244 				msg->Append( GetDriver()->GetTransmitOptions() );
245 				msg->setEncrypted();
246 				GetDriver()->SendMsg( msg, Driver::MsgQueue_Security);
247 				m_schemeagreed = true;
248 			}
249 			else
250 			{
251 				/* No common security scheme.  The device should continue as an unsecured node.
252 				 * but Some Command Classes might not be present...
253 				 */
254 				Log::Write(LogLevel_Warning,  GetNodeId(), "    No common security scheme.  The device will continue as an unsecured node." );
255 			}
256 			break;
257 		}
258 		case SecurityCmd_NetworkKeySet:
259 		{
260 			/* we shouldn't get a NetworkKeySet from a node if we are the controller
261 			 * as we send it out to the Devices
262 			 */
263 			Log::Write(LogLevel_Info,  GetNodeId(), "Received SecurityCmd_NetworkKeySet from node %d", GetNodeId() );
264 			break;
265 		}
266 		case SecurityCmd_NetworkKeyVerify:
267 		{
268 			/* if we can decrypt this packet, then we are assured that our NetworkKeySet is successfull
269 			 * and thus should set the Flag referenced in SecurityCmd_SchemeReport
270 			 */
271 			Log::Write(LogLevel_Info,  GetNodeId(), "Received SecurityCmd_NetworkKeyVerify from node %d", GetNodeId() );
272 			/* now as for our SupportedGet */
273 			Msg* msg = new Msg( "SecurityCmd_SupportedGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
274 			msg->Append( GetNodeId() );
275 			msg->Append( 2 );
276 			msg->Append( GetCommandClassId() );
277 			msg->Append( SecurityCmd_SupportedGet );
278 			msg->Append( GetDriver()->GetTransmitOptions() );
279 			msg->setEncrypted();
280 			GetDriver()->SendMsg( msg, Driver::MsgQueue_Security);
281 
282 			break;
283 		}
284 		case SecurityCmd_SchemeInherit:
285 		{
286 			/* only used in a Controller Replication Type enviroment.
287 			 *
288 			 */
289 			Log::Write(LogLevel_Info,  GetNodeId(), "Received SecurityCmd_SchemeInherit from node %d", GetNodeId() );
290 			break;
291 		}
292 		/* the rest of these should be handled by the Driver Code (in Driver::ProcessMsg) */
293 		case SecurityCmd_NonceGet:
294 		case SecurityCmd_NonceReport:
295 		case SecurityCmd_MessageEncap:
296 		case SecurityCmd_MessageEncapNonceGet:
297 		{
298 			Log::Write(LogLevel_Warning, GetNodeId(), "Received a Security Message that should have been handled in the Driver");
299 			break;
300 		}
301 		default:
302 		{
303 			return false;
304 		}
305 	}
306 
307 	return true;
308 }
309 
310 //-----------------------------------------------------------------------------
311 // <Security::CreateVars>
312 // Create the values managed by this command class
313 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)314 void Security::CreateVars
315 (
316 		uint8 const _instance
317 )
318 {
319 	if( Node* node = GetNodeUnsafe() )
320 	{
321 		node->CreateValueBool( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 0, "Secured", "", true, false, false, 0 );
322 	}
323 }
324 
325