‪Black Box
MiBand.cs
Go to the documentation of this file.
1 /*
2 * FILE : MiBand.cs
3 * PROJECT : Black Box
4 * PROGRAMMER : BRIAN HINDS
5 * FIRST VERSION : 1.0.0.0
6 * DATE : March 20, 2019
7 * DESCRIPTION : Used for manage Mi Band 2 BLE device - Authenticate and manager heart rate sensor
8 */
9 
10 using System;
11 using System.IO;
12 using System.Linq;
13 using System.Security.Cryptography;
14 using System.Threading;
15 using Windows.Devices.Bluetooth.GenericAttributeProfile;
16 using Windows.Storage.Streams;
17 
18 namespace ‪MiBand_Heartrate
19 {
20  internal class ‪MiBand
21  {
22  // Services UUID
23  public static string ‪ATS_UUID = "0000fee1-0000-1000-8000-00805f9b34fb"; // authentication
24 
25  public static string ‪HMS_UUID = "0000180d-0000-1000-8000-00805f9b34fb"; // Heart rate
26  public static string ‪SNS_UUID = "0000fee0-0000-1000-8000-00805f9b34fb"; // Sensor
27 
28  // Characteristics UUID
29  public static string ‪ATC_UUID = "00000009-0000-3512-2118-0009af100700"; // authentication
30 
31  public static string ‪HRM_UUID = "00002a37-0000-1000-8000-00805f9b34fb"; // Heart rate notify
32  public static string ‪HMC_UUID = "00002a39-0000-1000-8000-00805f9b34fb"; // Heart rate control
33  public static string ‪SNC_UUID = "00000001-0000-3512-2118-0009af100700"; // Sensor control
34 
35  // ----------------------------
36 
37  public delegate void ‪OnHeartrateChangedHandler(‪MiBand device, ushort value);
38 
39  public delegate void ‪OnAuthHandler(‪MiBand device, bool status);
40 
42  private byte[] ‪_authKey;
43  private GattDeviceService ‪HMSService = null;
44  private GattCharacteristic ‪HMRcharacteristic = null;
45  private GattCharacteristic ‪HMCCharacteristic = null;
46  private GattCharacteristic ‪SNSCharacteristic = null;
47  private Thread ‪pingThread = null;
48 
50 
51  private bool ‪continuousMode = false;
52 
53  public bool ‪ContinuousMode
54  {
55  get => ‪continuousMode;
56  set
57  {
58  if (‪HMCCharacteristic == null)
59  {
60  ‪continuousMode = value;
61  }
62  }
63  }
64 
65  // ----------------------------
66 
67  public ‪MiBand(‪BLEManager bluetoothLEManager)
68  {
69  ‪manager = bluetoothLEManager;
70  }
71 
72  /* Mi Band 2 - authentication
73  * Before using Mi Band 2, an authentication is required. Authenticate following this steps :
74  * 1. Enable notification on authentication characteristic
75  * 2. Generate authentication key 16 bytes long
76  * 3. Send key to authentication characteristic and asking for a random number
77  * 4. Get random number from characteristic notification
78  * 5. Encrypt number using AES ECB mode without padding and with authentication key
79  * 6. Send encrypted random number to authentication characteristic
80  * see https://leojrfs.github.io/writing/miband2-part1-auth/#reference
81  */
82 
83  public async void ‪Authenticate(‪OnAuthHandler callback = null)
84  {
85  GattDeviceServicesResult servicesResult = await ‪manager.‪Device.GetGattServicesForUuidAsync(new Guid(‪ATS_UUID));
86  if (servicesResult.Status == GattCommunicationStatus.Success && servicesResult.Services.Count > 0)
87  {
88  GattDeviceService service = servicesResult.Services[0];
89 
90  GattCharacteristicsResult characteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid(‪ATC_UUID));
91  if (characteristicsResult.Status == GattCommunicationStatus.Success && characteristicsResult.Characteristics.Count > 0)
92  {
93  GattCharacteristic characteristic = characteristicsResult.Characteristics[0];
94 
95  // Enable notification
96  GattCommunicationStatus status = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
97  if (status == GattCommunicationStatus.Success)
98  {
99  characteristic.ValueChanged += (GattCharacteristic sender, GattValueChangedEventArgs args) =>
100  {
101  DataReader reader = DataReader.FromBuffer(args.CharacteristicValue);
102  byte[] messageType = new byte[3]; reader.ReadBytes(messageType);
103 
104  if (messageType[1] == 0x01) // Request random number
105  {
106  if (messageType[2] == 0x01)
107  {
108  ‪manager.‪Write(sender, new byte[] { 0x02, 0x08 });
109  }
110  else
111  {
112  callback.Invoke(this, false);
113  service.Dispose();
114  }
115  }
116  else if (messageType[1] == 0x02) // Get random number
117  {
118  byte[] number = new byte[reader.UnconsumedBufferLength];
119  reader.ReadBytes(number);
120 
121  // Encrypt number
122  byte[] encNumber;
123  using (Aes aes = Aes.Create())
124  {
125  aes.Key = ‪_authKey;
126  aes.Mode = CipherMode.ECB;
127  aes.Padding = PaddingMode.None;
128 
129  ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); // Constant iv, bad :(
130  using (MemoryStream stream = new MemoryStream())
131  {
132  using (CryptoStream cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
133  {
134  cryptoStream.Write(number, 0, number.Length);
135  cryptoStream.FlushFinalBlock();
136  encNumber = stream.ToArray();
137  }
138  }
139  }
140 
141  // Send encrypted number
142  using (MemoryStream stream = new MemoryStream())
143  {
144  stream.Write(new byte[] { 0x03, 0x08 }, 0, 2);
145  stream.Write(encNumber, 0, encNumber.Length);
146  ‪manager.‪Write(characteristic, stream.ToArray());
147  }
148  }
149  else if (messageType[1] == 0x03)
150  {
151  if (messageType[2] == 0x01)
152  {
153  callback.Invoke(this, true);
154  }
155  else
156  {
157  callback.Invoke(this, false);
158  service.Dispose();
159  }
160  }
161  };
162  }
163 
164  // Define authentication secret key
165  byte[] hash = new SHA256Managed().ComputeHash(Guid.NewGuid().ToByteArray());
166  ‪_authKey = hash.Take(16).ToArray();
167 
168  // Send key it to device
169  using (MemoryStream stream = new MemoryStream())
170  {
171  stream.Write(new byte[] { 0x01, 0x08 }, 0, 2);
172  stream.Write(‪_authKey, 0, ‪_authKey.Length);
173  ‪manager.‪Write(characteristic, stream.ToArray());
174  }
175  }
176  }
177  }
178 
179  /* Mi Band 2 - Continuous heart rate monitoring
180  * 1. Enable sensor
181  * 2. Enable notification on heart rate characteristic
182  * 3. Start heart rate sensor on continuous measuring
183  * 4. Activate sensor characteristic with 0x02
184  * 5. Send ping 0x16 after each heart rate notice to keep sensor active
185  * see https://medium.com/machine-learning-world/how-i-hacked-xiaomi-miband-2-to-control-it-from-linux-a5bd2f36d3ad
186  */
187 
188  public async void ‪StartMonitorHeartrate()
189  {
190  // Enabling sensor
191  GattDeviceServicesResult servicesResult = await ‪manager.‪Device.GetGattServicesForUuidAsync(new Guid(‪SNS_UUID));
192  if (servicesResult.Status == GattCommunicationStatus.Success && servicesResult.Services.Count > 0)
193  {
194  GattDeviceService service = servicesResult.Services[0];
195  GattCharacteristicsResult characteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid(‪SNC_UUID));
196  if (characteristicsResult.Status == GattCommunicationStatus.Success && characteristicsResult.Characteristics.Count > 0)
197  {
198  GattCharacteristic characteristic = characteristicsResult.Characteristics[0];
199  ‪SNSCharacteristic = characteristic;
200  ‪manager.‪Write(characteristic, new byte[] { 0x01, 0x03, 0x19 });
201  }
202  }
203 
204  // Enabling Heart rate measurements
205  servicesResult = await ‪manager.‪Device.GetGattServicesForUuidAsync(new Guid(‪HMS_UUID));
206  if (servicesResult.Status == GattCommunicationStatus.Success && servicesResult.Services.Count > 0)
207  {
208  GattDeviceService service = servicesResult.Services[0];
209 
210  // Enabling notification
211  GattCharacteristicsResult characteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid(‪HRM_UUID));
212  if (characteristicsResult.Status == GattCommunicationStatus.Success && characteristicsResult.Characteristics.Count > 0)
213  {
214  ‪HMRcharacteristic = characteristicsResult.Characteristics[0];
215 
216  GattCommunicationStatus status = await ‪HMRcharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
217  if (status == GattCommunicationStatus.Success)
218  {
219  ‪HMRcharacteristic.ValueChanged += (GattCharacteristic sender, GattValueChangedEventArgs args) =>
220  {
221  DataReader reader = DataReader.FromBuffer(args.CharacteristicValue);
222  ushort heartrate = reader.ReadUInt16();
223 
224  if (heartrate > 0)
225  { // Other wise there is a high probability that sensor failed to retrieve heart rate or that you're dead ;)
226  ‪HeartrateChanged?.Invoke(this, heartrate);
227  }
228 
229  if (!‪continuousMode)
230  {
231  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x15, 0x02, 0x01 });
232  }
233  };
234  }
235  }
236 
237  // Enable measurements
238  characteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid(‪HMC_UUID));
239  if (characteristicsResult.Status == GattCommunicationStatus.Success && characteristicsResult.Characteristics.Count > 0)
240  {
241  ‪HMCCharacteristic = characteristicsResult.Characteristics[0];
242 
243  if (‪continuousMode)
244  {
245  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x15, 0x01, 0x01 });
246  }
247  else
248  {
249  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x15, 0x02, 0x00 });
250  }
251 
252  if (‪SNSCharacteristic != null)
253  {
254  ‪manager.‪Write(‪SNSCharacteristic, new byte[] { 0x02 });
255  }
256  }
257 
258  // Enable ping HMC every 5 sec.
259  ‪pingThread = new Thread(new ThreadStart(‪RunPingSensor));
260  ‪pingThread.Start();
261 
262  ‪HMSService = service;
263  }
264  }
265 
270  {
271  if (‪HMCCharacteristic != null)
272  {
273  if (‪continuousMode)
274  {
275  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x15, 0x01, 0x00 });
276  }
277  else
278  {
279  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x15, 0x02, 0x00 });
280  }
281  }
282 
283  ‪HMCCharacteristic = null;
284 
285  if (‪pingThread != null)
286  {
287  ‪pingThread.Join();
288  ‪pingThread = null;
289  }
290 
291  if (‪SNSCharacteristic != null)
292  {
293  ‪SNSCharacteristic.Service.Dispose();
294  ‪SNSCharacteristic = null;
295  }
296 
297  if (‪HMSService != null)
298  {
299  ‪HMSService.Dispose();
300  ‪HMSService = null;
301  }
302  }
303 
306  private void ‪RunPingSensor()
307  {
308  while (‪HMCCharacteristic != null)
309  {
310  ‪manager.‪Write(‪HMCCharacteristic, new byte[] { 0x16 });
311  Thread.Sleep(5000);
312  }
313  }
314  }
315 }
‪static string SNS_UUID
Definition: MiBand.cs:26
‪static string ATC_UUID
Definition: MiBand.cs:29
‪BLEManager manager
Definition: MiBand.cs:41
‪static string SNC_UUID
Definition: MiBand.cs:33
‪OnHeartrateChangedHandler HeartrateChanged
Definition: MiBand.cs:49
‪GattCharacteristic HMRcharacteristic
Definition: MiBand.cs:44
‪static string HRM_UUID
Definition: MiBand.cs:31
‪BluetoothLEDevice Device
Definition: BLEManager.cs:32
‪MiBand(BLEManager bluetoothLEManager)
Definition: MiBand.cs:67
‪GattDeviceService HMSService
Definition: MiBand.cs:43
‪void StopMonitorHeartrate()
‪Stop heart rate reading
Definition: MiBand.cs:269
‪static string HMC_UUID
Definition: MiBand.cs:32
‪static string ATS_UUID
Definition: MiBand.cs:23
‪async void StartMonitorHeartrate()
Definition: MiBand.cs:188
‪async void Write(GattCharacteristic c, byte[] data)
Definition: BLEManager.cs:122
‪delegate void OnHeartrateChangedHandler(MiBand device, ushort value)
‪delegate void OnAuthHandler(MiBand device, bool status)
‪static string HMS_UUID
Definition: MiBand.cs:25
‪GattCharacteristic SNSCharacteristic
Definition: MiBand.cs:46
‪GattCharacteristic HMCCharacteristic
Definition: MiBand.cs:45
‪async void Authenticate(OnAuthHandler callback=null)
Definition: MiBand.cs:83