├── Readme.md └── miband2_auth.py /Readme.md: -------------------------------------------------------------------------------- 1 | # Mi Band 2 2 | 3 | Playing around with the [**Mi Band 2**](http://www.mi.com/en/miband2/) and Linux. 4 | 5 | - [Mi Band 2, Part 1: Authentication](https://leojrfs.github.io/writing/miband2-part1-auth/) 6 | 7 | ## NEW FORK 8 | 9 | Theres a fork by @creotiv with extended work and more features. Check [#4](https://github.com/leojrfs/miband2/issues/4) 10 | 11 | https://github.com/creotiv/MiBand2 12 | -------------------------------------------------------------------------------- /miband2_auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import struct 3 | import time 4 | import sys 5 | import argparse 6 | from Crypto.Cipher import AES 7 | from bluepy.btle import Peripheral, DefaultDelegate, ADDR_TYPE_RANDOM 8 | 9 | 10 | class MiBand2(Peripheral): 11 | _KEY = b'\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x40\x41\x42\x43\x44\x45' 12 | _send_key_cmd = struct.pack('<18s', b'\x01\x08' + _KEY) 13 | _send_rnd_cmd = struct.pack('<2s', b'\x02\x08') 14 | _send_enc_key = struct.pack('<2s', b'\x03\x08') 15 | 16 | def __init__(self, addr): 17 | Peripheral.__init__(self, addr, addrType=ADDR_TYPE_RANDOM) 18 | print("Connected") 19 | self.handle = 0 20 | self.raw_data = b'0' 21 | self.timeout = 5.0 22 | self.auth = False 23 | # Enable auth service notifications on startup 24 | self.auth_notif(True) 25 | 26 | def encrypt(self, message): 27 | aes = AES.new(self._KEY, AES.MODE_ECB) 28 | return aes.encrypt(message) 29 | 30 | def auth_notif(self, status): 31 | if status: 32 | print("Enabling Auth Service notifications status...") 33 | self.writeCharacteristic(0x51, struct.pack('<2s', b'\x01\x00'), True) 34 | elif not status: 35 | print("Disabling Auth Service notifications status...") 36 | self.writeCharacteristic(0x51, struct.pack('<2s', b'\x00\x00'), True) 37 | else: 38 | print("Something went wrong while changing the Auth Service notifications status...") 39 | 40 | def send_key(self): 41 | print("Sending Key...") 42 | self.writeCharacteristic(0x50, self._send_key_cmd) 43 | self.waitForNotifications(self.timeout) 44 | 45 | def req_rdn(self): 46 | print("Requesting random number...") 47 | self.writeCharacteristic(0x50, self._send_rnd_cmd) 48 | self.waitForNotifications(self.timeout) 49 | 50 | def send_enc_rdn(self, data): 51 | print("Sending encrypted random number") 52 | cmd = self._send_enc_key + self.encrypt(data) 53 | send_cmd = struct.pack('<18s', cmd) 54 | self.writeCharacteristic(0x50, send_cmd) 55 | self.waitForNotifications(self.timeout) 56 | 57 | def authenticate(self): 58 | self.setDelegate(AuthenticationDelegate(self)) 59 | print("Requesting random number...") 60 | self.writeCharacteristic(0x50, self._send_rnd_cmd) 61 | self.waitForNotifications(self.timeout) 62 | while True: 63 | if self.auth: 64 | return True 65 | elif self.auth == -1: 66 | return False 67 | 68 | 69 | class AuthenticationDelegate(DefaultDelegate): 70 | 71 | """This Class inherits DefaultDelegate to handle the authentication process.""" 72 | def __init__(self, device): 73 | DefaultDelegate.__init__(self) 74 | self.device = device 75 | 76 | def handleNotification(self, hnd, data): 77 | # Debug purposes 78 | # print("HANDLE: " + str(hex(hnd))) 79 | # print("DATA: " + str(data.encode("hex"))) 80 | if hnd == int('0x50', 16): 81 | if data[:3] == b'\x10\x01\x01': 82 | self.device.req_rdn() 83 | elif data[:3] == b'\x10\x01\x04': 84 | print("Something went wrong while sending the key!") 85 | print("Response: " + str(data.encode("hex"))) 86 | self.device.auth = -1 87 | elif data[:3] == b'\x10\x02\x01': 88 | random_nr = data[3:] 89 | self.device.send_enc_rdn(random_nr) 90 | elif data[:3] == b'\x10\x02\x04': 91 | print("Authention failed! Something wrong when requesting the random number...") 92 | print("Response: " + str(data.encode("hex"))) 93 | self.device.auth = -1 94 | elif data[:3] == b'\x10\x03\x01': 95 | print("Authenticated!") 96 | self.device.auth = True 97 | elif data[:3] == b'\x10\x03\x04': 98 | print("Encryption Key Auth Fail, sending new Key...") 99 | self.device.send_key() 100 | else: 101 | print("Auth error, cant help! Response: " + str(data.encode("hex"))) 102 | self.device.auth = -1 103 | 104 | 105 | def main(): 106 | """ main func """ 107 | parser = argparse.ArgumentParser() 108 | parser.add_argument('host', action='store', help='MAC of BT device') 109 | parser.add_argument('-t', action='store', type=float, default=3.0, 110 | help='duration of each notification') 111 | parser.add_argument('-a', '--authenticate', action='store_true', default=False) 112 | parser.add_argument('-n', '--notify', action='store_true', default=False) 113 | arg = parser.parse_args(sys.argv[1:]) 114 | 115 | print('Connecting to ' + arg.host) 116 | band = MiBand2(arg.host) 117 | band.setSecurityLevel(level="medium") 118 | 119 | if arg.authenticate: 120 | if band.authenticate(): 121 | if arg.notify: 122 | print("Sending message notification...") 123 | band.writeCharacteristic(0x25, struct.pack('