Skip to content
Permalink
Browse files

Added the BLE unlock helper

  • Loading branch information...
COM8 committed Sep 24, 2019
1 parent cbdc261 commit cdf15031503e913dba67d659f64ae3d5af8f9f21
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Logging;
using Microsoft.Toolkit.Uwp.Connectivity;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using XMPP_API_IoT.Classes.Bluetooth.Events;

@@ -16,6 +18,7 @@ public class BLEDevice
//--------------------------------------------------------Attributes:-----------------------------------------------------------------\\
#region --Attributes--
private BLEDeviceState state = BLEDeviceState.DISCONNECTED;
private readonly BLEUnlockHelper UNLOCK_HELPER;
public readonly BluetoothLEDevice DEVICE;
public readonly ObservableBluetoothLEDevice OBSERVALBLE_DEVICE;

@@ -31,6 +34,7 @@ public class BLEDevice
private BLEDevice(BluetoothLEDevice device)
{
DEVICE = device;
UNLOCK_HELPER = new BLEUnlockHelper(this);
DEVICE.ConnectionStatusChanged += DEVICE_ConnectionStatusChanged;
OBSERVALBLE_DEVICE = new ObservableBluetoothLEDevice(device.DeviceInformation);
}
@@ -120,6 +124,11 @@ public async Task<byte[]> ReadBytesAsync(Guid uuid)
return c != null ? await ReadBytesAsync(c) : null;
}

public Task<bool> UnlockAsync()
{
return UNLOCK_HELPER.UnlockAsync();
}

#endregion

#region --Misc Methods (Private)--
@@ -196,7 +205,7 @@ private async Task<bool> SubscribeToCharacteristicAsync(GattCharacteristic c)
{
status = await c.WriteClientCharacteristicConfigurationDescriptorAsync(cccdValue);
}
catch (UnauthorizedAccessException)
catch (Exception)
{
return false;
}
@@ -289,6 +298,32 @@ private byte[] ReadBytesFromBuffer(IBuffer buffer)
return data;
}

public async Task<GattWriteResult> WriteStringAsync(Guid uuid, string data)
{
return await WriteBytesAsync(uuid, Encoding.UTF8.GetBytes(data));
}

public async Task<GattWriteResult> WriteBytesAsync(Guid uuid, byte[] data)
{
CHARACTERISTICS.TryGetValue(uuid, out GattCharacteristic c);
if (c != null)
{
IBuffer buffer = CryptographicBuffer.CreateFromByteArray(data);
GattWriteResult result = await c.WriteValueWithResultAsync(buffer);
if (result.Status == GattCommunicationStatus.Success)
{
// Convert to little endian:
CACHE.AddToDictionary(uuid, data);
}
return result;
}
else
{
Logger.Warn("Failed to write to write bytes to: " + uuid.ToString() + " - not loaded.");
}
return null;
}

#endregion

#region --Misc Methods (Protected)--
@@ -140,7 +140,8 @@ public void Dispose()
#region --Misc Methods (Private)--
private async Task OnDevicesChangedAsync()
{
foreach (ObservableBluetoothLEDevice device in BLE_HELPER.BluetoothLeDevices)
List<ObservableBluetoothLEDevice> devices = new List<ObservableBluetoothLEDevice>(BLE_HELPER.BluetoothLeDevices);
foreach (ObservableBluetoothLEDevice device in devices)
{
if (string.Equals(device.BluetoothAddressAsString.ToLowerInvariant(), targetMac))
{
@@ -150,9 +151,13 @@ private async Task OnDevicesChangedAsync()
// Try to connect to the device:
if (await bleDevice.ConnectAsync())
{
// Invoke event:
DeviceFound?.Invoke(this, new BLEDeviceEventArgs(bleDevice));
return;
// Execute the challenge response mechanism:
if (await bleDevice.UnlockAsync())
{
// Invoke event:
DeviceFound?.Invoke(this, new BLEDeviceEventArgs(bleDevice));
return;
}
}
}
}
@@ -0,0 +1,132 @@
using System;
using System.Threading.Tasks;
using Logging;

namespace XMPP_API_IoT.Classes.Bluetooth
{
internal class BLEUnlockHelper: IDisposable
{
//--------------------------------------------------------Attributes:-----------------------------------------------------------------\\
#region --Attributes--
private readonly BLEDevice DEVICE;

/// <summary>
/// Default timeout for unlocking is 5 seconds.
/// </summary>
private readonly TimeSpan TIMEOUT = TimeSpan.FromSeconds(5.0);

private Task timeoutTask;
private TaskCompletionSource<bool> completionSource;
private bool disposed;
private bool done;

#endregion
//--------------------------------------------------------Constructor:----------------------------------------------------------------\\
#region --Constructors--
public BLEUnlockHelper(BLEDevice device)
{
DEVICE = device;
}

#endregion
//--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\
#region --Set-, Get- Methods--


#endregion
//--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\
#region --Misc Methods (Public)--
public async Task<bool> UnlockAsync()
{
done = false;
completionSource = new TaskCompletionSource<bool>();
DEVICE.CACHE.CharacteristicChanged -= CACHE_CharacteristicChanged;
DEVICE.CACHE.CharacteristicChanged += CACHE_CharacteristicChanged;

// At the moment a simple "ready" unlocks the ESP32.
// This will change in future versions (public/private key authentication).
await DEVICE.WriteStringAsync(BTUtils.CHARACTERISTIC_CHALLENGE_RESPONSE_WRITE, "ready");
return await WaitForCompletionAsync();
}

public void stop()
{
if (!disposed)
{
done = true;
if (!(completionSource is null) && !completionSource.Task.IsCanceled && !completionSource.Task.IsCompleted && !completionSource.Task.IsFaulted)
{
completionSource.SetResult(false);
}

if (!(DEVICE is null))
{
DEVICE.CACHE.CharacteristicChanged -= CACHE_CharacteristicChanged;
}
}
}

public void Dispose()
{
stop();
disposed = true;
}

#endregion

#region --Misc Methods (Private)--
private async Task<bool> WaitForCompletionAsync()
{
timeoutTask = Task.Delay(TIMEOUT);

// Wait for completion:
Task resultTask = await Task.WhenAny(new Task[] { completionSource.Task, timeoutTask });

bool result = false;
// Evaluate and return result:
if (resultTask == completionSource.Task)
{
if (completionSource.Task.IsCompleted)
{
Logger.Info("Bluetooth unlock helper finished with: " + completionSource.Task.Result);
result = completionSource.Task.Result;
}
else
{
Logger.Warn("Bluetooth unlock helper failed with: UNKNOWN");
result = false;
}
}
else
{
Logger.Warn("Bluetooth unlock helper failed with: TIMEOUT");
result = false;
}

stop();
return result;
}

#endregion

#region --Misc Methods (Protected)--


#endregion
//--------------------------------------------------------Events:---------------------------------------------------------------------\\
#region --Events--
private void CACHE_CharacteristicChanged(CharacteristicsCache sender, Events.CharacteristicChangedEventArgs args)
{
if (args.UUID.Equals(BTUtils.CHARACTERISTIC_CHALLENGE_RESPONSE_UNLOCKED))
{
if (DEVICE.CACHE.GetBool(BTUtils.CHARACTERISTIC_CHALLENGE_RESPONSE_UNLOCKED))
{
completionSource.TrySetResult(true);
done = true;
}
}
}

#endregion
}
}
@@ -7,11 +7,19 @@ public static class BTUtils
{
//--------------------------------------------------------Attributes:-----------------------------------------------------------------\\
#region --Attributes--
public static readonly Guid CHARACTERISTIC_SERIAL_NUMBER = Guid.Parse("00002A25-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_HARDWARE_REVISION = Guid.Parse("00002A27-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_DEVICE_NAME = Guid.Parse("00002A00-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_LANGUAGE = Guid.Parse("00002AA2-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_HARDWARE_REVISION = Guid.Parse("00002A27-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_SERIAL_NUMBER = Guid.Parse("00002A25-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_MANUFACTURER_NAME = Guid.Parse("00002A29-0000-1000-8000-00805F9B34FB");
public static readonly Guid CHARACTERISTIC_WIFI_SSID = Guid.Parse("00000001-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_WIFI_PASSWORD = Guid.Parse("00000002-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_JID = Guid.Parse("00000003-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_JID_PASSWORD = Guid.Parse("00000004-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_SETTINGS_DONE = Guid.Parse("00000005-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_CHALLENGE_RESPONSE_READ = Guid.Parse("00000006-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_CHALLENGE_RESPONSE_WRITE = Guid.Parse("00000007-0000-0000-0000-000000000002");
public static readonly Guid CHARACTERISTIC_CHALLENGE_RESPONSE_UNLOCKED = Guid.Parse("00000008-0000-0000-0000-000000000002");

#endregion
//--------------------------------------------------------Constructor:----------------------------------------------------------------\\
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Logging;
using XMPP_API.Classes.Crypto;
using XMPP_API_IoT.Classes.Bluetooth.Events;

namespace XMPP_API_IoT.Classes.Bluetooth
@@ -9,7 +11,15 @@ public class CharacteristicsCache
{
//--------------------------------------------------------Attributes:-----------------------------------------------------------------\\
#region --Attributes--
public static readonly Guid[] SUBSCRIBE_TO_CHARACTERISTICS = new Guid[] { };
public static readonly Guid[] SUBSCRIBE_TO_CHARACTERISTICS = new Guid[] {
BTUtils.CHARACTERISTIC_LANGUAGE,
BTUtils.CHARACTERISTIC_HARDWARE_REVISION,
BTUtils.CHARACTERISTIC_SERIAL_NUMBER,
BTUtils.CHARACTERISTIC_MANUFACTURER_NAME,

BTUtils.CHARACTERISTIC_CHALLENGE_RESPONSE_READ,
BTUtils.CHARACTERISTIC_CHALLENGE_RESPONSE_UNLOCKED
};

private readonly Dictionary<Guid, byte[]> CHARACTERISTICS = new Dictionary<Guid, byte[]>();
private readonly object CHARACTERISTICS_LOCK = new object();
@@ -127,6 +137,7 @@ internal bool AddToDictionary(Guid uuid, byte[] value, DateTime timestamp)
CHARACTERISTICS[uuid] = value;
if (!oldValue.SequenceEqual(value))
{
Logger.Debug("Bluetooth characteristic " + uuid.ToString() + " has a new value: " + CryptoUtils.byteArrayToHexString(value));
CharacteristicChanged?.Invoke(this, new CharacteristicChangedEventArgs(uuid, oldValue, value));
}
}
@@ -124,6 +124,7 @@
<Compile Include="Classes\Bluetooth\BLEDeviceState.cs" />
<Compile Include="Classes\Bluetooth\BLEScanner.cs" />
<Compile Include="Classes\Bluetooth\BLEScannerState.cs" />
<Compile Include="Classes\Bluetooth\BLEUnlockHelper.cs" />
<Compile Include="Classes\Bluetooth\BTUtils.cs" />
<Compile Include="Classes\Bluetooth\CharacteristicsCache.cs" />
<Compile Include="Classes\Bluetooth\Events\BLEDeviceEventArgs.cs" />

0 comments on commit cdf1503

Please sign in to comment.
You can’t perform that action at this time.