context: /*
      This file is part of HomeGenie Project source code.
  
      HomeGenie is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      HomeGenie is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with HomeGenie.  If not, see <http://www.gnu.org/licenses/>.  
      
      Code inspired from Security CommandClass in OpenZWave
   
  */
  
  /*
   *     Author: https://github.com/snagytx
   *     Project Homepage: http://homegenie.it
   */
  
  using ZWaveLib.Values;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using ZWaveLib.Devices;
  using System.Diagnostics;
  
  namespace ZWaveLib.CommandClasses
  {
      
      public class SecutiryPayload
      {
          public byte[] message;
          public int length;
          public int part;
      }
  
      public class SecurityData
      {
          private byte[] encryptionKey = null;
          private byte[] controllerCurrentNonce = null;
          private byte[] privateNetworkKey = null;
          //private byte[] privateNetworkKey = new byte[] { 0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78, 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0 };
          //private byte[] privateNetworkKey = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
          private ZWaveNode parentNode = null;
      
          public byte[] DeviceCurrentNonce = null;
  
          public Stopwatch ControllerNonceTimer = new Stopwatch();
          public Stopwatch DeviceNonceTimer = new Stopwatch();
  
          public bool SchemeAgreed = false;
          public bool IsWaitingForNonce = false;
          public bool IsAddingNode = false;
          public bool IsNetworkKeySet = false;
  
          public SecurityData(ZWaveNode node){
              parentNode = node;
          }
       
          public void SetPrivateNetworkKey(byte[] key){
              privateNetworkKey = key;
          }
  
          public byte[] GetPrivateNetworkKey(){
              return privateNetworkKey;
          }
  
          public byte[] GeneratePrivateNetworkKey()
          {
              privateNetworkKey = new byte[16];
              Random rnd = new Random();
              rnd.NextBytes(privateNetworkKey);
  
              // notify the controller that the privateNetworkKey was generated so that it can save it
              ZWaveEvent keyGenEvent = new ZWaveEvent(parentNode, EventParameter.SecurityGeneratedKey, 0, 0);
              parentNode.RaiseUpdateParameterEvent(keyGenEvent);
  
              return privateNetworkKey;
          }
  
          public void GenerateControllerCurrentNonce(){
              byte[] localControllerCurrentNonce = new byte[8];
  
              if (controllerCurrentNonce == null)
              {
                  controllerCurrentNonce = new byte[8];
                  // we needs to generate one and save it for the next call;
                  Random rnd = new Random();
                  rnd.NextBytes(controllerCurrentNonce);
              }
  
              Utility.DebugLog(DebugMessageType.Information, "ControllerCurrentNonce: " + Utility.ByteArrayToString(localControllerCurrentNonce));
              //return localControllerCurrentNonce;
          }
  
          public byte[] GetControllerCurrentNonce(bool clearNonce = false)
          {
              byte[] localControllerCurrentNonce = new byte[8];
  
              Array.Copy(controllerCurrentNonce, localControllerCurrentNonce, 8);
  
              if (clearNonce)
              {
                  // Ideally we should get in here ONLY when decrypting a message from the device.
                  // This would cause the controllerCurrentNonce to be re-generated which would
                  // give us the most secure communication, if we see issues we just don't pass
                  // the clearNonce argument
                  controllerCurrentNonce = null;
              }
  
              return localControllerCurrentNonce;
          }
  
          public byte[] EncryptionKey 
          {
              get 
              {
                  // the EncryptionKey needs to be generated in two cases:
                  // 1 - encryptionKey is null
                  // 2 - once the PrivateNetworkKey was sent to the device
  
                  // these three arrays seems to be this like this 
                  byte[] initialNetworkKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };                
                  byte[] encryptPassword = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA };
                  byte[] authPassword = new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
  
                  byte[] networkKey;
  
                  if (IsAddingNode && !IsNetworkKeySet)
                  {
                      networkKey = initialNetworkKey;
                      Utility.DebugLog(DebugMessageType.Information, "In SetupNetworkKey  - in node inclusion mode.");
                  }
                  else
                  {
                      if (privateNetworkKey == null)
                          GeneratePrivateNetworkKey();
                      networkKey = privateNetworkKey;
                  }
  
                  encryptionKey = AesWork.EncryptEcbMessage(networkKey, encryptPassword);
                  AuthorizationKey = AesWork.EncryptEcbMessage(networkKey, authPassword);
  
                  Utility.DebugLog(DebugMessageType.Information, "      networkKey: " + Utility.ByteArrayToString(networkKey));
                  Utility.DebugLog(DebugMessageType.Information, "   encryptionKey: " + Utility.ByteArrayToString(encryptionKey));
                  Utility.DebugLog(DebugMessageType.Information, "AuthorizationKey: " + Utility.ByteArrayToString(AuthorizationKey));
  
                  return encryptionKey;
              }
          }
          public byte[] AuthorizationKey = null;
  
          public List<SecutiryPayload> SecurePayload = new List<SecutiryPayload>();
          public int SequenceCounter = 0;
      }
  
      public class Security : ICommandClass
      {
  
          //public byte[] PrivateNetworkKey = new byte[] { 0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78, 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0 };
          //public static byte[] PrivateNetworkKey = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
  
          public CommandClass GetClassId()
          {
              return CommandClass.Security;
          }
  
          public ZWaveEvent GetEvent(ZWaveNode node, byte[] message)
          {
              byte cmdType = message[1];
              byte[] decryptedMessage;
              ZWaveEvent nodeEvent = null;
  
              int startOffset = 1;
              switch (cmdType)
              {
              case (byte)SecurityCommand.SupportedReport:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_SUPPORTED_REPORT for node: " + node.Id);
                  /* this is a list of CommandClasses that should be Encrypted.
                   * and it might contain new command classes that were not present in the NodeInfoFrame
                   * so we have to run through, mark existing Command Classes as SetSecured (so SendMsg in the Driver
                   * class will route the unecrypted messages to our SendMsg) and for New Command
                   * Classes, create them, and of course, also do a SetSecured on them.
                   *
                   * This means we must do a SecurityCmd_SupportedGet request ASAP so we dont have
                   * Command Classes created after the Discovery Phase is completed!
                   */
                  byte[] securedClasses = new byte[message.Length - 3];
                  Array.Copy(message, 3, securedClasses, 0, message.Length - 3);
                  nodeEvent = new ZWaveEvent(node, EventParameter.SecurityNodeInformationFrame, securedClasses, 0);
                  break;
  
              case (byte)SecurityCommand.SchemeReport:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_SCHEME_REPORT for node: " + node.Id + ", " + (startOffset + 1));
                  int schemes = message[startOffset + 1];
                  SecurityData nodeSecurityData = GetSecurityData(node);
                  if (nodeSecurityData.SchemeAgreed)
                  {
                      break;
                  }
                  else if (schemes == (byte)SecurityScheme.SchemeZero)
                  {
                      SetNetworkKey(node);
                      nodeSecurityData.SchemeAgreed = true;
                  }
                  else
                  {
                      Utility.DebugLog(DebugMessageType.Information, "   No common security scheme.  The device will continue as an unsecured node.");
                  }
                  break;
  
              case (byte)SecurityCommand.NonceGet:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_NONCE_GET for node: " + node.Id);
  
                  /* the Device wants to send us a Encrypted Packet, and thus requesting for our latest NONCE */
                  SendNonceReport(node);
                  break;
  
              case (byte)SecurityCommand.MessageEncap:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_MESSAGE_ENCAP for node: " + node.Id);
  
                  /* We recieved a Encrypted single packet from the Device. Decrypt it. */
                  decryptedMessage = DecryptMessage(node, message, startOffset);
                  if (decryptedMessage != null)
                      nodeEvent = new ZWaveEvent(node, EventParameter.SecurityDecriptedMessage, decryptedMessage, 0);
                  break;
  
              case (byte)SecurityCommand.NonceReport:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_NONCE_REPORT for node: " + node.Id);
  
                  /* we recieved a NONCE from a device, so assume that there is something in a queue to send out */
                  ProcessNonceReport(node, message, startOffset);
                  break;
  
              case (byte)SecurityCommand.MessageEncapNonceGet:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_MESSAGE_ENCAP_NONCE_GET for node: " + node.Id);
  
                  /* we recieved a encrypted packet from the device, and the device is also asking us to send a
                       * new NONCE to it, hence there must be multiple packets.*/
                  decryptedMessage = DecryptMessage(node, message, startOffset);
                  if (decryptedMessage != null)
                      nodeEvent = new ZWaveEvent(node, EventParameter.SecurityDecriptedMessage, decryptedMessage, 0);
                  /* Regardless of the success/failure of Decrypting, send a new NONCE */
                  SendNonceReport(node);
                  break;
  
              case (byte)SecurityCommand.NetworkKeySet:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_NETWORK_KEY_SET for node: " + node.Id + ", " + (startOffset + 1));
  
                  /* we shouldn't get a NetworkKeySet from a node if we are the controller
                       * as we send it out to the Devices
                      */
                  break;
  
              case (byte)SecurityCommand.NetworkKeyVerify:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_NETWORK_KEY_VERIFY for node: " + node.Id + ", " + (startOffset + 1));
  
                  /*
                       * if we can decrypt this packet, then we are assured that our NetworkKeySet is successfull
                       * and thus should set the Flag referenced in SecurityCmd_SchemeReport
                      */
                  GetSupported(node);
                  break;
  
              case (byte)SecurityCommand.SchemeInherit:
                  Utility.DebugLog(DebugMessageType.Information, "Received COMMAND_SCHEME_INHERIT for node: " + node.Id);
                  /* only used in a Controller Replication Type enviroment. */
                  break;
  
              }
  
              return nodeEvent;
          }
  
          #region Public Security Command Class methods
  
          public static void GetSupported(ZWaveNode node)
          {
              var message = ZWaveMessage.CreateRequest(node.Id, new byte[] { 
                  (byte)CommandClass.Security,
                  (byte)SecurityCommand.SupportedGet
              });
              SendMessage(node, message);
          }
  
          public static void GetScheme(ZWaveNode node)
          {
              node.SendRequest(new byte[]{
                  (byte)CommandClass.Security,
                  (byte)SecurityCommand.SchemeGet,
                  0
              });
          }
  
          private static void SetNetworkKey(ZWaveNode node)
          {
              byte[] t_msg = new byte[18];
              t_msg[0] = (byte)CommandClass.Security;
              t_msg[1] = (byte)SecurityCommand.NetworkKeySet;
              var privateNetworkKey = GetSecurityData(node).GetPrivateNetworkKey();
              if (privateNetworkKey == null) {
                  privateNetworkKey = GetSecurityData(node).GeneratePrivateNetworkKey();
              }
              Array.Copy(privateNetworkKey, 0, t_msg, 2, 16);
              byte[] f_msg = ZWaveMessage.CreateRequest(node.Id, t_msg);
              SendMessage(node, f_msg);
          }
  
          public static void SendMessage(ZWaveNode node, byte[] message)
          {
              int length = message[5];
  
              Utility.DebugLog(DebugMessageType.Information, "In sendMsg - SecurityHandler");
  
              if (message.Length < 7)
              {
                  Utility.DebugLog(DebugMessageType.Information, "Message too short");
              }
              if (message[3] != 0x13)
              {
                  Utility.DebugLog(DebugMessageType.Information, "Invalid Message type");
              }
  
              if (length > 28)
              {
                  SecutiryPayload t_payload = new SecutiryPayload();
                  t_payload.length = 28;
                  t_payload.part = 1;
                  byte[] t_message = new byte[t_payload.length];
                  System.Array.Copy(message, 6, t_message, 0, t_payload.length);
                  t_payload.message = t_message;
                  QueuePayload(node, t_payload);
  
                  SecutiryPayload t_payload2 = new SecutiryPayload();
                  t_payload2.length = length - 28;
                  t_payload2.part = 2;
                  byte[] t_message2 = new byte[t_payload.length];
                  System.Array.Copy(message, 34, t_message2, 0, t_payload2.length);
                  t_payload2.message = t_message2;
                  QueuePayload(node, t_payload2);
              }
              else
              {
                  SecutiryPayload t_payload = new SecutiryPayload();
                  t_payload.length = length;
                  t_payload.part = 0;
                  byte[] t_message = new byte[t_payload.length];
                  System.Array.Copy(message, 6, t_message, 0, t_payload.length);
                  t_payload.message = t_message;
                  QueuePayload(node, t_payload);
              }
          }
  
          public static SecurityData GetSecurityData(ZWaveNode node)
          {
              if (!node.Data.ContainsKey("SecurityData"))
              {
                  node.Data.Add("SecurityData", new SecurityData(node));
              }
              return (SecurityData)node.Data["SecurityData"];
          }
  
          #endregion
  
          #region Private helper methods
  
          private static void QueuePayload(ZWaveNode node, SecutiryPayload payload)
          {
              var nodeSecurityData = GetSecurityData(node);
  
              lock (nodeSecurityData.SecurePayload)
              {
                  nodeSecurityData.SecurePayload.Add(payload);
                  if (nodeSecurityData.DeviceNonceTimer.ElapsedMilliseconds > 10000)
                      nodeSecurityData.IsWaitingForNonce = false;
                  if (!nodeSecurityData.IsWaitingForNonce)
                  {
                      RequestNonce(node);
                  }
              }
          }
  
          private static bool RequestNonce(ZWaveNode node)
          {
              var nodeSecurityData = GetSecurityData(node);
  
              Utility.DebugLog(DebugMessageType.Information, "In sendRequestNonce - SecurityHandler");
  
              if (nodeSecurityData.IsWaitingForNonce)
                  return false;
  
              Utility.DebugLog(DebugMessageType.Information, "In sendRequestNonce - not waiting for Nonce - SecurityHandler");
              nodeSecurityData.IsWaitingForNonce = true;
  
              node.SendRequest(new byte[] {
                  (byte)CommandClass.Security,
                  (byte)SecurityCommand.NonceGet
              });
  
              return true;
          }
  
          private static void SendNonceReport(ZWaveNode node)
          {
              byte[] message = new byte[10];
  
              message[0] = (byte)CommandClass.Security;
              message[1] = (byte)SecurityCommand.NonceReport;
  
              GetSecurityData(node).GenerateControllerCurrentNonce();
  
              Array.Copy(GetSecurityData(node).GetControllerCurrentNonce(), 0, message, 2, 8);
  
              node.SendRequest(message);
  
              GetSecurityData(node).ControllerNonceTimer.Reset();
          }
  
          private static void ProcessNonceReport(ZWaveNode node, byte[] message, int start)
          {
              SecurityData nodeSecurityData = GetSecurityData(node);
              nodeSecurityData.DeviceNonceTimer.Restart();
  
              if (nodeSecurityData.DeviceCurrentNonce == null)
                  nodeSecurityData.DeviceCurrentNonce = new byte[8];
              Array.Copy(message, start + 1, nodeSecurityData.DeviceCurrentNonce, 0, 8);
  
              EncryptMessage(node, message);
              nodeSecurityData.IsWaitingForNonce = false;
  
              // if we still have items in the queue request a new nonce
              if (nodeSecurityData.SecurePayload.Count > 0)
              {
                  RequestNonce(node);
              }
          }
  
          // IN the mesage to be Encrypted
          // OUT - true - message processed and sent - proceed to next one
          //     - false - we need to wait for the nonce report to come
          private static bool EncryptMessage(ZWaveNode node, byte[] message)
          {
              SecurityData nodeSecurityData = GetSecurityData(node);
  
              Utility.DebugLog(DebugMessageType.Information, "In EncryptMessage - secure_payload [" + nodeSecurityData.SecurePayload.Count + "]  - " + nodeSecurityData.DeviceNonceTimer.ElapsedMilliseconds);
  
              // if we get true we need to wait for the new Nonce
              // if we get false we need to proceed
              //            if (sendRequestNonce())
              //                return false;
              if (nodeSecurityData.DeviceNonceTimer.ElapsedMilliseconds > 10000)
                  return false;
  
              SecutiryPayload payload = null;
              lock (nodeSecurityData.SecurePayload)
              {
                  if (nodeSecurityData.SecurePayload.Count > 0)
                  {
                      payload = nodeSecurityData.SecurePayload.First();
                      nodeSecurityData.SecurePayload.Remove(payload);
                  }
              }
  
              if (payload != null)
              {
                  int len = payload.length + 20;
  
                  byte[] t_message = new byte[len];
  
                  int i = 0;
  
                  t_message[i] = (byte)CommandClass.Security;
                  i++;
                  t_message[i] = (byte)SecurityCommand.MessageEncap;
  
                  byte[] initializationVector = new byte[16];
                  for (int a = 0; a < 8; a++)
                  {
                      initializationVector[a] = (byte)0xAA;
                      i++;
                      t_message[i] = initializationVector[a];
                  }
  
                  Array.Copy(nodeSecurityData.DeviceCurrentNonce, 0, initializationVector, 8, 8);
  
                  int sequence = 0;
  
  
gt:                 if (payload.part == 1)