internal static byte[] EncryptData(byte[] plainData, HapSession session) { var resultData = new byte[0]; for (int offset = 0; offset < plainData.Length;) { int length = Math.Min(plainData.Length - offset, 1024); var dataLength = BitConverter.GetBytes((short)length); resultData = resultData.Concat(dataLength).ToArray(); var zeros = new byte[] { 0, 0, 0, 0 }; var nonce = new Nonce(zeros, BitConverter.GetBytes(session.OutboundBinaryMessageCount++)); var dataToEncrypt = new byte[length]; Array.Copy(plainData, offset, dataToEncrypt, 0, length); // Use the AccessoryToController key to decrypt the data var encryptedData = AeadAlgorithm.ChaCha20Poly1305.Encrypt(Key.Import(AeadAlgorithm.ChaCha20Poly1305, session.AccessoryToControllerKey, KeyBlobFormat.RawSymmetricKey), nonce, dataLength, dataToEncrypt); resultData = resultData.Concat(encryptedData).ToArray(); offset += length; } return(resultData); }
internal CharacteristicReturn <Characteristic> Put(byte[] v, HapSession session, HomeKitServer server) { var json = Encoding.UTF8.GetString(v); var characteristics = JsonConvert.DeserializeObject <CharacteristicReturn <SentCharacteristic> >(json); var all = server.GetAccessories(); var retCharactersitcs = new List <Characteristic>(); foreach (var sendCharacteristic in characteristics.Characteristics) { var accessoryId = sendCharacteristic.AccessoryNumber; var characteristicId = sendCharacteristic.Id; var accessory = all.SingleOrDefault(a => a.Id == accessoryId); if (accessory == null) { _logger.LogError($"Could not find accessory with id {accessoryId}"); continue; } Characteristic characteristic = null; foreach (var service in accessory.Services) { var c = service.Characteristics.SingleOrDefault(a => a.Id == characteristicId); if (c != null) { characteristic = c; break; } } if (characteristic == null) { _logger.LogError($"Could not find characterstic with id {characteristicId}"); continue; } if (sendCharacteristic.EventBasedNotification.HasValue && sendCharacteristic.EventBasedNotification.Value) { server.RegisterNotifications(characteristic, session); } characteristic.Value = sendCharacteristic.Value; server.NotifyValueChanged(characteristic); characteristic.AccessoryId = accessoryId; retCharactersitcs.Add(characteristic); } return(new CharacteristicReturn <Characteristic> { Characteristics = new List <Characteristic>() }); }
public static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("You need add the path of your HAP-NodeJS directory to continue like an argument."); Console.WriteLine("Ex: mono HapSharp.Host.Console.exe /Users/user/HapSharp/HAP-NodeJS"); return; } var hapNodeJsPath = args[0]; if (!Directory.Exists(hapNodeJsPath)) { throw new DirectoryNotFoundException(hapNodeJsPath); } //This class provides the handling of the output log messages var monitor = new ConsoleMonitor(); //Our HAP session manages our runner, this step only adds our prefered monitor var session = new HapSession(monitor); //Now we need add Accessories and MessagesDelegates //Our first element must be a bridgeCore, it contains all other accesories in session session.Add <BridgedCoreMessageDelegate> (new BridgedCore("Console Net Bridge", "22:32:43:54:45:14")); //Now, we add all the accessories from the Shared Project //LightMessageDelegate handles the logic of a simple light with two states on/off session.Add <CustomLightMessageDelegate>(new LightAccessory("Humidity Example", "A1:32:45:66:57:73")); //There are different ways of add new accessories, it depends in your needs how to do it var motionSensorAccessory = new MotionSensorAccessory("Motion Sensor Example", "A1:42:35:67:55:73"); var motionSensorMessageDelegate = new CustomMotionSensorMessageDelegate(motionSensorAccessory); session.Add(new AccessoryHost(motionSensorAccessory, motionSensorMessageDelegate)); var regulableLightAccessory = new RegulableLightAccessory("Regulable Light Example", "A1:A2:35:A7:15:73"); session.Add(regulableLightAccessory, new CustomRegulableLightMessageDelegate(regulableLightAccessory)); session.Add <CustomTemperatureMessageDelegate>(new TemperatureAccessory("Temperature Example", "11:32:75:36:17:73")); //Also you can create custom libraries or nugets to share with the community! session.Add <NugetHumidityMessageDelegate>(new CustomHumidityAccessory("Example of Humidity", "A2:12:41:67:55:73")); //Now with all accessories added to our host we can start the session //¡ Before run next step you will need a Broker running in background ! //Remember follow the Requisites steps in Github readme!! //https://github.com/netonjm/HapSharp session.Start(hapNodeJsPath); Console.ReadKey(); }
internal void RegisterNotifications(Characteristic characteristic, HapSession session) { lock (_eventBasedNotifications) { if (!_eventBasedNotifications.ContainsKey(session.ClientUsername)) { _eventBasedNotifications.Add(session.ClientUsername, new List <Characteristic>()); } if (!_eventBasedNotifications[session.ClientUsername].Contains(characteristic)) { _eventBasedNotifications[session.ClientUsername].Add(characteristic); } } }
internal void RegisterNotifications(Characteristic characteristic, HapSession session) { lock (_eventBasedNotifications) { if (!_eventBasedNotifications.ContainsKey(characteristic)) { _eventBasedNotifications.Add(characteristic, new List <HapSession>()); } if (!_eventBasedNotifications[characteristic].Contains(session)) { _eventBasedNotifications[characteristic].Add(session); } } }
static void Main(string[] args) { //This class provides the handling of the output log messages var monitor = new ConsoleMonitor(); //Our HAP session manages our runner, this step only adds our prefered monitor var session = new HapSession(monitor) { Sudo = true, //if we want execute our host with privileges AllowedHosts = new string[] { AllowedHost } //ensures your host is the expected, if not an exception raises }; //Now we need add Accessories and MessagesDelegates //Our first element must be a bridgeCore, it contains all other accesories in session session.Add <BridgedCoreMessageDelegate> (new BridgedCore("Raspian Net Bridge", "22:32:43:54:45:14")); //Now, we add all the accessories from the Shared Project //LightMessageDelegate handles the logic of a simple light with two states on/off session.Add <CustomLightMessageDelegate> (new LightAccessory("Humidity Example", "A1:32:45:66:57:73")); //There are different ways of add new accessories, it depends in your needs how to do it var motionSensorAccessory = new MotionSensorAccessory("Motion Sensor Example", "A1:42:35:67:55:73"); var motionSensorMessageDelegate = new CustomMotionSensorMessageDelegate(motionSensorAccessory); session.Add(new AccessoryHost(motionSensorAccessory, motionSensorMessageDelegate)); var regulableLightAccessory = new RegulableLightAccessory("Regulable Light Example", "A1:A2:35:A7:15:73"); session.Add(regulableLightAccessory, new CustomRegulableLightMessageDelegate(regulableLightAccessory)); session.Add <CustomTemperatureMessageDelegate> (new TemperatureAccessory("Temperature Example", "11:32:75:36:17:73")); //Now with all accessories added to our host we can start the session //¡ Before run next step you will need a Broker running in background ! //Remember follow the Requisites steps in Github readme!! //https://github.com/netonjm/HapSharp session.Start(HapNodeJsPath); while (true) { } }
public bool SendNotification(Characteristic characteristic, HapSession session) { var baseChar = new CharacteristicBase(characteristic.DefaultType) { Id = characteristic.Id, Value = characteristic.Value, AccessoryId = characteristic.Service.Accessory.Id }; var charReturn = new CharacteristicsList <CharacteristicBase> { Characteristics = new List <CharacteristicBase> { baseChar } }; var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(charReturn, HapMiddleware.JsonSettings)); var response = HttpServerConnection.GetHttpResponse("EVENT/1.0", "application/hap+json", data, DateTime.Now); _logger.LogTrace($"Writing {Encoding.UTF8.GetString(response)}"); lock (session) { try { var encrypted = HttpServerConnection.EncryptData(response, session); session.Client.Client.Send(encrypted); return(true); } catch (Exception e) { _logger.LogError(e, "Could not set value"); } } return(false); }
internal static byte[] DecryptData(byte[] content, HapSession session) { var encryptionResult = new byte[0]; for (int offset = 0; offset < content.Length;) { // The first type bytes represent the length of the data. // byte[] twoBytes = new Byte[] { content[0], content[1] }; offset += 2; UInt16 frameLength = BitConverter.ToUInt16(twoBytes, 0); int availableDataLength = content.Length - offset; var message = content.AsSpan(offset, availableDataLength); var zeros = new byte[] { 0, 0, 0, 0 }; var nonce = new Nonce(zeros, BitConverter.GetBytes(session.InboundBinaryMessageCount++)); // Use the AccessoryToController key to decrypt the data. // var decrypt = AeadAlgorithm.ChaCha20Poly1305.Decrypt(Key.Import(AeadAlgorithm.ChaCha20Poly1305, session.ControllerToAccessoryKey, KeyBlobFormat.RawSymmetricKey), nonce, twoBytes, message, out var output); if (!decrypt) { throw new InvalidDataException(); } encryptionResult = encryptionResult.Concat(output).ToArray(); offset += (18 + frameLength); } return(encryptionResult); }
static void Main(string[] args) { //This class provides the handling of the output log messages var monitor = new ConsoleMonitor(); //Our HAP session manages our runner, this step only adds our prefered monitor var session = new HapSession(monitor) { Sudo = true, //if we want execute our host with privileges }; //Now we need add Accessories and MessagesDelegates //Our first element must be a bridgeCore, it contains all other accesories in session session.Add <BridgedCoreMessageDelegate> (new BridgedCore("Raspian Net Bridge", "22:32:43:54:45:14")); //Now, we add all the accessories from the Shared Project session.Add <CustomLightMessageDelegate> (new LightAccessory("Light 1", "A1:32:45:66:57:73")); session.Start(HapNodeJsPath); while (true) { } }
internal Task HandleClient() { _remoteEndPoint = _client.Client.RemoteEndPoint; _logger.LogDebug($"Start client socket on port {_remoteEndPoint}"); var session = Guid.NewGuid().ToString(); _connectionSession = _middleware.GetSession(session); _connectionSession.Client = _client; _client.ReceiveTimeout = 0; try { using (var networkStream = _client.GetStream()) { byte[] receiveBuffer = new byte[_client.ReceiveBufferSize]; while (_client.Connected) { _logger.LogDebug("Waiting for more data from the client...."); // This is blocking and will wait for data to come from the client. // var bytesRead = networkStream.Read(receiveBuffer, 0, _client.ReceiveBufferSize);; lock (_connectionSession) { if (bytesRead == 0) { _logger.LogDebug( "**************************** REQUEST RECEIVED but no data available *************************"); break; } var content = receiveBuffer.AsSpan(0, bytesRead).ToArray(); if (_connectionSession.IsVerified) { _logger.LogDebug("Already in a verified state.."); var encryptionResult = DecryptData(content, _connectionSession); content = encryptionResult; } //_logger.LogTrace($"Read {Encoding.UTF8.GetString(content)}"); var ms = new MemoryStream(content); var sr = new StreamReader(ms); var request = sr.ReadLine(); var tokens = request.Split(' '); if (tokens.Length != 3) { _middleware.TerminateSession(session); throw new Exception("Invalid HTTP request line"); } var method = tokens[0].ToUpper(); var url = tokens[1].Trim('/'); _logger.LogDebug($"Request {method} on path {url}"); string line; Dictionary <string, string> httpHeaders = new Dictionary <string, string>(); while ((line = sr.ReadLine()) != null) { if (String.IsNullOrEmpty(line)) { break; } var lineSplit = line.Split(":", StringSplitOptions.RemoveEmptyEntries); if (lineSplit.Length != 2) { _logger.LogWarning($"Invalid header format in {line}"); continue; } httpHeaders.Add(lineSplit[0].ToLower(), lineSplit[1]); } var contentLengthHeader = httpHeaders.ContainsKey("content-length"); var contentLenght = 0; if (contentLengthHeader) { contentLenght = Convert.ToInt32( httpHeaders.Single(a => a.Key == "content-length").Value); } var datLen = content.Length; var data = content.AsSpan(datLen - contentLenght, contentLenght).ToArray(); var result = _middleware.Invoke(session, url, method, data, _session); var response = GetHttpResponse("HTTP/1.1", result.Item1, result.Item2, DateTime.Now); _logger.LogTrace($"Writing {Encoding.UTF8.GetString(response)}"); if (_connectionSession.IsVerified && !_connectionSession.SkipFirstEncryption) { response = EncryptData(response, _connectionSession); networkStream.Write(response, 0, response.Length); networkStream.Flush(); } else { networkStream.Write(response, 0, response.Length); networkStream.Flush(); if (_connectionSession.SkipFirstEncryption) { _connectionSession.SkipFirstEncryption = false; } } _logger.LogDebug( "**************************** REQUEST DONE *************************"); } } _middleware.TerminateSession(session); _client.Close(); _client.Dispose(); _httpServer.ConnectionClosed(this); } } catch (Exception e) { _logger.LogError(e, "Error occured in http server"); _middleware.TerminateSession(session); } return(Task.CompletedTask); }
public PairVerifyReturn Post(Tlv parts, HapSession session) { var state = parts.GetTypeAsInt(Constants.State); if (state == 1) { _logger.LogDebug("* Pair Verify Step 1/4"); _logger.LogDebug("* Verify Start Request"); var clientPublicKey = parts.GetType(Constants.PublicKey); byte[] privateKey = new byte[32]; Random random = new Random(); random.NextBytes(privateKey); var publicKey = Curve25519.GetPublicKey(privateKey); var sharedSecret = Curve25519.GetSharedSecret(privateKey, clientPublicKey); var serverUsername = Encoding.UTF8.GetBytes(HapControllerServer.HapControllerId); var material = publicKey.Concat(serverUsername).Concat(clientPublicKey).ToArray(); var accessoryLtsk = StringToByteArray(HapControllerServer.HapControllerLtsk); var proof = Chaos.NaCl.Ed25519.Sign(material, accessoryLtsk); var hdkf = new HkdfSha512(); var hkdfEncKey = hdkf.DeriveBytes(SharedSecret.Import(sharedSecret), Encoding.UTF8.GetBytes("Pair-Verify-Encrypt-Salt"), Encoding.UTF8.GetBytes("Pair-Verify-Encrypt-Info"), 32); var encoder = new Tlv(); encoder.AddType(Constants.Identifier, serverUsername); encoder.AddType(Constants.Signature, proof); var plaintext = TlvParser.Serialize(encoder); var zeros = new byte[] { 0, 0, 0, 0 }; var nonce = new Nonce(zeros, Encoding.UTF8.GetBytes("PV-Msg02")); var encryptedOutput = AeadAlgorithm.ChaCha20Poly1305.Encrypt( Key.Import(AeadAlgorithm.ChaCha20Poly1305, hkdfEncKey, KeyBlobFormat.RawSymmetricKey), nonce, new byte[0], plaintext); var responseTlv = new Tlv(); responseTlv.AddType(Constants.State, 2); responseTlv.AddType(Constants.EncryptedData, encryptedOutput); responseTlv.AddType(Constants.PublicKey, publicKey); // Store the details on the session. // session.ClientPublicKey = clientPublicKey; session.PrivateKey = privateKey; session.PublicKey = publicKey; session.SharedSecret = sharedSecret; session.HkdfPairEncKey = hkdfEncKey; var encSalt = Encoding.UTF8.GetBytes("Control-Salt"); var infoRead = Encoding.UTF8.GetBytes("Control-Read-Encryption-Key"); var infoWrite = Encoding.UTF8.GetBytes("Control-Write-Encryption-Key"); session.AccessoryToControllerKey = hdkf.DeriveBytes(SharedSecret.Import(sharedSecret), encSalt, infoRead, 32); session.ControllerToAccessoryKey = hdkf.DeriveBytes(SharedSecret.Import(sharedSecret), encSalt, infoWrite, 32); return(new PairVerifyReturn { TlvData = responseTlv, Ok = true, HapSession = session }); } if (state == 3) { _logger.LogDebug("* Pair Verify Step 3/4"); _logger.LogDebug("* Verify Finish Request"); var encryptedData = parts.GetType(Constants.EncryptedData); var zeros = new byte[] { 0, 0, 0, 0 }; var nonce = new Nonce(zeros, Encoding.UTF8.GetBytes("PV-Msg03")); var decrypt = AeadAlgorithm.ChaCha20Poly1305.Decrypt(Key.Import(AeadAlgorithm.ChaCha20Poly1305, session.HkdfPairEncKey, KeyBlobFormat.RawSymmetricKey), nonce, new byte[0], encryptedData, out var output); if (!decrypt) { var errorTlv = new Tlv(); errorTlv.AddType(Constants.State, 4); errorTlv.AddType(Constants.Error, ErrorCodes.Authentication); return(new PairVerifyReturn { TlvData = errorTlv, Ok = false }); } var subData = TlvParser.Parse(output); var clientUserName = subData.GetType(Constants.Identifier); var signature = subData.GetType(Constants.Signature); var clientPublicKey = StringToByteArray(HapControllerServer.HapControllerLtpk); var material = session.ClientPublicKey.Concat(clientUserName).Concat(session.PublicKey).ToArray(); session.ClientUsername = Automatica.Core.Driver.Utility.Utils.ByteArrayToString(in clientUserName); if (!Chaos.NaCl.Ed25519.Verify(signature, material, clientPublicKey)) { var errorTlv = new Tlv(); errorTlv.AddType(Constants.State, 4); errorTlv.AddType(Constants.Error, ErrorCodes.Authentication); return(new PairVerifyReturn { TlvData = errorTlv, Ok = false }); } var responseTlv = new Tlv(); responseTlv.AddType(Constants.State, 4); session.IsVerified = true; session.SkipFirstEncryption = true; return(new PairVerifyReturn { Ok = true, TlvData = responseTlv }); } return(null); }