diff --git a/HapSharp.Core.Example/HapSharp.Core.Example.csproj b/HapSharp.Core.Example/HapSharp.Core.Example.csproj
new file mode 100644
index 0000000..d17a820
--- /dev/null
+++ b/HapSharp.Core.Example/HapSharp.Core.Example.csproj
@@ -0,0 +1,43 @@
+
+
+
+ Debug
+ AnyCPU
+ {3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}
+ Exe
+ HapSharp.Core.Example
+ HapSharp.Core.Example
+ v4.7
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ true
+
+
+ true
+ bin\Release
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}
+ HapSharp.Core
+
+
+
+
\ No newline at end of file
diff --git a/HapSharp.Core.Example/Program.cs b/HapSharp.Core.Example/Program.cs
new file mode 100755
index 0000000..06402a3
--- /dev/null
+++ b/HapSharp.Core.Example/Program.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace HapSharp.Core.Example
+{
+ class MainClass
+ {
+ public static void Main (string[] args)
+ {
+ Console.WriteLine ("Hello World!");
+ }
+ }
+}
diff --git a/HapSharp.Core.Example/Properties/AssemblyInfo.cs b/HapSharp.Core.Example/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..68641e8
--- /dev/null
+++ b/HapSharp.Core.Example/Properties/AssemblyInfo.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle ("HapSharp.Core.Example")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("${AuthorCopyright}")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/HapSharp.Core.Tests/Chacha20poly1305_Tests.cs b/HapSharp.Core.Tests/Chacha20poly1305_Tests.cs
new file mode 100644
index 0000000..673d7a0
--- /dev/null
+++ b/HapSharp.Core.Tests/Chacha20poly1305_Tests.cs
@@ -0,0 +1,39 @@
+using NUnit.Framework;
+
+namespace HapSharp.Core.Tests
+{
+ class Chacha20poly1305_Tests
+ {
+ [Test ()]
+ public void load32_test ()
+ {
+ var test = new int[16];
+ test[1] = 1;
+ test[2] = 2;
+ test[3] = 2;
+ var result = Chacha20poly1305.load32 (test, 0);
+ Assert.AreEqual (33685760, result);
+ }
+
+ [Test ()]
+ public void store32_test ()
+ {
+ var test = new int[16];
+ test[1] = 1;
+ test[2] = 2;
+ test[3] = 2;
+
+ Chacha20poly1305.store32 (test, 0, 2);
+ Assert.AreEqual (2, test[0]);
+ Assert.AreEqual (0, test[1]);
+ Assert.AreEqual (0, test[2]);
+ }
+
+ [Test ()]
+ public void plus_test ()
+ {
+ var result = Chacha20poly1305.plus (5, 6);
+ Assert.AreEqual (11, result);
+ }
+ }
+}
diff --git a/HapSharp.Core.Tests/HapSharp.Core.Tests.csproj b/HapSharp.Core.Tests/HapSharp.Core.Tests.csproj
new file mode 100644
index 0000000..bbed39b
--- /dev/null
+++ b/HapSharp.Core.Tests/HapSharp.Core.Tests.csproj
@@ -0,0 +1,48 @@
+
+
+
+ Debug
+ AnyCPU
+ {7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}
+ Library
+ HapSharp.Core.Tests
+ HapSharp.Core.Tests
+ v4.7
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+
+
+ true
+ bin\Release
+ prompt
+ 4
+
+
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}
+ HapSharp.Core
+
+
+
+
\ No newline at end of file
diff --git a/HapSharp.Core.Tests/Test.cs b/HapSharp.Core.Tests/Test.cs
new file mode 100755
index 0000000..88016d3
--- /dev/null
+++ b/HapSharp.Core.Tests/Test.cs
@@ -0,0 +1,48 @@
+using NUnit.Framework;
+using System;
+namespace HapSharp.Core.Tests
+{
+ [TestFixture ()]
+ public class AccessoryTests
+ {
+ readonly Service service;
+
+ [Test ()]
+ public void Bridge_CheckInitialValues ()
+ {
+ var bridgeGuid = Guid.NewGuid ();
+ var bridge = new Bridge ("bridge", bridgeGuid);
+ Assert.IsTrue (bridge._isBridge);
+ }
+
+ [Test ()]
+ public void Accessory_CheckInitialValues ()
+ {
+ string accessoryName = "test";
+ var accessoryGuid = Guid.NewGuid ();
+
+ var accessory = new Accessory (accessoryName, accessoryGuid);
+ Assert.AreEqual (accessoryGuid, accessory.UUID);
+ Assert.AreEqual (AccessoryCategory.OTHER, accessory.Category);
+ Assert.AreEqual (0, accessory.BridgedAccessories.Count);
+
+ Assert.IsFalse (accessory._isBridge);
+
+ Assert.AreEqual (5, accessory.serviceCharacteristic.Count);
+ Assert.IsTrue (accessory.serviceCharacteristic.TryGetValue (ServiceCharacteristicType.Manufacturer, out string manufacturer));
+ Assert.AreEqual ("Default-Manufacturer", manufacturer);
+
+ Assert.IsTrue (accessory.serviceCharacteristic.TryGetValue (ServiceCharacteristicType.Name, out string displayName));
+ Assert.AreEqual (accessoryName, displayName);
+
+ Assert.IsTrue (accessory.serviceCharacteristic.TryGetValue (ServiceCharacteristicType.Model, out string model));
+ Assert.AreEqual ("Default-Model", model);
+
+ Assert.IsTrue (accessory.serviceCharacteristic.TryGetValue (ServiceCharacteristicType.SerialNumber, out string serial));
+ Assert.AreEqual ("Default-SerialNumber", serial);
+
+ Assert.IsTrue (accessory.serviceCharacteristic.TryGetValue (ServiceCharacteristicType.FirmwareRevision, out string firmwareRevision));
+ Assert.AreEqual ("1.0", firmwareRevision);
+ }
+ }
+}
diff --git a/HapSharp.Core.Tests/UUID_Tests.cs b/HapSharp.Core.Tests/UUID_Tests.cs
new file mode 100644
index 0000000..a873e40
--- /dev/null
+++ b/HapSharp.Core.Tests/UUID_Tests.cs
@@ -0,0 +1,16 @@
+using NUnit.Framework;
+using System;
+namespace HapSharp.Core.Tests
+{
+ [TestFixture ()]
+ class UUID_Tests
+ {
+ [Test ()]
+ public void Bridge_CheckInitialValues ()
+ {
+ var bridgeGuid = Guid.NewGuid ();
+ var bridge = new Bridge ("bridge", bridgeGuid);
+ Assert.IsTrue (bridge._isBridge);
+ }
+ }
+}
diff --git a/HapSharp.Core.Tests/packages.config b/HapSharp.Core.Tests/packages.config
new file mode 100755
index 0000000..01c187c
--- /dev/null
+++ b/HapSharp.Core.Tests/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/HapSharp.Core.sln b/HapSharp.Core.sln
new file mode 100644
index 0000000..9d970e6
--- /dev/null
+++ b/HapSharp.Core.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HapSharp.Core", "HapSharp.Core\HapSharp.Core.csproj", "{24CB3D20-D49D-4361-8B78-92726D8477BD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HapSharp.Core.Example", "HapSharp.Core.Example\HapSharp.Core.Example.csproj", "{3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HapSharp.Core.Tests", "HapSharp.Core.Tests\HapSharp.Core.Tests.csproj", "{7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3C9CBFBB-1DE6-4599-A293-C0CC7DAAA852}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FDBCF30-37C2-43A4-9B0A-6EBE5E340D14}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/HapSharp.Core/Accessory.cs b/HapSharp.Core/Accessory.cs
new file mode 100755
index 0000000..44a6725
--- /dev/null
+++ b/HapSharp.Core/Accessory.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace HapSharp.Core
+{
+ public enum AccessoryCategory
+ {
+ OTHER = 1,
+ BRIDGE = 2,
+ FAN = 3,
+ GARAGE_DOOR_OPENER = 4,
+ LIGHTBULB = 5,
+ DOOR_LOCK = 6,
+ OUTLET = 7,
+ SWITCH = 8,
+ THERMOSTAT = 9,
+ SENSOR = 10,
+ ALARM_SYSTEM = 11,
+ SECURITY_SYSTEM = 11, //Added to conform to HAP naming
+ DOOR = 12,
+ WINDOW = 13,
+ WINDOW_COVERING = 14,
+ PROGRAMMABLE_SWITCH = 15,
+ RANGE_EXTENDER = 16,
+ CAMERA = 17,
+ IP_CAMERA = 17, //Added to conform to HAP naming
+ VIDEO_DOORBELL = 18,
+ AIR_PURIFIER = 19,
+ AIR_HEATER = 20, //Not in HAP Spec
+ AIR_CONDITIONER = 21, //Not in HAP Spec
+ AIR_HUMIDIFIER = 22, //Not in HAP Spec
+ AIR_DEHUMIDIFIER = 23, // Not in HAP Spec
+ APPLE_TV = 24,
+ SPEAKER = 26,
+ AIRPORT = 27,
+ SPRINKLER = 28,
+ FAUCET = 29,
+ SHOWER_HEAD = 30
+ }
+
+ public class Accessory
+ {
+ object aid;
+ internal bool _isBridge;
+ bool bridged;
+ bool reachable;
+ bool shouldPurgeUnusedIDs;
+
+ public string DisplayName { get; protected set; }
+ public Guid UUID { get; private set; }
+
+ public ServiceType ServiceType { get; set; }
+ public AccessoryCategory Category { get; private set; }
+
+ // If we are a Bridge, these are the Accessories we are bridging
+ readonly public List BridgedAccessories = new List ();
+ readonly public List Services = new List ();
+ internal Dictionary serviceCharacteristic = new Dictionary();
+
+ public virtual void Identify ()
+ {
+
+ }
+
+ public Accessory (string displayName, Guid UUID)
+ {
+ if (displayName == null)
+ throw new Exception ("Accessories must be created with a non-empty displayName.");
+ if (UUID == null)
+ throw new Exception ("Accessories must be created with a valid UUID.");
+
+ this.DisplayName = displayName;
+ this.UUID = UUID;
+ this.aid = null; // assigned by us in assignIDs() or by a Bridge
+ this._isBridge = false; // true if we are a Bridge (creating a new instance of the Bridge subclass sets this to true)
+ this.bridged = false; // true if we are hosted "behind" a Bridge Accessory
+ this.reachable = true;
+ this.Category = AccessoryCategory.OTHER;
+ //this.services = []; // of Service
+ //this.cameraSource = null;
+ this.shouldPurgeUnusedIDs = true; // Purge unused ids by default
+
+ // create our initial "Accessory Information" Service that all Accessories are expected to have
+ serviceCharacteristic.Add (ServiceCharacteristicType.Name, displayName);
+ serviceCharacteristic.Add (ServiceCharacteristicType.Manufacturer, "Default-Manufacturer");
+ serviceCharacteristic.Add (ServiceCharacteristicType.Model, "Default-Model");
+ serviceCharacteristic.Add (ServiceCharacteristicType.SerialNumber, "Default-SerialNumber");
+ serviceCharacteristic.Add (ServiceCharacteristicType.FirmwareRevision, "1.0");
+
+ // sign up for when iOS attempts to "set" the Identify characteristic - this means a paired device wishes
+ }
+
+
+ public void OnIdentificationRequest ()
+ {
+ Console.WriteLine ("Identification request {0}", DisplayName);
+ // allow implementors to identify this Accessory in whatever way is appropriate, and pass along
+ // the standard callback for completion.
+ //this.emit ('identify', paired, callback);
+ // debug("[%s] Identification request ignored; no listeners to 'identify' event", this.displayName);
+ //callback ();
+ }
+
+ public void AddService (Service service)
+ {
+ //TODO: we need to check UUID, subtype
+ if (Services.Contains (service)) {
+ return;
+ }
+
+ Services.Add (service);
+
+ if (bridged) {
+ _updateConfiguration ();
+
+ } else {
+ this.Emit (MessageType.ServiceConfigurationChange, new Tuple (this, service));
+ }
+
+ service.MessageReceived += (s, message) => {
+ if (message == MessageType.ServiceConfigurationChange) {
+ if (!this.bridged) {
+ this._updateConfiguration ();
+ } else {
+ this.Emit (MessageType.ServiceConfigurationChange, new Tuple (this, service));
+ }
+ } else if (message == MessageType.CharacteristicChange) {
+ this.Emit (MessageType.ServiceCharacteristicChange, new Tuple (this, service));
+
+ // if we're not bridged, when we'll want to process this event through our HAPServer
+ if (!this.bridged)
+ this._handleCharacteristicChange ();
+ }
+ };
+
+ // listen for changes in characteristics and bubble them up
+
+ service.MessageReceived += (s, message) => {
+ if (!this.bridged) {
+ this._updateConfiguration ();
+ } else {
+ this.Emit (MessageType.ServiceConfigurationChange, new Tuple (this, service));
+ }
+ };
+ }
+
+ public void UpdateReachability (bool reachable)
+ {
+ if (!this.bridged)
+ throw new Exception ("Cannot update reachability on non-bridged accessory!");
+ this.reachable = reachable;
+
+ Console.WriteLine ("Reachability update is no longer being supported.");
+ }
+
+ public void AddBridgedAccessory (Accessory accessory, bool deferUpdate)
+ {
+ if (accessory._isBridge)
+ throw new Exception ("Cannot Bridge another Bridge!");
+
+ if (BridgedAccessories.Any (s => s.UUID == accessory.UUID)) {
+ throw new Exception ("Cannot add a bridged Accessory with the same UUID as another bridged Accessory: " + accessory.UUID);
+ }
+ //accessory.
+ }
+
+ private void _handleCharacteristicChange ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public Service GetService (string name)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Emit (MessageType order, object args)
+ {
+ throw new NotImplementedException ();
+ }
+
+ private void _updateConfiguration ()
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/HapSharp.Core/Bridge.cs b/HapSharp.Core/Bridge.cs
new file mode 100644
index 0000000..239c1e2
--- /dev/null
+++ b/HapSharp.Core/Bridge.cs
@@ -0,0 +1,13 @@
+using System;
+namespace HapSharp.Core
+{
+ public class Bridge : Accessory
+ {
+ readonly public Guid SerialNumber;
+
+ public Bridge (string displayName, Guid serialNumber) : base (displayName, serialNumber)
+ {
+ _isBridge = true;
+ }
+ }
+}
diff --git a/HapSharp.Core/Chacha20poly1305.cs b/HapSharp.Core/Chacha20poly1305.cs
new file mode 100644
index 0000000..1036500
--- /dev/null
+++ b/HapSharp.Core/Chacha20poly1305.cs
@@ -0,0 +1,547 @@
+using System;
+
+namespace HapSharp.Core
+{
+ public static class BufferShim
+ {
+ public static int[] Alloc (int test) {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public class Chacha20poly1305
+ {
+ #region Chacha20
+
+ public class Chacha20Cxt
+ {
+ public int[] input;
+ public int leftover;
+ public int[] buffer;
+ }
+
+ const int Chacha20KeySize = 32;
+ const int Chacha20NonceSize = 8;
+
+ public Chacha20Cxt Chacha20Ctx ()
+ {
+ var ctx = new Chacha20Cxt {
+ input = new int[16],
+ leftover = 0,
+ buffer = new int[64]
+ };
+ return ctx;
+ }
+
+ public static int load32 (int[] x, int i)
+ {
+ return x[i] | (x[i + 1] << 8) | (x[i + 2] << 16) | (x[i + 3] << 24);
+ }
+
+ public static void store32 (int[] x, int i, int u)
+ {
+ x[i] = u & 0xff;
+ u = u.GreaterShiftRightOperator (8);
+ x[i + 1] = u & 0xff;
+ u = u.GreaterShiftRightOperator (8);
+ x[i + 2] = u & 0xff;
+ u = u.GreaterShiftRightOperator (8);
+ x[i + 3] = u & 0xff;
+ }
+
+ public static int plus (int v, int w)
+ {
+ return (v + w).GreaterShiftRightOperator (0);
+ }
+
+ public static int rotl32 (int v, int c)
+ {
+ return ((v << c).GreaterShiftRightOperator (0)) | (v.GreaterShiftRightOperator (32 - c));
+ }
+
+ public static void quarterRound (int[] x, int a, int b, int c, int d)
+ {
+ x[a] = plus (x[a], x[b]); x[d] = rotl32 (x[d] ^ x[a], 16);
+ x[c] = plus (x[c], x[d]); x[b] = rotl32 (x[b] ^ x[c], 12);
+ x[a] = plus (x[a], x[b]); x[d] = rotl32 (x[d] ^ x[a], 8);
+ x[c] = plus (x[c], x[d]); x[b] = rotl32 (x[b] ^ x[c], 7);
+ }
+
+ public static void chacha20_keysetup (Chacha20Cxt ctx, int[] key)
+ {
+ ctx.input[0] = 1634760805;
+ ctx.input[1] = 857760878;
+ ctx.input[2] = 2036477234;
+ ctx.input[3] = 1797285236;
+ for (var i = 0; i < 8; i++) {
+ ctx.input[i + 4] = load32 (key, i * 4);
+ }
+ }
+
+ public static void chacha20_ivsetup (Chacha20Cxt ctx, int[] iv)
+ {
+ ctx.input[12] = 0;
+ ctx.input[13] = 0;
+ ctx.input[14] = load32 (iv, 0);
+ ctx.input[15] = load32 (iv, 4);
+ }
+
+ public static void chacha20_encrypt (Chacha20Cxt ctx, int[] dst, int[] src, int len)
+ {
+ var x = new int[16];
+ var buf = new int[64];
+ int i = 0, dpos = 0, spos = 0;
+
+ while (len > 0) {
+ for (i = 16; i != 0; i--) x[i] = ctx.input[i];
+ for (i = 20; i > 0; i -= 2) {
+ quarterRound (x, 0, 4, 8, 12);
+ quarterRound (x, 1, 5, 9, 13);
+ quarterRound (x, 2, 6, 10, 14);
+ quarterRound (x, 3, 7, 11, 15);
+ quarterRound (x, 0, 5, 10, 15);
+ quarterRound (x, 1, 6, 11, 12);
+ quarterRound (x, 2, 7, 8, 13);
+ quarterRound (x, 3, 4, 9, 14);
+ }
+ for (i = 16; i != 0; i--) x[i] += ctx.input[i];
+ for (i = 16; i != 0; i--) store32 (buf, 4 * i, x[i]);
+
+ ctx.input[12] = plus (ctx.input[12], 1);
+ if (ctx.input[12] != 0) {
+ ctx.input[13] = plus (ctx.input[13], 1);
+ }
+ if (len <= 64) {
+ for (i = len; i != 0; i--) {
+ dst[i + dpos] = src[i + spos] ^ buf[i];
+ }
+ return;
+ }
+ for (i = 64; i != 0; i--) {
+ dst[i + dpos] = src[i + spos] ^ buf[i];
+ }
+ len -= 64;
+ spos += 64;
+ dpos += 64;
+ }
+ }
+
+ public static void chacha20_decrypt (Chacha20Cxt ctx, int[] dst, int[] src, int len)
+ {
+ chacha20_encrypt (ctx, dst, src, len);
+ }
+
+ public static int chacha20_update (Chacha20Cxt ctx, int[] dst, int[] src, int inlen)
+ {
+ var bytes = 0;
+ var out_start = 0;
+ var out_inc = 0;
+
+ if ((ctx.leftover + inlen) >= 64) {
+
+ if (ctx.leftover != 0) {
+ bytes = 64 - ctx.leftover;
+
+ if (src.Length > 0) {
+ src.Copy (ctx.buffer, ctx.leftover, 0, bytes);
+ src = src.Slice (bytes, src.Length);
+ }
+
+ chacha20_encrypt (ctx, dst, ctx.buffer, 64);
+ inlen -= bytes;
+ dst = dst.Slice (64, dst.Length);
+ out_inc += 64;
+ ctx.leftover = 0;
+ }
+
+ bytes = (inlen & (~63));
+ if (bytes > 0) {
+ chacha20_encrypt (ctx, dst, src, bytes);
+ inlen -= bytes;
+ src = src.Slice (bytes, src.Length);
+ dst = dst.Slice (bytes, dst.Length);
+ out_inc += bytes;
+ }
+
+ }
+
+ if (inlen > 0) {
+ if (src.Length > 0) {
+ src.Copy (ctx.buffer, ctx.leftover, 0, src.Length);
+ }
+ //else {
+ // var zeros = bufferShim.Alloc (inlen);
+ // zeros.Copy (ctx.buffer, ctx.leftover, 0, inlen);
+ //}
+ ctx.leftover += inlen;
+ }
+
+ return out_inc - out_start;
+ }
+
+
+ public static int chacha20_final (Chacha20Cxt ctx, int[] dst)
+ {
+ if (ctx.leftover != 0) {
+ chacha20_encrypt (ctx, dst, ctx.buffer, 64);
+ }
+
+ return ctx.leftover;
+ }
+
+ public static void chacha20_keystream (Chacha20Cxt ctx, int[] dst, int len)
+ {
+ for (var i = 0; i < len; ++i) dst[i] = 0;
+ chacha20_encrypt (ctx, dst, dst, len);
+ }
+ #endregion
+
+ #region poly1305
+ /*
+ // Written in 2014 by Devi Mandiri. Public domain.
+ //
+ // Implementation derived from poly1305-donna-16.h
+ // See for details: https://github.com/floodyberry/poly1305-donna
+ */
+
+ const int Poly1305KeySize = 32;
+ const int Poly1305TagSize = 16;
+
+ public Poly1305Ctx CreatePoly1305Ctx ()
+ {
+ var ctx = new Poly1305Ctx {
+ buffer = new int[Poly1305TagSize],
+ leftover = 0,
+ r = new int[10],
+ h = new int[10],
+ pad = new int[8],
+ finished = 0
+ };
+ return ctx;
+ }
+
+ public class Poly1305Ctx
+ {
+ public int[] r;
+ public int[] h;
+ public int[] pad;
+ public int finished;
+ public int leftover;
+ public int[] buffer;
+ }
+
+ public static int U8TO16 (int[] p, int pos)
+ {
+ return ((p[pos] & 0xff) & 0xffff) | (((p[pos + 1] & 0xff) & 0xffff) << 8);
+ }
+
+ public static void U16TO8 (int[] p, int pos, int v)
+ {
+ p[pos] = (v) & 0xff;
+ p[pos + 1] = (v.GreaterShiftRightOperator (8)) & 0xff;
+ }
+
+ public static void poly1305_init (Poly1305Ctx ctx, int[] key)
+ {
+ int[] t = new int[1000];
+ int i = 0;
+
+ for (i = 8; i != 0; i--) t[i] = U8TO16 (key, i * 2);
+
+ ctx.r[0] = t[0] & 0x1fff;
+ ctx.r[1] = ((t[0].GreaterShiftRightOperator (13)) | (t[1] << 3)) & 0x1fff;
+ ctx.r[2] = ((t[1].GreaterShiftRightOperator (10)) | (t[2] << 6)) & 0x1f03;
+ ctx.r[3] = ((t[2].GreaterShiftRightOperator (7)) | (t[3] << 9)) & 0x1fff;
+ ctx.r[4] = ((t[3].GreaterShiftRightOperator (4)) | (t[4] << 12)) & 0x00ff;
+ ctx.r[5] = (t[4].GreaterShiftRightOperator (1)) & 0x1ffe;
+ ctx.r[6] = ((t[4].GreaterShiftRightOperator (14)) | (t[5] << 2)) & 0x1fff;
+ ctx.r[7] = ((t[5].GreaterShiftRightOperator (11)) | (t[6] << 5)) & 0x1f81;
+ ctx.r[8] = ((t[6].GreaterShiftRightOperator (8)) | (t[7] << 8)) & 0x1fff;
+ ctx.r[9] = (t[7].GreaterShiftRightOperator (5)) & 0x007f;
+
+ for (i = 8; i != 0; i--) {
+ ctx.h[i] = 0;
+ ctx.pad[i] = U8TO16 (key, 16 + (2 * i));
+ }
+ ctx.h[8] = 0;
+ ctx.h[9] = 0;
+ ctx.leftover = 0;
+ ctx.finished = 0;
+ }
+
+ public static void poly1305_blocks (Poly1305Ctx ctx, int[] m, int mpos, int bytes)
+ {
+ int hibit = ctx.finished == 0 ? 0 : (1 << 11);
+ int[] t = new int[1000], d = new int[1000];
+ int c = 0, i = 0, j = 0;
+
+ while (bytes >= Poly1305TagSize) {
+ for (i = 8; i != 0; i--) t[i] = U8TO16 (m, i * 2 + mpos);
+
+ ctx.h[0] += t[0] & 0x1fff;
+ ctx.h[1] += ((t[0].GreaterShiftRightOperator (13)) | (t[1] << 3)) & 0x1fff;
+ ctx.h[2] += ((t[1].GreaterShiftRightOperator (10)) | (t[2] << 6)) & 0x1fff;
+ ctx.h[3] += ((t[2].GreaterShiftRightOperator (7)) | (t[3] << 9)) & 0x1fff;
+ ctx.h[4] += ((t[3].GreaterShiftRightOperator (4)) | (t[4] << 12)) & 0x1fff;
+ ctx.h[5] += (t[4].GreaterShiftRightOperator (1)) & 0x1fff;
+ ctx.h[6] += ((t[4].GreaterShiftRightOperator (14)) | (t[5] << 2)) & 0x1fff;
+ ctx.h[7] += ((t[5].GreaterShiftRightOperator (11)) | (t[6] << 5)) & 0x1fff;
+ ctx.h[8] += ((t[6].GreaterShiftRightOperator (8)) | (t[7] << 8)) & 0x1fff;
+ ctx.h[9] += (t[7].GreaterShiftRightOperator (7)) | hibit;
+
+ for (i = 0, c = 0; i < 10; i++) {
+ d[i] = c;
+ for (j = 0; j < 10; j++) {
+ d[i] += (int)(ctx.h[j] & 0xffffffff) * ((j <= i) ? ctx.r[i - j] : (5 * ctx.r[i + 10 - j]));
+ if (j == 4) {
+ c = (d[i].GreaterShiftRightOperator (13));
+ d[i] &= 0x1fff;
+ }
+ }
+ c += (d[i].GreaterShiftRightOperator (13));
+ d[i] &= 0x1fff;
+ }
+ c = ((c << 2) + c);
+ c += d[0];
+ d[0] = ((c & 0xffff) & 0x1fff);
+ c = (c.GreaterShiftRightOperator (13));
+ d[1] += c;
+
+ for (i = 10; i != 0; i--) ctx.h[i] = d[i] & 0xffff;
+
+ mpos += Poly1305TagSize;
+ bytes -= Poly1305TagSize;
+ }
+ }
+
+ public static void poly1305_update (Poly1305Ctx ctx, int[] m, int bytes)
+ {
+ int want = 0, i = 0, mpos = 0;
+
+ if (ctx.leftover == 0) {
+ want = (Poly1305TagSize - ctx.leftover);
+ if (want > bytes)
+ want = bytes;
+ for (i = want; i != 0; i--) {
+ ctx.buffer[ctx.leftover + i] = m[i + mpos];
+ }
+ bytes -= want;
+ mpos += want;
+ ctx.leftover += want;
+ if (ctx.leftover < Poly1305TagSize)
+ return;
+ poly1305_blocks (ctx, ctx.buffer, 0, Poly1305TagSize);
+ ctx.leftover = 0;
+ }
+
+ if (bytes >= Poly1305TagSize) {
+ want = (bytes & ~(Poly1305TagSize - 1));
+ poly1305_blocks (ctx, m, mpos, want);
+ mpos += want;
+ bytes -= want;
+ }
+
+ if (bytes == 0) {
+ for (i = bytes; i != 0; i--) {
+ ctx.buffer[ctx.leftover + i] = m[i + mpos];
+ }
+ ctx.leftover += bytes;
+ }
+ }
+
+ public static void poly1305_finish (Poly1305Ctx ctx, int[] mac)
+ {
+ var g = new int[1000];
+ int c = 0, mask = 0, f = 0, i = 0;
+
+ if (ctx.leftover == 0) {
+ i = ctx.leftover;
+ ctx.buffer[i++] = 1;
+ for (; i < Poly1305TagSize; i++) {
+ ctx.buffer[i] = 0;
+ }
+ ctx.finished = 1;
+ poly1305_blocks (ctx, ctx.buffer, 0, Poly1305TagSize);
+ }
+
+ c = ctx.h[1].GreaterShiftRightOperator (13);
+ ctx.h[1] &= 0x1fff;
+ for (i = 2; i < 10; i++) {
+ ctx.h[i] += c;
+ c = ctx.h[i].GreaterShiftRightOperator (13);
+ ctx.h[i] &= 0x1fff;
+ }
+ ctx.h[0] += (c * 5);
+ c = ctx.h[0].GreaterShiftRightOperator (13);
+ ctx.h[0] &= 0x1fff;
+ ctx.h[1] += c;
+ c = ctx.h[1].GreaterShiftRightOperator (13);
+ ctx.h[1] &= 0x1fff;
+ ctx.h[2] += c;
+
+ g[0] = ctx.h[0] + 5;
+ c = g[0].GreaterShiftRightOperator (13);
+ g[0] &= 0x1fff;
+ for (i = 1; i < 10; i++) {
+ g[i] = ctx.h[i] + c;
+ c = g[i].GreaterShiftRightOperator (13);
+ g[i] &= 0x1fff;
+ }
+ g[9] -= (1 << 13);
+ g[9] &= 0xffff;
+
+ mask = (g[9].GreaterShiftRightOperator (15)) - 1;
+ for (i = 10; i != 0; i--) g[i] &= mask;
+ mask = ~mask;
+ for (i = 10; i != 0; i--) {
+ ctx.h[i] = (ctx.h[i] & mask) | g[i];
+ }
+
+ ctx.h[0] = ((ctx.h[0]) | (ctx.h[1] << 13)) & 0xffff;
+ ctx.h[1] = ((ctx.h[1] >> 3) | (ctx.h[2] << 10)) & 0xffff;
+ ctx.h[2] = ((ctx.h[2] >> 6) | (ctx.h[3] << 7)) & 0xffff;
+ ctx.h[3] = ((ctx.h[3] >> 9) | (ctx.h[4] << 4)) & 0xffff;
+ ctx.h[4] = ((ctx.h[4] >> 12) | (ctx.h[5] << 1) | (ctx.h[6] << 14)) & 0xffff;
+ ctx.h[5] = ((ctx.h[6] >> 2) | (ctx.h[7] << 11)) & 0xffff;
+ ctx.h[6] = ((ctx.h[7] >> 5) | (ctx.h[8] << 8)) & 0xffff;
+ ctx.h[7] = ((ctx.h[8] >> 8) | (ctx.h[9] << 5)) & 0xffff;
+
+ f = (int)(ctx.h[0] & 0xffffffff) + ctx.pad[0];
+ ctx.h[0] = f & 0xffff;
+ for (i = 1; i < 8; i++) {
+ f = ((int)(ctx.h[i] & 0xffffffff)) + ctx.pad[i] + (f.GreaterShiftRightOperator (16));
+ ctx.h[i] = f & 0xffff;
+ }
+
+ for (i = 8; i != 0; i--) {
+ U16TO8 (mac, i * 2, ctx.h[i]);
+ ctx.pad[i] = 0;
+ }
+ for (i = 10; i != 0; i--) {
+ ctx.h[i] = 0;
+ ctx.r[i] = 0;
+ }
+ }
+
+ public static void poly1305_auth (int[] mac, int[] m, int bytes, int[] key)
+ {
+ var ctx = new Poly1305Ctx ();
+ poly1305_init (ctx, key);
+ poly1305_update (ctx, m, bytes);
+ poly1305_finish (ctx, mac);
+ }
+
+ public static int poly1305_verify (int[] mac1, int[] mac2)
+ {
+ var dif = 0;
+ for (var i = 0; i < 16; i++) {
+ dif |= (mac1[i] ^ mac2[i]);
+ }
+ dif = (dif - 1).GreaterShiftRightOperator (31);
+ return (dif & 1);
+ }
+
+ #endregion
+
+ #region AEAD
+
+ public static AeadCtx CreateAeadCtx (int[] key)
+ {
+ var ctx = new AeadCtx ();
+ ctx.key = key;
+ return ctx;
+ }
+
+ public class AeadCtx
+ {
+ public int[] key;
+ }
+
+ public static int[] aead_init (Chacha20Cxt c20ctx, int[] key,int[] nonce)
+ {
+ chacha20_keysetup (c20ctx, key);
+ chacha20_ivsetup (c20ctx, nonce);
+
+ var subkey = new int [64];
+ chacha20_keystream (c20ctx, subkey, subkey.Length);
+
+ return subkey.Slice (0, 32);
+ }
+
+ public static void store64 (int[] dst,int pos,int num)
+ {
+ int hi = 0, lo = num.GreaterShiftRightOperator (0);
+ if ((+(Math.Abs (num))) >= 1) {
+ if (num > 0) {
+ var floor = (int) Math.Floor (num / 4294967296f);
+ var number = (int) Math.Min ((+(floor)), 4294967295);
+ hi = (number | 0).GreaterShiftRightOperator (0);
+ } else {
+ var ceiling = (int) Math.Ceiling ((num - +(((~~(num))).GreaterShiftRightOperator (0))) / 4294967296f);
+ hi = (~~(+ceiling)).GreaterShiftRightOperator (0);
+ }
+ }
+ dst[pos] = lo & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 1] = lo & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 2] = lo & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 3] = lo & 0xff;
+ dst[pos + 4] = hi & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 5] = hi & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 6] = hi & 0xff; lo = lo.GreaterShiftRightOperator (8);
+ dst[pos + 7] = hi & 0xff;
+ }
+
+ public static int[] aead_mac (int[] key,int[] ciphertext,int[] data)
+ {
+ var clen = ciphertext.Length;
+ var dlen = data.Length;
+ var m = new int[clen + dlen + 16];
+ var i = dlen;
+
+ for (; i != 0; i--) m[i] = data[i];
+ store64 (m, dlen, dlen);
+
+ for (i = clen; i != 0; i--) m[dlen + 8 + i] = ciphertext[i];
+ store64 (m, clen + dlen + 8, clen);
+
+ var mac = new int[1000];
+ poly1305_auth (mac, m, m.Length, key);
+
+ return mac;
+ }
+
+
+ public static int[] aead_encrypt (AeadCtx ctx,int[] nonce,int[] input,int[] ad)
+ {
+ var c = new Chacha20Cxt ();
+ var key = aead_init (c, ctx.key, nonce);
+
+ var ciphertext = new int [1000];
+ chacha20_encrypt (c, ciphertext, input, input.Length);
+
+ var mac = aead_mac (key, ciphertext, ad);
+
+ var outArray = ciphertext.Concat (mac);
+ //outArray = ciphertext. Array. outArray. concat (ciphertext, mac);
+
+ return outArray;
+ }
+
+ public static int[] aead_decrypt (AeadCtx ctx,int[] nonce,int[] ciphertext,int[] ad)
+ {
+ var c = new Chacha20Cxt ();
+ var key = aead_init (c, ctx.key, nonce);
+ var clen = ciphertext.Length - Poly1305TagSize;
+ var digest = ciphertext.Slice (clen);
+ var mac = aead_mac (key, ciphertext.Slice (0, clen), ad);
+
+ if (poly1305_verify (digest, mac) != 1) return null;
+
+ var outArray = new int [1000];
+ chacha20_decrypt (c, outArray, ciphertext, clen);
+ return outArray;
+ }
+
+ #endregion
+ }
+}
diff --git a/HapSharp.Core/Characteristic.cs b/HapSharp.Core/Characteristic.cs
new file mode 100644
index 0000000..a027050
--- /dev/null
+++ b/HapSharp.Core/Characteristic.cs
@@ -0,0 +1,16 @@
+using System;
+namespace HapSharp.Core
+{
+ public class Characteristic
+ {
+ string UUID;
+ string displayName;
+
+ public Characteristic (string displayName, string UUID, object props)
+ {
+ this.displayName = displayName;
+ this.UUID = UUID;
+ //this.iid = null; // assigned by our containing Service
+ }
+ }
+}
diff --git a/HapSharp.Core/Definitions.cs b/HapSharp.Core/Definitions.cs
new file mode 100644
index 0000000..c59ee9d
--- /dev/null
+++ b/HapSharp.Core/Definitions.cs
@@ -0,0 +1,82 @@
+using System;
+namespace HapSharp.Core
+{
+ public interface IUUID
+ {
+ string Generate (string data);
+ bool IsValid (string UUID);
+ string Unparse (string bug, int offset);
+ }
+
+ public enum EventService
+ {
+ CharacteristicChange,
+ ServiceConfigurationChange,
+ }
+
+ public interface IEventEmitterAccessory
+ {
+ void AddListener (EventAccessory eventAccessory, Action handler);
+ void On (EventAccessory eventAccessory, Action handler);
+ void Once (EventAccessory eventAccessory, Action handler);
+ void RemoveListener (EventAccessory eventAccessory, Action handler);
+ void RemoveAllListeners (EventAccessory eventAccessory);
+ void SetMaxListeners (int number);
+ int GetMaxListeners ();
+ }
+
+ public interface IService : IEventEmitterAccessory
+ {
+ //new (displayName: string, UUID: string, subtype: string): Service;
+ string displayName { get; set; }
+ string UUID { get; set; }
+ string subtype { get; set; }
+ string iid { get; set; }
+
+ ICharacteristic[] characteristics { get; set; }
+ ICharacteristic[] optionalCharacteristics { get; set; }
+
+ ICharacteristic addCharacteristic (ICharacteristic characteristic, Action handler);
+
+ void removeCharacteristic (ICharacteristic characteristic);
+
+ ICharacteristic getCharacteristic (string characteristic, string value);
+
+ bool testCharacteristic (string characteristic);
+ bool setCharacteristic (string characteristic);
+
+ IService updateCharacteristic (string name, string value);
+ void addOptionalCharacteristic (ICharacteristic characteristic);
+ ICharacteristic getCharacteristicByIID (string iid);
+
+ string toHAP (object any);
+ }
+
+ public interface ICharacteristic
+ {
+
+ }
+
+ public enum EventAccessory
+ {
+ ServiceConfigurationChange,
+ ServiceCharacteristicChange,
+ Identify
+ }
+
+ public enum EventCharacteristic
+ {
+ Get,
+ Set,
+ }
+
+ public enum ServiceType
+ {
+ AccessoryInformation
+ }
+
+ public enum ServiceCharacteristicType
+ {
+ Name, Manufacturer, Model, SerialNumber, FirmwareRevision, Identify
+ }
+}
diff --git a/HapSharp.Core/Encription.cs b/HapSharp.Core/Encription.cs
new file mode 100644
index 0000000..c94d921
--- /dev/null
+++ b/HapSharp.Core/Encription.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace HapSharp.Core
+{
+ public enum EncriptionType
+ {
+ SHA1
+ }
+
+ public static class Curve25519
+ {
+ //TODO: TO IMPLEMENT
+ public static void MakeSecretKey (int[] data)
+ {
+ throw new NotImplementedException ();
+ }
+ //TODO: TO IMPLEMENT
+ internal static int[] deriveSharedSecret (int[] secKey, int[] pubKey)
+ {
+ throw new NotImplementedException ();
+ }
+ //TODO: TO IMPLEMENT
+ internal static int[] derivePublicKey (int[] secKey)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public class EncriptionHelper
+ {
+ public static string CreateHash (EncriptionType type, string input)
+ {
+ if (type == EncriptionType.SHA1) {
+ return CreateHashSha1 (input);
+ }
+ throw new NotImplementedException ();
+ }
+
+ static string CreateHashSha1 (string input)
+ {
+ using (SHA1Managed sha1 = new SHA1Managed ()) {
+ var hash = sha1.ComputeHash (Encoding.UTF8.GetBytes (input));
+ var sb = new StringBuilder (hash.Length * 2);
+
+ foreach (byte b in hash) {
+ // can be "x2" if you want lowercase
+ sb.Append (b.ToString ("X2"));
+ }
+
+ return sb.ToString ();
+ }
+ }
+ }
+
+ public class Encription
+ {
+ //TODO: TO IMPLEMENT
+ public static int[] FromHex (string h)
+ {
+ //h.replace (/ ([^ 0 - 9a - f]) / g, '');
+ //var out = [], len = h.length, w = '';
+ //for (var i = 0; i < len; i += 2) {
+ // w = h[i];
+ // if (((i + 1) >= len) || typeof h[i + 1] === 'undefined') {
+ // w += '0';
+ // } else {
+ // w += h[i + 1];
+ // }
+ // out.push (parseInt (w, 16));
+ //}
+ //return out;
+ throw new NotImplementedException ();
+ }
+
+ //TODO: TO IMPLEMENT
+ public static int[] generateCurve25519SecretKey ()
+ {
+ var secretKey = BufferShim.Alloc (32);
+ Curve25519.MakeSecretKey (secretKey);
+ return secretKey;
+ }
+
+ //TODO: TO IMPLEMENT
+ public static int[] generateCurve25519PublicKeyFromSecretKey (int[] secKey)
+ {
+ var publicKey = Curve25519.derivePublicKey (secKey);
+ return publicKey;
+ }
+
+ //TODO: TO IMPLEMENT
+ public static int[] generateCurve25519SharedSecKey (int[] secKey, int[] pubKey)
+ {
+ var sharedSec = Curve25519.deriveSharedSecret (secKey, pubKey);
+ return sharedSec;
+ }
+
+ //Security Layer Enc/Dec
+ //TODO: TO IMPLEMENT
+ public static int[] layerEncrypt (int[] data, int[] count, int[] key)
+ {
+ // var result = BufferShim.Alloc (0);
+ // var total = data.Length;
+ // for (var offset = 0; offset < total;) {
+ // var length = Math.Min (total - offset, 0x400);
+ // var leLength = BufferShim.Alloc (2);
+ // leLength.writeUInt16LE (length, 0);
+
+ // var nonce = BufferShim.Alloc (8);
+ // writeUInt64LE (count.Value++, nonce, 0);
+
+ // var result_Buffer = bufferShim.alloc (length);
+ // var result_mac = bufferShim.alloc (16);
+ // encryptAndSeal (key, nonce, data.slice (offset, offset + length),
+ // leLength, result_Buffer, result_mac);
+
+ // offset += length;
+
+ // result = Buffer.Concat ([result, leLength, result_Buffer, result_mac]);
+ //}
+ //return result;
+ throw new NotImplementedException ();
+ }
+
+ //TODO: TO IMPLEMENT
+ public static int[] layerDecrypt (int[] packet,int count,int[] key,int[] extraInfo)
+ {
+ // Handle Extra Info
+ // if (extraInfo.leftoverData != undefined) {
+ // packet = Buffer.concat ([extraInfo.leftoverData, packet]);
+ // }
+
+ // var result = bufferShim.alloc (0);
+ // var total = packet.length;
+
+ // for (var offset = 0; offset < total;) {
+ // var realDataLength = packet.slice (offset, offset + 2).readUInt16LE (0);
+
+ // var availableDataLength = total - offset - 2 - 16;
+ // if (realDataLength > availableDataLength) {
+ // // Fragmented packet
+ // extraInfo.leftoverData = packet.slice (offset);
+ // break;
+ // } else {
+ // extraInfo.leftoverData = undefined;
+ // }
+
+ // var nonce = bufferShim.alloc (8);
+ // writeUInt64LE (count.value++, nonce, 0);
+
+ // var result_Buffer = bufferShim.alloc (realDataLength);
+
+ // if (verifyAndDecrypt (key, nonce, packet.slice (offset + 2, offset + 2 + realDataLength),
+ // packet.slice (offset + 2 + realDataLength, offset + 2 + realDataLength + 16),
+ // packet.slice (offset, offset + 2), result_Buffer)) {
+ // result = Buffer.concat ([result, result_Buffer]);
+ // offset += (18 + realDataLength);
+ // } else {
+ // debug ('Layer Decrypt fail!');
+ // debug ('Packet: %s', packet.toString('hex'));
+ // return 0;
+ // }
+ //}
+
+ //return result;
+ throw new NotImplementedException ();
+ }
+
+ //General Enc/Dec
+ //TODO: TO IMPLEMENT
+ public static int[] verifyAndDecrypt (int[] key,int[] nonce,int[] ciphertext,int[] mac,int[] addData,int[] plaintext)
+ {
+ //var ctx = new chacha20poly1305.Chacha20Ctx ();
+ //chacha20poly1305.chacha20_keysetup (ctx, key);
+ //chacha20poly1305.chacha20_ivsetup (ctx, nonce);
+ //var poly1305key = bufferShim.alloc (64);
+ //var zeros = bufferShim.alloc (64);
+ //chacha20poly1305.chacha20_update (ctx, poly1305key, zeros, zeros.length);
+
+ //var poly1305_contxt = new chacha20poly1305.Poly1305Ctx ();
+ //chacha20poly1305.poly1305_init (poly1305_contxt, poly1305key);
+
+ //var addDataLength = 0;
+ //if (addData != undefined) {
+ // addDataLength = addData.length;
+ // chacha20poly1305.poly1305_update (poly1305_contxt, addData, addData.length);
+ // if ((addData.length % 16) != 0) {
+ // chacha20poly1305.poly1305_update (poly1305_contxt, bufferShim.alloc (16 - (addData.length % 16)), 16 - (addData.length % 16));
+ // }
+ //}
+
+ //chacha20poly1305.poly1305_update (poly1305_contxt, ciphertext, ciphertext.length);
+ //if ((ciphertext.length % 16) != 0) {
+ // chacha20poly1305.poly1305_update (poly1305_contxt, bufferShim.alloc (16 - (ciphertext.length % 16)), 16 - (ciphertext.length % 16));
+ //}
+
+ //var leAddDataLen = bufferShim.alloc (8);
+ //writeUInt64LE (addDataLength, leAddDataLen, 0);
+ //chacha20poly1305.poly1305_update (poly1305_contxt, leAddDataLen, 8);
+
+ //var leTextDataLen = bufferShim.alloc (8);
+ //writeUInt64LE (ciphertext.length, leTextDataLen, 0);
+ //chacha20poly1305.poly1305_update (poly1305_contxt, leTextDataLen, 8);
+
+ //var poly_out = [];
+ //chacha20poly1305.poly1305_finish (poly1305_contxt, poly_out);
+
+ //if (chacha20poly1305.poly1305_verify (mac, poly_out) != 1) {
+ // debug ('Verify Fail');
+ // return false;
+ //} else {
+ // var written = chacha20poly1305.chacha20_update (ctx, plaintext, ciphertext, ciphertext.length);
+ // chacha20poly1305.chacha20_final (ctx, plaintext.slice (written, ciphertext.length));
+ // return true;
+ //}
+
+ throw new NotImplementedException ();
+ }
+
+ //TODO: TO IMPLEMENT
+ public static int[] encryptAndSeal (int[] key,int[] nonce, int[] plaintext,int[] addData,int[] ciphertext,int[] mac)
+ {
+ //var ctx = new chacha20poly1305.Chacha20Ctx ();
+ //chacha20poly1305.chacha20_keysetup (ctx, key);
+ //chacha20poly1305.chacha20_ivsetup (ctx, nonce);
+ //var poly1305key = bufferShim.alloc (64);
+ //var zeros = bufferShim.alloc (64);
+ //chacha20poly1305.chacha20_update (ctx, poly1305key, zeros, zeros.length);
+
+ //var written = chacha20poly1305.chacha20_update (ctx, ciphertext, plaintext, plaintext.length);
+ //chacha20poly1305.chacha20_final (ctx, ciphertext.slice (written, plaintext.length));
+
+ //var poly1305_contxt = new chacha20poly1305.Poly1305Ctx ();
+ //chacha20poly1305.poly1305_init (poly1305_contxt, poly1305key);
+
+ //var addDataLength = 0;
+ //if (addData != undefined) {
+ // addDataLength = addData.length;
+ // chacha20poly1305.poly1305_update (poly1305_contxt, addData, addData.length);
+ // if ((addData.length % 16) != 0) {
+ // chacha20poly1305.poly1305_update (poly1305_contxt, bufferShim.alloc (16 - (addData.length % 16)), 16 - (addData.length % 16));
+ // }
+ //}
+
+ //chacha20poly1305.poly1305_update (poly1305_contxt, ciphertext, ciphertext.length);
+ //if ((ciphertext.length % 16) != 0) {
+ // chacha20poly1305.poly1305_update (poly1305_contxt, bufferShim.alloc (16 - (ciphertext.length % 16)), 16 - (ciphertext.length % 16));
+ //}
+
+ //var leAddDataLen = bufferShim.alloc (8);
+ //writeUInt64LE (addDataLength, leAddDataLen, 0);
+ //chacha20poly1305.poly1305_update (poly1305_contxt, leAddDataLen, 8);
+
+ //var leTextDataLen = bufferShim.alloc (8);
+ //writeUInt64LE (ciphertext.length, leTextDataLen, 0);
+ //chacha20poly1305.poly1305_update (poly1305_contxt, leTextDataLen, 8);
+
+ //chacha20poly1305.poly1305_finish (poly1305_contxt, mac);
+ throw new NotImplementedException ();
+ }
+
+ const ulong MAX_UINT32 = 0x00000000FFFFFFFF;
+ const ulong MAX_INT53 = 0x001FFFFFFFFFFFFF;
+
+ public static ulong onesComplement (ulong number)
+ {
+ number = ~number;
+ if (number < 0) {
+ number = (number & 0x7FFFFFFF) + 0x80000000;
+ }
+ return number;
+ }
+
+ //TODO: TO IMPLEMENT
+ public static ulong[] uintHighLow (ulong number)
+ {
+ if (((long) number) > -1 && number <= MAX_INT53) {
+ throw new IndexOutOfRangeException ();
+ }
+
+ ulong high = 0;
+ ulong signbit = number & 0xFFFFFFFF;
+ var low = signbit < 0 ? (number & 0x7FFFFFFF) + 0x80000000 : signbit;
+ if (number > MAX_UINT32) {
+ high = (number - low) / (MAX_UINT32 + 1);
+
+ }
+ return new[] { high, low };
+ }
+
+ //TODO: TO IMPLEMENT
+ public static ulong[] intHighLow (int number)
+ {
+ if (number > -1) {
+ return uintHighLow ((ulong)number);
+ }
+ var hl = uintHighLow ((ulong)-number);
+ var high = onesComplement (hl[0]);
+ var low = onesComplement (hl[1]);
+ if (low == MAX_UINT32) {
+ high += 1;
+ low = 0;
+ } else {
+ low += 1;
+ }
+ return new[] { high, low };
+ }
+
+ //TODO: TO IMPLEMENT
+ public static ulong[] writeUInt64BE (ulong number,int buffer,ulong offset)
+ {
+ // offset = offset || 0;
+ //var hl = uintHighLow (number)
+ //buffer.writeUInt32BE (hl[0], offset)
+ //buffer.writeUInt32BE (hl[1], offset + 4)
+ throw new NotImplementedException ();
+ }
+
+ //TODO: TO IMPLEMENT
+ public static ulong[] writeUInt64LE (int number,int buffer,int offset)
+ {
+ //offset = offset || 0;
+ //var hl = uintHighLow (number)
+ //buffer.writeUInt32LE (hl[1], offset)
+ //buffer.writeUInt32LE (hl[0], offset + 4)
+ throw new NotImplementedException ();
+ }
+
+ }
+}
diff --git a/HapSharp.Core/HapServer.cs b/HapSharp.Core/HapServer.cs
new file mode 100755
index 0000000..1abaa44
--- /dev/null
+++ b/HapSharp.Core/HapServer.cs
@@ -0,0 +1,117 @@
+/*
+ * The actual HAP server that iOS devices talk to.
+ *
+ * Notes
+ * -----
+ * It turns out that the IP-based version of HomeKit's HAP protocol operates over a sort of pseudo-HTTP.
+ * Accessories are meant to host a TCP socket server that initially behaves exactly as an HTTP/1.1 server.
+ * So iOS devices will open up a long-lived connection to this server and begin issuing HTTP requests.
+ * So far, this conforms with HTTP/1.1 Keepalive. However, after the "pairing" process is complete, the
+ * connection is expected to be "upgraded" to support full-packet encryption of both HTTP headers and data.
+ * This encryption is NOT SSL. It is a customized ChaCha20+Poly1305 encryption layer.
+ *
+ * Additionally, this "HTTP Server" supports sending "event" responses at any time without warning. The iOS
+ * device simply keeps the connection open after it's finished with HTTP request/response traffic, and while
+ * the connection is open, the server can elect to issue "EVENT/1.0 200 OK" HTTP-style responses. These are
+ * typically sent to inform the iOS device of a characteristic change for the accessory (like "Door was Unlocked").
+ *
+ * See eventedhttp.js for more detail on the implementation of this protocol.
+ *
+ * @event 'listening' => function() { }
+ * Emitted when the server is fully set up and ready to receive connections.
+ *
+ * @event 'identify' => function(callback(err)) { }
+ * Emitted when a client wishes for this server to identify itself before pairing. You must call the
+ * callback to respond to the client with success.
+ *
+ * @event 'pair' => function(username, publicKey, callback(err)) { }
+ * This event is emitted when a client completes the "pairing" process and exchanges encryption keys.
+ * Note that this does not mean the "Add Accessory" process in iOS has completed. You must call the
+ * callback to complete the process.
+ *
+ * @event 'verify' => function() { }
+ * This event is emitted after a client successfully completes the "verify" process, thereby authenticating
+ * itself to an Accessory as a known-paired client.
+ *
+ * @event 'unpair' => function(username, callback(err)) { }
+ * This event is emitted when a client has requested us to "remove their pairing info", or basically to unpair.
+ * You must call the callback to complete the process.
+ *
+ * @event 'accessories' => function(callback(err, accessories)) { }
+ * This event is emitted when a client requests the complete representation of Accessory data for
+ * this Accessory (for instance, what services, characteristics, etc. are supported) and any bridged
+ * Accessories in the case of a Bridge Accessory. The listener must call the provided callback function
+ * when the accessory data is ready. We will automatically JSON.stringify the data.
+ *
+ * @event 'get-characteristics' => function(data, events, callback(err, characteristics), remote, connectionID) { }
+ * This event is emitted when a client wishes to retrieve the current value of one or more characteristics.
+ * The listener must call the provided callback function when the values are ready. iOS clients can typically
+ * wait up to 10 seconds for this call to return. We will automatically JSON.stringify the data (which must
+ * be an array) and wrap it in an object with a top-level "characteristics" property.
+ *
+ * @event 'set-characteristics' => function(data, events, callback(err), remote, connectionID) { }
+ * This event is emitted when a client wishes to set the current value of one or more characteristics and/or
+ * subscribe to one or more events. The 'events' param is an initially-empty object, associated with the current
+ * connection, on which you may store event registration keys for later processing. The listener must call
+ * the provided callback when the request has been processed.
+ */
+
+namespace HapSharp.Core
+{
+ class HAP_TYPES
+ {
+ public const int REQUEST_TYPE = 0x00,
+ USERNAME = 0x01,
+ SALT = 0x02,
+ PUBLIC_KEY = 0x03,
+ PASSWORD_PROOF = 0x04,
+ ENCRYPTED_DATA = 0x05,
+ SEQUENCE_NUM = 0x06,
+ ERROR_CODE = 0x07,
+ PROOF = 0x0a;
+ }
+
+ class HAP_ERRORCODES
+ {
+ public const int INVALID_REQUEST = 0x02,
+ INVALID_SIGNATURE = 0x04;
+ }
+
+ class HAP_STATUS
+ {
+ public const int SUCCESS = 0,
+ INSUFFICIENT_PRIVILEGES = -70401,
+ SERVICE_COMMUNICATION_FAILURE = -70402,
+ RESOURCE_BUSY = -70403,
+ READ_ONLY_CHARACTERISTIC = -70404,
+ WRITE_ONLY_CHARACTERISTIC = -70405,
+ NOTIFICATION_NOT_SUPPORTED = -70406,
+ OUT_OF_RESOURCE = -70407,
+ OPERATION_TIMED_OUT = -70408,
+ RESOURCE_DOES_NOT_EXIST = -70409,
+ INVALID_VALUE_IN_REQUEST = -70410;
+ }
+
+ class HapServer
+ {
+
+ System.Timers.Timer timer = new System.Timers.Timer (1000 * 60 * 10);
+
+ public HapServer ()
+ {
+ // so iOS is very reluctant to actually disconnect HAP connections (as in, sending a FIN packet).
+ // For instance, if you turn off wifi on your phone, it will not close the connection, instead
+ // it will leave it open and hope that it's still valid when it returns to the network. And Node,
+ // by itself, does not ever "discover" that the connection has been closed behind it, until a
+ // potentially very long system-level socket timeout (like, days). To work around this, we have
+ // invented a manual "keepalive" mechanism where we send "empty" events perodicially, such that
+ // when Node attempts to write to the socket, it discovers that it's been disconnected after
+ // an additional one-minute timeout (this timeout appears to be hardcoded).
+
+ timer.Start ();
+ timer.Elapsed += (s, e) => {
+ //this._keepAliveTimerID = setInterval (this._onKeepAliveTimerTick.bind (this), 1000 * 60 * 10); // send keepalive every 10 minutes
+ };
+ }
+ }
+}
diff --git a/HapSharp.Core/HapSharp.Core.csproj b/HapSharp.Core/HapSharp.Core.csproj
new file mode 100644
index 0000000..16b4e48
--- /dev/null
+++ b/HapSharp.Core/HapSharp.Core.csproj
@@ -0,0 +1,48 @@
+
+
+
+ Debug
+ AnyCPU
+ {24CB3D20-D49D-4361-8B78-92726D8477BD}
+ Library
+ HapSharp.Core
+ HapSharp.Core
+ v4.7
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ false
+
+
+ true
+ bin\Release
+ prompt
+ 4
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HapSharp.Core/MessageType.cs b/HapSharp.Core/MessageType.cs
new file mode 100755
index 0000000..5aa56fb
--- /dev/null
+++ b/HapSharp.Core/MessageType.cs
@@ -0,0 +1,14 @@
+namespace HapSharp.Core
+{
+ public enum MessageType
+ {
+ //service-configurationChange
+ ServiceConfigurationChange,
+
+ //characteristic-change
+ CharacteristicChange,
+
+ //service-characteristic-change
+ ServiceCharacteristicChange
+ }
+}
diff --git a/HapSharp.Core/OperationExpensions.cs b/HapSharp.Core/OperationExpensions.cs
new file mode 100644
index 0000000..bdafc53
--- /dev/null
+++ b/HapSharp.Core/OperationExpensions.cs
@@ -0,0 +1,82 @@
+using System;
+
+namespace HapSharp.Core
+{
+ public static class OperationExpensions
+ {
+ public static int GreaterSignedOperator (this int sender, int value)
+ {
+ return sender >> value;
+ }
+ public static int MinorSignedOperator (this int sender, int value)
+ {
+ return sender << value;
+ }
+
+ public static int MinorShiftRightOperator (this int sender, int value)
+ {
+ return (int)(((uint)sender) << value);
+ }
+
+ public static int GreaterShiftRightOperator (this int sender, int value)
+ {
+ return (int)(((uint)sender) >> value);
+ }
+
+ ///
+ /// Get the array slice between the two indexes.
+ /// ... Inclusive for start index, exclusive for end index.
+ ///
+ public static T[] Slice (this T[] source, int len)
+ {
+ // Handles negative ends.
+ T[] res = new T[len];
+ for (int i = 0; i < len; i++) {
+ res[i] = source[i];
+ }
+ return res;
+ }
+
+ ///
+ /// Get the array slice between the two indexes.
+ /// ... Inclusive for start index, exclusive for end index.
+ ///
+ public static T[] Slice (this T[] source, int start, int end)
+ {
+ // Handles negative ends.
+ if (end < 0) {
+ end = source.Length + end;
+ }
+ int len = end - start;
+
+ // Return new array.
+ T[] res = new T[len];
+ for (int i = 0; i < len; i++) {
+ res[i] = source[i + start];
+ }
+ return res;
+ }
+
+
+ ///
+ /// Get the array slice between the two indexes.
+ /// ... Inclusive for start index, exclusive for end index.
+ ///
+ public static void Copy (this Array source, Array target, int targetStart, int sourceStart, int sourceEnd)
+ {
+ Array.Copy (source, sourceStart, target, targetStart, sourceEnd - sourceStart);
+ }
+
+ ///
+ /// Get the array slice between the two indexes.
+ /// ... Inclusive for start index, exclusive for end index.
+ ///
+ public static int[] Concat (this int[] x, int[] y)
+ {
+ var z = new int[x.Length + y.Length];
+ x.CopyTo (z, 0);
+ y.CopyTo (z, x.Length);
+ return z;
+ }
+ }
+}
diff --git a/HapSharp.Core/Properties/AssemblyInfo.cs b/HapSharp.Core/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..642c37b
--- /dev/null
+++ b/HapSharp.Core/Properties/AssemblyInfo.cs
@@ -0,0 +1,27 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle ("HapSharp.Core")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("${AuthorCopyright}")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+[assembly: InternalsVisibleTo ("HapSharp.Core.Tests")]
\ No newline at end of file
diff --git a/HapSharp.Core/Service.cs b/HapSharp.Core/Service.cs
new file mode 100755
index 0000000..0efc0db
--- /dev/null
+++ b/HapSharp.Core/Service.cs
@@ -0,0 +1,57 @@
+/*
+* Service represents a set of grouped values necessary to provide a logical function. For instance, a
+* "Door Lock Mechanism" service might contain two values, one for the "desired lock state" and one for the
+* "current lock state". A particular Service is distinguished from others by its "type", which is a UUID.
+* HomeKit provides a set of known Service UUIDs defined in HomeKitTypes.js along with a corresponding
+* concrete subclass that you can instantiate directly to setup the necessary values. These natively-supported
+* Services are expected to contain a particular set of Characteristics.
+*
+* Unlike Characteristics, where you cannot have two Characteristics with the same UUID in the same Service,
+* you can actually have multiple Services with the same UUID in a single Accessory. For instance, imagine
+* a Garage Door Opener with both a "security light" and a "backlight" for the display. Each light could be
+* a "Lightbulb" Service with the same UUID. To account for this situation, we define an extra "subtype"
+* property on Service, that can be a string or other string-convertible object that uniquely identifies the
+* Service among its peers in an Accessory. For instance, you might have `service1.subtype = 'security_light'`
+* for one and `service2.subtype = 'backlight'` for the other.
+*
+* You can also define custom Services by providing your own UUID for the type that you generate yourself.
+* Custom Services can contain an arbitrary set of Characteristics, but Siri will likely not be able to
+* work with these.
+*
+* @event 'characteristic-change' => function({characteristic, oldValue, newValue, context}) { }
+* Emitted after a change in the value of one of our Characteristics has occurred.
+*/
+
+using System;
+using System.Collections.Generic;
+
+namespace HapSharp.Core
+{
+ public class Service
+ {
+ public event EventHandler Set;
+
+ List Characteristics = new List ();
+
+ public event EventHandler MessageReceived;
+
+ public Service (string displayName, string UUID, string subtype) {
+
+ }
+
+ public void Add ()
+ {
+
+ }
+
+ public void SetCharacteristic ()
+ {
+
+ }
+
+ public Service GetCharacteristic (ServiceCharacteristicType characteristic)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/HapSharp.Core/Types.cs b/HapSharp.Core/Types.cs
new file mode 100644
index 0000000..f5702ca
--- /dev/null
+++ b/HapSharp.Core/Types.cs
@@ -0,0 +1,28 @@
+using System;
+namespace HapSharp.Core
+{
+ public enum Utf8AsciiLatin1Encoding
+ {
+ UTF8, ASCII, LATIN1
+ }
+
+ public enum HexBase64Latin1Encoding
+ {
+ LATIN1, HEX, BASE64
+ }
+
+ public enum Utf8AsciiBinaryEncoding
+ {
+ UTF8, ASCII, BINARY
+ }
+
+ public enum HexBase64BinaryEncoding
+ {
+ BINARY, BASE64, HEX
+ }
+
+ public enum ECDHKeyFormat
+ {
+ Compressed, Uncompressed, Hybrid
+ }
+}
diff --git a/HapSharp.Core/UUID.cs b/HapSharp.Core/UUID.cs
new file mode 100644
index 0000000..be0b5db
--- /dev/null
+++ b/HapSharp.Core/UUID.cs
@@ -0,0 +1,14 @@
+using System;
+namespace HapSharp.Core
+{
+ class UUID
+ {
+ public Guid guid;
+ readonly public string Name;
+ public UUID (string name)
+ {
+ Name = name;
+ guid = Guid.NewGuid ();
+ }
+ }
+}