vshymanskyy / miband-js

MiBand 2 JS library for Node.JS and HTML5 (WebBluetooth)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Setting constant 'key' in MiBand class constructor

aytunch opened this issue · comments

I am trying to port this code to Flutter(dart) so people can use it in their mobile devices to communicate with MiBand 3, However I am stuck in authentication. I am not familiar with js too much unfortunately :(

In the code, author shared the comment below.

// TODO: this is constant for now, but should random and managed per-device
this.key = new Buffer('30313233343536373839404142434445', 'hex');

Is this a problem? Why does it need to be managed per device?
I always thought the key was related to a value hardcoded in the MiBand(like a serial number or so)

Back to my Authorization problem. Can anybody please summarize the authentication process for MiBand3? Seems like auth differs for all the different MiBands (1,2,3,4) and the resources on the internet confuses me. I am interested in MiBand3. This repos web app works perfectly so this code is working with my MiBand3

await this.startNotificationsFor('auth')

await this.authenticate()

// Notifications should be enabled after auth
for (let char of ['hrm_data', 'event', 'raw_data']) {
   await this.startNotificationsFor(char)
}

What did I understand wrong?
1-We run startNotifications on "auth" characteristic which is;
00000009-0000-3512-2118-0009af100700

async startNotificationsFor(c) {
    let char = this.char[c]
    await char.startNotifications()
    char.addEventListener('characteristicvaluechanged', this._handleNotify.bind(this));
}

This allows auth event changes to be listened by _handleNotify() function

2-We run authenticate():

async authenticate() {
   await this.authReqRandomKey()
   return new Promise((resolve, reject) => {
      setTimeout(() => reject('Timeout'), 10000);
      this.once('authenticated', resolve);
   });
}

which runs await this.authReqRandomKey() and requests the MiBand for a Random Key. However, aren't we supposed to run authSendNewKey(key) before requesting the random key? I am very confused here.
I am thinking maybe sending authReqRandomKey before authSendNewKey throws an error inside of _handleNotify which is;

else if (cmd === '100304') {
     debug('Encryption Key Auth Fail, sending new key...')
     this.authSendNewKey(this.key)
}

which fires the authSendNewKey for the first time. Inside of this we prepend 0108 to the constant Key I talked about in the beginning: this.char.auth.writeValue(AB([0x01, 0x08], key))

3-MiBand takes this constant Key and sends us a 100101 inside _handleNotify:

if (cmd === '100101') {         // Set New Key OK
     this.authReqRandomKey()
}

4-MiBand gets our request and sends us the random number:

else if (cmd === '100201') {  // Req Random Number OK
     let rdn = value.slice(3)
     let cipher = crypto.createCipheriv('aes-128-ecb', this.key, '').setAutoPadding(false)
     let encrypted = Buffer.concat([cipher.update(rdn), cipher.final()])
     this.authSendEncKey(encrypted)
}

And we use this random number and do the AES stuff to get the encrypted key

5-We send the encrypted key to MiBand by:
this.char.auth.writeValue(AB([0x03, 0x08], encrypted))

6-_handleNotify is triggered for the last time with a value of 100301:

else if (cmd === '100301') {
     debug('Authenticated')
     this.emit('authenticated')
}

I don't understand what does this.once('authenticated', resolve); inside of the Authenticate Promise have to do any thing with the this.emit('authenticated') inside of the _handleNotify.

7- Now we are authenticated and ready to do the heart rate stuff.

Lastly, @creotiv in his how-i-hacked-xiaomi-miband-2-to-control-it-from-linux article

A1-Setting on auth notifications (to get a response) by sending 2 bytes request \x01\x00 to the Des.
A2-Send 16 bytes encryption key to the Char with a command and appending to it 2 bytes \x01\x00 + KEY.
A3-Requesting random key from the device with a command by sending 2 bytes \x02\x00 to the Char.
A4-Getting random key from the device response (last 16 bytes).
A5-Encrypting this random number with our 16 bytes key using the AES/ECB/NoPadding encryption algorithm (from Crypto.Cipher import AES) and send it back to the Char (\x03\x00 + encoded data)

In A1 he talks about sending x01x00 to initiate the auth process. However I did not see something like that in this code. Was it specific to MiBand 2 and not MiBand3?

@dj0001 I am sure you can help me:) I have seen some of your very helpful comments in the issues. Can you please clarify this authorization process