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