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>()
            });
        }
Exemple #3
0
        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);
                }
            }
        }
Exemple #5
0
        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);
                }
            }
        }
Exemple #6
0
        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)
            {
            }
        }
Exemple #7
0
        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);
        }
Exemple #9
0
        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);
        }
Exemple #11
0
        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);
        }