How we did it

How we worked out the Xiaomi Smart Band 10

The goal: use the Xiaomi Smart Band 10 / 10 Pro with our own iPhone app, without Mi Fitness or a Xiaomi account, with the data staying on the phone.

  1. 1. Desk research

    • We read the Xiaomi code in Gadgetbridge (an open source Android app): connection, authentication, encryption, commands and file formats.
    • We compared it with the protobuf schemas of AstroBox, an iOS app that supports the Band 10.
    • Conclusion: the Band 10 speaks Xiaomi's new protocol, and doing this over BLE on an iPhone was very likely possible.
  2. 2. First contact from a Mac

    • In a BLE scan the band showed up with Xiaomi's FE95 advertisement.
    • Connecting from the Mac timed out twice: the band only accepts the iPhone it is paired with.
    • nRF Connect showed that the band doesn't advertise at all while it's connected to the iPhone.
  3. 3. A test app on the iPhone

    We picked up the band that iOS already keeps connected, without pairing again. Its GATT table had the FE95 service with the 005E (read) and 005F (write) characteristics: the BLE V2 channel was open. This was the make-or-break moment of the project.

  4. 4. The key

    • The band wants a 16-byte auth key that Xiaomi's server creates when the band is first paired.
    • On an iPhone that key sits in Files → Mi Fitness, inside a cache database.
    • The app asks you to pick that folder, reads the key on the phone and keeps it in the Keychain. The key is never sent anywhere.
  5. 5. The handshake

    • The phone and the band swap random numbers (nonces).
    • Session keys are derived from the key and those numbers with HKDF-SHA256 ("miwear-auth"). When the band's HMAC matches, the key is right.
    • In the last step we introduce ourselves as device_type = iOS, encrypted with AES-CCM.
    Phone Band nonce nonce + HMAC auth key + nonces HKDF-SHA256 "miwear-auth" session keys the band's HMAC matched: the key is right device_type = iOS · AES-CCM protobuf · AES-128-CTR
    After the handshake, every message is protobuf encrypted with AES-128-CTR.
  6. 6. The encrypted channel and the first command

    • Messages travel as AES-128-CTR encrypted protobuf, in frames that start with a5 a5 and carry a CRC16.
    • The first command was the battery: 46%. We read the same value from the standard BLE battery service too, which proved the decryption right.
    46%the answer to the first command: battery
    a5 a5headertypetypeseqsequencelenlengthcrc16checksum payloadencrypted
    The frame of one message on the encrypted channel.
  7. 7. Independent check

    We checked the protocol code against 13 test vectors with a separate Python implementation on the Mac.

  8. 8. Live data

    One command makes the band stream live stats: steps, calories, heart rate. We turn it off after 2 minutes to save the battery.

  9. 9. Getting along with Mi Fitness

    • There can't be two sessions on the same channel. When our app didn't let go of the channel, Mi Fitness's sync got stuck; we added releasing the channel.
    • When Mi Fitness acknowledges (ACKs) the files, the band deletes them.
  10. 10. Other commands

    Sending notifications, finding the band by making it vibrate, setting the clock.

  11. 11. History (activity files)

    • The band lists the files for today and for past days.
    • Each file has a 7-byte ID: time, time zone, version and kind (per-minute data, daily summary, sleep, sleep stages, manual readings).
    • Files arrive in chunks and end with a CRC32.
    • We never send the ACK, so nothing is deleted from the band.
  12. 12. File formats

    • We wrote the formats down first. Then we wrote the code from scratch: a Python reference parser, and a Swift parser that gives exactly the same output.
    • 147 tests pass, and it doesn't crash even on 37,000 corrupted files.
    147tests passing
    37,000corrupted files, no crash
  13. 13. The product

    • From this data Freeband works out its own Sleep, Recovery and Strain scores; each score shows its contributors and how confident it is.
    • Alongside it there is an optional API and web panel on Cloudflare.
  14. 14. Still open

    • A real sleep file hasn't been verified yet.
    • We don't know yet what the 2/110 message, which the band sends on its own, means.
    • Pairing a brand-new band without Mi Fitness isn't possible for now, because the key is created on Xiaomi's server.

Thanks

Details on GitHub