Fitbit Flex: switching between encrypted and unencrypted mode

  • From: "dsx@xxxxxxxxxx" <dsx@xxxxxxxxxx>
  • To: galileo@xxxxxxxxxxxxx
  • Date: Thu, 4 May 2017 19:49:44 +0200 (CEST)

I recently had some time to play again with my Fitbit Flex and my Java version 
of Galileo (not ready yet for publication).
After having not used my Fitbit Flex for several month I recently synchronized 
it using my iPhone and the Fitbit supplied App which works over native iPhone 
BLE protocol.  Not surprisingly, I've encountered that after this sync my 
Fitbit Flex switched to encrypted megadump format (it was in unencryted 
megadump format before).
So, when I do a megadump now using my Java Galileo tool, I get the following 
megadump (encrypted):
Fitbit Flex Encrypted MegaDump
------------------------------
28 02 00 00 01 00 05 00 00 00 09 57 4c 30 11 07 3b fc 98 dc
bc 84 f6 e3 24 1e 42 9c 6c d5 51 74 d3 d2 65 42 a9 51 07 a6
15 e0 1b 07 bf bf 9f e8 4b 81 f1 e7 23 49 40 10 87 99 80 85
32 aa 47 ab 9e c7 31 e6 b7 28 3a 09 01 0d f7 89 61 6d e2 99
f3 dd be 55 12 46 cb 26 0e 50 3d 17 e9 47 d5 4a 1b e2 a4 4d
45 5d 3b 97 f2 df 79 bc fb 92 be e8 52 b4 92 63 38 26 7b 9b
68 fc ce 98 5d c8 b8 b8 e4 10 f0 0b e2 f7 76 a9 8d 2c 53 78
df 1d 9b 36 dc 61 d5 6b 84 42 c4 e3 3e 24 25 00 fc 8a 5a fb
83 bd ab bf cf d7 be a1 85 84 5b 90 64 68 19 51 35 e7 13 ff
1e 42 70 ad 6e 45 dd 08 a6 0e 47 14 30 84 b1 50 97 06 c5 bf
a9 47 e1 d3 bb 5e 67 72 cb 27 e1 85 a4 a0 45 bf b0 68 03 a7
92 61 d8 b5 1c 55 3b 63 1b 81 47 ea 5a 5f ab 96 75 af 49 28
b3 93 7e 67 bd 20 0e c7 08 af 07 30 a3 35 71 15 df 00 00
From all the infromation we've gathered so far, we strongly assume that the 5th 
byte in the above megadump has something to do if the megadump if encrypted or 
not. If this byte is 01 it is encryted, and if 00 it is unencrypted.
In an earlier post I've already indicated that I have strong believes that the 
Fitbit tracker can be switched back to send megadump in unencrypted format, if 
we sent it an appropriate server response in unencrypted format, that means 
with the 5th byte set to 00 and the payload also to be sent in unecnrytped 
format. This would clear the encryption bit in the Tracker.
My Java tool allows me to generate such a server response out of a parameter 
text file containing all parameter such as Personal Walking & Running Stride 
Lenght, BMR, Day Goal, Greeting Text (although not displayed on Fitbit Flex), 
and Options such as metric distance, left hand, etc. as well as alarm settings. 
Once I send such a server response in unencrypted format to my Fitbit Flex 
tracker, the next megadump I receive is UNENCRYPTED:
Fitbit Flex Unencrypted MegaDump
--------------------------------
28 02 00 00 00 00 00 00 00 00 09 57 4c 30 11 07 07 40 07 40
00 00 00 00 00 00 00 00 00 00 14 14 c3 0f 14 3c 00 00 00 00
d2 02 89 03 d5 23 52 09 1c 17 00 00 00 00 00 00 00 ff 48 00
20 20 20 20 20 20 20 20 20 20 4d 4f 52 47 45 4e 20 20 20 20
53 43 48 4e 45 4c 4c 20 20 20 48 41 4c 4c 4f 20 20 20 20 20
34 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 c0 db dc dd 71 d8 03 57 08 03 05 03 57 00 72 d8
00 00 00 72 d8 03 57 0b 00 05 00 00 00 00 00 00 00 00 72 d8
03 57 0b 01 05 00 00 00 bc 03 00 00 00 72 d8 03 57 0b 02 05
00 00 00 00 00 00 00 00 72 d8 03 57 0b 04 05 00 00 00 00 00
00 00 00 bc d9 03 57 13 05 05 00 00 00 c3 0f 00 00 00 bc d9
03 57 00 00 03 c3 0f 00 00 00 00 00 00 c0 c0 db dc dd c0 c0
db dd dc dd c0 c0 db dc dd 14 d9 03 57 28 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 ec d9 03 57 46 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 c0 00 00 00 00 c0 db dc dd c0 92
19 00 00 00 00 00 00 11 01 00
I repeated above steps (synced using iPhone Fitbit App -> get encrypted 
megadump afterwards, then followed by manual unecrypted server response -> get 
unecrypted megadump afterwards).
So this confirms - at least to me - that the Fitbit Flex Tracker can be 
switched to send megadump in unencrypted format by sending it an unencrypted 
server response (this may also work for any Fitbit Flex Tracker), as my Tracker 
uses FW 2.5. However, as soon as the tracker receives a new server response in 
encrytped format, it will switch to encrypted mode accordingly. 
The tricky part here was to simulate an unencrypted server response which the 
tracker will accept.
Here are some guidance how to accomplish this:
 * I used the native Fitbit supplied HW BL dongle for my Java Galileo tool to 
send the simulated server response
   (May'be someone can modify the galileo tool to also be able to send 
simulated server response)
 * Compile a simulated server response according to the below server response 
format description
Fitbit Flex Server Response Format (unencrypted):
-------------------------------------------------
AA BB 00 00 EE 00 DD 00 00 00 00 00 00 00 WW WW RR RR MM MM
CC DD F1 F2 00 00 00 00 00 00 00 FF F3 00 GG GG GG GG GG GG
GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG
GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG 00 00 00 00 AL
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 TT TT
TT TT 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 TZ AS 00 00 00 SS SS SS SS ZZ ZZ ZZ DF VB VB VB VB
VB VB VB VB VB VB VB VB VB VB VB AI LS LS LS LS S5 T5 T5 T5
T5 S2 T2 T2 T2 T2 S1 T1 T1 T1 T1 S4 T4 T4 T4 T4 01 00 00 00
DG GV GV GV 80 00 00 00 00 0A FF F0 3F 03 F0 3F 03 F0 38 1C (80 .. .. 1C 
unknown always the same)
00 00 00 00 TR TR TR TR TR TR TR TR TR TR TR
Legend:
AA: FitBit Model: 26 (ONE), 28 (Flex), 2D (Charge), 2E (ChargeHR), F4 (ZIP), ...
BB: Tracker Type: 02 (TRACKER), ?? (SCALE)
EE: Encryption Mode: 00 = unencrypted;  01 = encrypted
DD: unknown: in unencrypted mode = 01, in encrypted mode = 05
WW: WalkingStrideLenght in mm:    D2 02 -> 0x02d2 (722mm)
RR: RunningStrideLenght in mm:     03 93 -> 0x0389 (905mm)
MM: BasalMetabolicRate:           2B 93 -> 0x932B (1115.5)
CC: 52  always same for FitBit Flex, ONE, Charge
DD: 09  always same for FitBit Flex, ONE, Charge
F1, F2, F3 = Feature Flags 
F1:                             f3:
bit0 = !isMetricDistance                bit7 = isChatterEnabled
                                bit6 = isFlowerEnabled
F2:                             bit4 = isFloorEnabled
bit5 = isGreetingEnabled                bit3 = isClockEnabled
bit3 = isUsing24HourClock       bit2 = isCaloriesEnabled
bit2 = isRightHand              bit1 = isDistanceEnabled
GG:  Greeting Text in ASCII format (not displayed on fitbit flex)
AL:  50  no alarms defined (bit 0 set to 0)
AL:  51  alarms defined (bit 0 set to 1)
TT:  local time UTC shortened to seconds (4 bytes in LSB format)
TZ:  Time Zone (usually 00)
     If non-zero a special time zone record is inserted here.
     (details available on request)
AS: Number of Alarms
    Note; if this is 00 (no alarams) record continues with LS
 
Alarm Record is 24 Byte:
========================
SS: Seconds after midnight (0x5460 = 21'600 sec => 06:00) 
  
ZZ: 1C 02 01 (meaning unknown)
DF: DayFlags (Repeat, Mo, Di, Mi, ...)  
        Bit0: Monday, Bit1: Tuesday, Bit2: Wednesday, Bit3: Thursday
        Bit4: Friday, Bit5: Saturday,Bit6: Sunday
        Bit7: If bit 7 is set, then one or more day bits should be set
              If bit 7 is not set, then only one day bit should be set
VB: Vibrate Pattern (there's only one DEFAULT?)
    0D FC 0F C0 FC 0F C0 FF FF C0 FC 0F C0 FC 00
AI: 00 Alarm Index (starting with 0)
NN: 04 00 00 00 (Number of last synced blocks)
S5: 05  (last synced data block index 5)
T1: last synced time for data block 5 in UTC shortened to seconds (4 bytes LSB 
first) 
S2: 02 (see above)
T2: (see above)
S1: 01 (see above)
T1: (see above)
S4: 04 (see above)
T4: (see above)
Assume these are last synced dates for the different
data record sections. Data on tracker will be deleted up to
above defined last sync date
                 
Slot:  Last Date recorded in MegaDump before sync:
-----  -------------------------------------------
 S1    Steps Data
 S2    Floor Data?
 S4    Summary Data?
 S5    Header Data? (same as local time)
DG: Day Goal
Flex supports 3 Types: Steps 0x01, Distance 0x03, Calories 0x02?
On Web Site   5 Types: Steps, Distance, Calories, Activity, (Sleeptime)
GV: Goal Value
Default Steps     = 0x002710 (   10'000 Steps)
Default Distance  = 0x7AD552 (8'050'002 mm) -> 8.05km -> 5 miles 
Default Calories  = 0x000809 (    2'057 kCal)    
Default Activity  = 0x000708 (       30 min -> 1800 sec)
TR: Trailer (15 Bytes):
    4 Bytes: signature64Lo - lower 32-bits of 64-bit signature 
    4 Bytes: signature64Hi - upper 32-bits of 64-bit signature 
    2 Bytes: length24Lo    - lower 16-bits of 24-bit length
    1 Byte:  length24Hi    - upper  8-bits of 24-bit length
 
    signature64 = CRC16 (data[10] - data[end-11]; (diff 21 Bytes!)
    length24 = Total Bytes - 0x15 (21 Bytes)!
Here is a complete Example Server Response which contains 1 alarm record (you 
can use it to give it a try):
28 02 00 00 00 00 01 00 00 00 00 00 00 00 D2 02 89 03 D5 23
52 09 1C 1F 00 00 00 00 00 00 00 FF 48 00 20 20 20 20 20 20
20 20 20 20 42 45 52 45 49 54 3F 20 20 20 53 55 50 45 52 21
20 20 20 20 4A 55 43 48 48 55 21 20 20 20 34 00 00 00 00 21
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 82
06 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 01 00 00 00 60 54 00 00 1C 02 01 10 0D FC 0F C0
FC 0F C0 FF FF C0 FC 0F C0 FC 00 00 04 00 00 00 05 14 82 06
57 02 F9 81 06 57 01 EC 79 06 57 04 13 82 06 57 01 00 00 00
01 10 27 00 80 00 00 00 00 0A FF F0 3F 03 F0 3F 03 F0 38 1C
00 00 00 00 7D 8E 00 00 00 00 00 00 C2 00 00
Once we have compiled the raw data for the simulated server response, we need 
to encapsulate it into a server response message sent to the tracker. In 
addition, each server response block contains 20 bytes (see above Example) 
which must be padded with 00 to 32 bytes. This is done as follows (assumed air 
link with tracker is already established):
First we need to send an UPDATE TRACKER COMMAND (START BLOCK):
to tracker: c0 24 04 PL PL PL PL 02 19 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 0a
c0: command prefix
24: command code (UPDATE_TRACKER)
04: data type 04 = Server Response
PL: Payload 4 bytes LSB first (in this case: BF 00 00 00)
CC: CRC16 over payload 2 bytes LSB first (in this case: 02 19) + 1 byte 00 
padding
..: rest filled with padding 00 until last byte (32)
0a: last byte is byte count
Then we wait for the ACK from the tracker:
from tracker: c0 12 04 00 00 e4 00 c0 14 0c 01 02 00 76 fa a5 a7 b7 e5 00 00 00 
00 00 00 00 00 00 00 0f 00 05
c0: command prefix
12: command code (ACK_START_BLOCK)
04: 1st nubble 0 = sequence number (will increase with each block by 0x10)
    2nd nibble 4 = data type (SERVER RESPONSE)
Immediately after the initial START_BLOCK message, we need to tell the dongle 
to not use fast air link mode with the Fitbit Flex tracker:
to dongle: 04 19 01 00
04: byte count
19: command code (CLEAR_FEATURE_BITS -> turns fast air link mode off)
01: feature byte lsb bit 0 is fast air link mode; 1 = clear this bit
00: feature byte msb (no other feature bits to be cleared in this case)
The dongle does not respond to this command (status must be polled, not used 
here)
Then we can start to subsequently send the server response in junks of 20 bytes:
to Tracker: 28 02 00 00 00 00 01 00 00 00 00 00 00 00 d2 02 89 03 93 2b 00 00 
00 00 00 00 00 00 00 00 00 14
 
28-2b  (20 bytes of server response)
00..00 (padding with 00 up to byte 31)
14:    (last byte is byte count of payload = 20 bytes in hex)
from Tracker: c0 13 14 00 00 e4 00 c0 14 0c 01 02 00 76 fa a5 a7 b7 e5 00 00 00 
00 00 00 00 00 00 00 0f 00 05
c0: command prefix
13: command code (UPDATE_BLOCK_ACK)
14: sequence number (1) & data type (4)
Continue to send the server response in junks of 20 bytes
...
until less than 20 bytes:
to Tracker: 29 5f 00 00 00 00 00 00 aa 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 0b
29-aa 00 00 (last junk of server response (11 bytes)
00..00: (padding with 00 up to byte 31)
0b:     (last byte is byte count)
from Tracker: c0 13 a4 00 00 e4 00 c0 14 0c 01 02 00 76 fa a5 a7 b7 e5 00 00 00 
00 00 00 00 00 00 00 0f 00 05
c0: command prefix
13: command code (UPDATE_BLOCK_ACK)
a4: sequence number (a) & data type (4)
Then send a ACK_COMMIT to the tracker (the tracker will then verify the data 
and write it to its memory - this may take time)
to Tracker: c0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 02
c0: command prefix
02: command code (ACK_COMMIT)
00..00: padded with 00 up to byte 31
02: last byte is byte count
On success the tracker will also respond with an ACK_COMMIT (this reponse may 
take longer as usual)
from Tracker: c0 02 00 ff e4 00 c0 13 a4 00 00 e4 00 c0 14 0c 01 02 00 76 fa a5 
a7 b7 e5 00 00 00 00 00 00 02
c0: command prefix
02: command code (ACK_COMMIT)
..: rest is padding bytes up to byte 31 and not relevant
02: last byte is byte count
This completes the server response. 
Compiling a simluated server reponse in this way would allow us to be 
independent of the Fitbit WebSite, and allow us to do local synchronization, as 
well as to analyse our own private data by ourselfes (I can provide an almost 
complete decoded unencrypted megadump format description). 
Would be eager to know if some other Fitbit Flex owners are going to try above 
procedure to switch an encryted Fitbit Flex back to unencrypted mode.
If someone intends to integrate this manual server response feature into 
Benallard's Galileo Phyton tool, I can provide my Java code on request to be 
converted to Phyton. Unfortunately, I do not have enough time to create my own 
bitbucket project for this.
Regards, Dany
   

Other related posts:

  • » Fitbit Flex: switching between encrypted and unencrypted mode - dsx@xxxxxxxxxx