Browse Source

Sense API post.

dev
Chris Smith 8 years ago
parent
commit
e35e035d54
2 changed files with 233 additions and 0 deletions
  1. 233
    0
      _posts/2016-04-10-sense-api.md
  2. BIN
      res/images/sense/sense.jpg

+ 233
- 0
_posts/2016-04-10-sense-api.md View File

@@ -0,0 +1,233 @@
1
+---
2
+layout: post
3
+title: Reverse engineering the Sense API
4
+strapline: Who needs API docs when you can do a MITM attack?
5
+---
6
+
7
+### Meet Sense
8
+
9
+<div class="image right">
10
+ <img src="/res/images/sense/sense.jpg" alt="Sense">
11
+</div>
12
+
13
+<a href="https://hello.is/">Sense</a> is a little device that sits by your bedside and, in
14
+conjunction with a little 'pill' attached to your pillow, monitors your sleeping patterns and
15
+any environmental conditions that might hamper them. Android and iOS apps show you your sleep
16
+history, and offer suggestions for improvements.
17
+
18
+<!--more-->
19
+
20
+Sense was <a href="https://www.kickstarter.com/projects/hello/sense-know-more-sleep-better/description">Kickstarted</a>
21
+in August 2014, raising over 2.4 million US dollars, and shipped to backers in mid 2015. The
22
+campaign blurb included this snippet:
23
+
24
+> Building with Sense
25
+>
26
+> You'll always have access to your data via our API. Take it, play with it, graph it, do whatever
27
+> you want with it. It's yours. That's important to us.
28
+>
29
+> We enjoy tinkering with and building on-top of other products we like. Sense will let you have
30
+> that experience.
31
+>
32
+> We'd love to hear your thoughts on what you might want to build with Sense, and how you could
33
+> directly interact with the hardware, and the data it collects.
34
+
35
+Sounds great! But a year after shipping, there's no sign of an API, and some of us who enjoy
36
+tinkering are getting a bit restless...
37
+
38
+### Reverse engineering to the rescue
39
+
40
+Data from the Sense hardware is transmitted off onto the Internet somewhere, and then pulled
41
+down by the mobile apps. If we can snoop that traffic, we can probably figure out how to grab
42
+the data from the Sense.
43
+
44
+So the first step is to capture the raw network traffic while the Sense and the app are in use. I
45
+crafted an extremely over-the-top setup that involves my phone and the Sense being on a new,
46
+separate wireless network that gateways through a Linux server that can look in on all the
47
+traffic (because you never know when a dedicated capturing network will come in handy, right?...)
48
+
49
+Running `tshark` while the app and device are active showed they were making HTTPS requests an
50
+Amazon Elastic Cloud instance. That's not really much use, as we can't see the content of the
51
+requests. The next step is to move up the network stack and target the HTTPS traffic specifically.
52
+Step in, <a href="https://mitmproxy.org/">`mitmproxy`</a>. This automates man-in-the-middle attacks
53
+on HTTPS requests. When it receives such a request, it cooks up its own certificate and sends that
54
+to the client, and then sits in between while the client and the server communicate.
55
+
56
+The certificates generated by `mitmproxy` aren't issued by a trusted source, so (well behaved)
57
+browsers and apps will throw up lots of warnings and refuse to communicate with it for the most
58
+part. This is the mechanism that stops anyone impersonating your banking website when you access it
59
+over HTTPS, so is generally a very good thing. In this case, both the Sense itself and the Android
60
+app refused to talk with the blatant attacker.
61
+
62
+We control the Android phone, though, so can just tell it that we trust the dodgy certificate
63
+issuer. This involves grabbing the certificate generated by mitmproxy, and adding it to Android's
64
+key store. The process is <a href="http://docs.mitmproxy.org/en/stable/certinstall.html#installing-the-mitmproxy-ca-certificate-manually">documented nicely in the mitmproxy docs</a>
65
+and is pretty straightforward. With Android trusting our certificate authority, the Sense app
66
+starts talking through the proxy and we can look at the plain HTTP requests.
67
+
68
+### The API
69
+
70
+Logging in to the Android app and navigating through the various screens shows that the API is
71
+actually pretty nice. It's RESTful, and uses an OAuth bearer token for authorisation, as you can
72
+see in the `mitmproxy` output of the authentication and first few requests:
73
+
74
+{% highlight text %}
75
+POST https://api.hello.is/v1/oauth2/token
76
+    ← 200 application/json 151B 1.17s
77
+GET https://api.hello.is/v2/account/preferences
78
+    ← 200 application/json 124B 109ms
79
+GET https://api.hello.is/v1/account
80
+    ← 200 application/json 181B 388ms
81
+GET https://api.hello.is/v2/devices
82
+    ← 200 application/json 235B 126ms
83
+POST https://api.hello.is/v1/app/checkin
84
+    ← 200 application/json 106B 119ms
85
+GET https://api.hello.is/v2/insights
86
+    ← 200 application/json 1.4kB 157ms
87
+POST https://api.hello.is/v1/notifications/registration
88
+    ← 204 [no content] 364ms
89
+GET https://api.hello.is/v2/timeline/2016-04-09
90
+    ← 200 application/json 160B 550ms
91
+GET https://api.hello.is/v1/questions?date=2016-04-10
92
+    ← 200 application/json 284B 132ms
93
+PATCH https://api.hello.is/v1/app/stats
94
+    ← 202 [no content] 126ms
95
+GET https://api.hello.is/v1/app/stats/unread
96
+    ← 200 application/json 71B 171ms
97
+{% endhighlight %}
98
+
99
+#### Authorisation
100
+
101
+To get a bearer token, you send a POST request to `/v1/oauth2/token`, supplying the username and
102
+password, and a client ID and secret. Presumably if the API is ever opened up you'll be able to
103
+register clients and get your own ID and secret, but for now reusing the ones from the Android
104
+app works fine. [I've prettified and linewrapped the content to make it a bit easier to read.]
105
+
106
+{% highlight http %}
107
+POST /v1/oauth2/token HTTP/1.1
108
+Host: api.hello.is
109
+Accept: */*
110
+Content-Length: 123
111
+Content-Type: application/x-www-form-urlencoded
112
+
113
+grant_type=password
114
+ &client_id=8d3c1664-05ae-47e4-bcdb-477489590aa4
115
+ &client_secret=4f771f6f-5c10-4104-bbc6-3333f5b11bf9
116
+ &username=USERNAME
117
+ &password=PASSWORD
118
+{% endhighlight %}
119
+{% highlight http %}
120
+HTTP/1.1 200 OK
121
+Cache-Control: no-cache
122
+Content-Type: application/json
123
+Date: Sun, 10 Apr 2016 17:25:16 GMT
124
+transfer-encoding: chunked
125
+Connection: keep-alive
126
+
127
+{
128
+  "token_type":"Bearer",
129
+  "expires_in":31536000,
130
+  "account_id":"1234",
131
+  "access_token":"2.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
132
+  "refresh_token":"2.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
133
+}
134
+
135
+{% endhighlight %}
136
+
137
+#### Sleep timeline
138
+
139
+Getting the timeline for one night is just a straight up GET request. It gives a detailed log of
140
+events, including different 'depths' of sleep (I've cut out a whole bunch here for simplicity).
141
+It also shows metrics relating to the entire night, and their condition ('warning' or 'ideal').
142
+
143
+{% highlight http %}
144
+GET /v2/timeline/2016-04-08 HTTP/1.1
145
+Host: api.hello.is
146
+Accept: */*
147
+Authorization: Bearer 2.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
148
+{% endhighlight %}
149
+{% highlight http %}
150
+HTTP/1.1 200 OK
151
+Cache-Control: no-cache
152
+Content-Type: application/json
153
+Date: Sun, 10 Apr 2016 17:29:00 GMT
154
+transfer-encoding: chunked
155
+Connection: keep-alive
156
+
157
+{
158
+  "score":76,
159
+  "score_condition":"WARNING",
160
+  "message":"You were asleep for **8.2 hours**, and sleeping soundly for 3.2 hours.",
161
+  "date":"2016-04-08",
162
+  "events":[
163
+    {
164
+      "timestamp":1460168700000,
165
+      "timezone_offset":3600000,
166
+      "duration_millis":60000,
167
+      "message":"You went to bed.",
168
+      "sleep_depth":0,
169
+      "sleep_state":"AWAKE",
170
+      "event_type":"GOT_IN_BED",
171
+      "valid_actions":["ADJUST_TIME","VERIFY","INCORRECT"]
172
+    },{
173
+      "timestamp":1460169600000,
174
+      "timezone_offset":3600000,
175
+      "duration_millis":60000,
176
+      "message":"You fell asleep.",
177
+      "sleep_depth":100,
178
+      "sleep_state":"SOUND",
179
+      "event_type":"FELL_ASLEEP",
180
+      "valid_actions":["ADJUST_TIME","VERIFY","INCORRECT"]
181
+    },{
182
+      "timestamp":1460177040000,
183
+      "timezone_offset":3600000,
184
+      "duration_millis":60000,
185
+      "message":"You were moving around quite a bit.",
186
+      "sleep_depth":2,
187
+      "sleep_state":"AWAKE",
188
+      "event_type":"GENERIC_MOTION",
189
+      "valid_actions":["VERIFY","INCORRECT"]
190
+    },{
191
+      "timestamp":1460199000000,
192
+      "timezone_offset":3600000,
193
+      "duration_millis":60000,
194
+      "message":"Good morning.",
195
+      "sleep_depth":0,
196
+      "sleep_state":"AWAKE",
197
+      "event_type":"WOKE_UP",
198
+      "valid_actions":["ADJUST_TIME","VERIFY","INCORRECT"]
199
+    }
200
+  ],
201
+  "metrics":[
202
+    { "name":"total_sleep", "value":490, "unit":"MINUTES", "condition":"IDEAL" },
203
+    { "name":"sound_sleep", "value":190, "unit":"MINUTES", "condition":"IDEAL" },
204
+    { "name":"time_to_sleep", "value":15, "unit":"MINUTES", "condition":"IDEAL" },
205
+    { "name":"times_awake", "value":2, "unit":"QUANTITY", "condition":"IDEAL" },
206
+    { "name":"fell_asleep", "value":1460169600000, "unit":"TIMESTAMP", "condition":"IDEAL" },
207
+    { "name":"woke_up", "value":1460199000000, "unit":"TIMESTAMP", "condition":"IDEAL" },
208
+    { "name":"temperature", "value":null, "unit":"CONDITION", "condition":"IDEAL" },
209
+    { "name":"humidity", "value":null, "unit":"CONDITION", "condition":"IDEAL" },
210
+    { "name":"particulates", "value":null, "unit":"CONDITION", "condition":"WARNING" },
211
+    { "name":"light", "value":null, "unit":"CONDITION", "condition":"IDEAL" },
212
+    { "name":"sound", "value":null, "unit":"CONDITION", "condition":"IDEAL"}
213
+  ]
214
+}
215
+{% endhighlight %}
216
+
217
+#### Other interesting resources
218
+
219
+Pretty much everything displayed in the app is available as a REST resource. They clearly
220
+put some thought into API design, which makes it even more of a shame that they haven't made it
221
+public yet. Some of the other interesting resources are:
222
+
223
+* `/v1/alarms`: the alarms set on the Sense.
224
+* `/v1/room/current?temp_unit=c`: gives the latest environmental readouts from the Sense.
225
+* `/v1/room/all_sensors/hours?quantity=2&from_utc=<timestamp>`: sensor history.
226
+* `/v2/devices`: the paired devices, including battery status of pills.
227
+* `/v2/insights`: JSON representation of the cards displayed on the main screen (recommendations,
228
+  analysis, etc).
229
+* `/v2/trends/LAST_WEEK` (or `MONTH` or `3_MONTHS`): gives graph data for sleep scores, duration
230
+  and depths.
231
+
232
+
233
+Now the API is figured out, all that's left is to build something that uses it...

BIN
res/images/sense/sense.jpg View File


Loading…
Cancel
Save