joshua0: (Default)
Joshua Wise ([personal profile] joshua0) wrote2018-08-04 11:07 am

Gear review: Wahoo ELEMNT BOLT (or: "my new bike computer violates the GPL")

There have been a lot of exciting changes for me in the past month -- maybe the most exciting being that I left my job, and so I've been spending the last month biking and hiking and generally getting outside. I've been meaning to write about those, and I have a post drafted about that, but this is not that post. Instead, this post is an unrepentently nerdy dissection of a new toy that I got, in which I unintentionally buy yet another Android device -- a Wahoo ELEMNT BOLT, which is a bike computer with GPS (and other data) logging support, phone and internet connectivity, and a whole bunch of exciting gadget and doodad features.

Update August 6th, 2018: I am in contact with Chip, at Wahoo Fitness, about the licensing issues -- and with promising news about hackability! I'll write a little more later when I get a chance, but I figured this update should be front and center....

A quick summary:

Time for a new bike computer.

a Garmin Edge 510 hung at 'Saving ride...'

My old mostly-beloved Garmin Edge 510 had been getting more and more suspect over time. I've seen it take extremely large amounts of time at "Saving ride..." at the end, and occasionally, it seemed to hang there until I'd power it off. When I rebooted it, the ride would be there, but there would be a very suspicious turd in the Debugging/ERR_RPT_Log.csv file. This happened once every few months, until I went on a ride up around Tahoe, when I powered the device off at a rest stop, powered it back up, and ... the ride-in-progress was gone! This was frustrating and clearly unacceptable. When I got home, I managed to salvage a chunk of the missing data by digging through the filesystem, but not all of it -- and, anyway, I didn't quite trust it afterwards. I took it with me for a hike in the Sierras, too, and it *also* lost a chunk of my hike then. It was clearly time for a new device.

The options in front of me appeared to be threefold: the Garmin Edge 520 Plus (a known quantity; Garmin's crappy firmware, but at least a well known crappy); the Wahoo ELEMNT BOLT (seems well-reviewed, including by DC Rainmaker and by a randonneur friend); or waiting for the Hammerhead Karoo (an exciting upstart with a full-color display and built-in mapping, but with the unfortunate property that it runs Android ... and what kind of idiot would trust an Android system for something that you care about reliability and real-time performance?). Feeling kind of sick of the Garmin ecosystem, I decided to give the ELEMNT BOLT a whirl. I liked the idea of some of the features, like an LED bar with heart rate indication, and the ability to use Bluetooth LE sensors, not just ANT+ sensors. So, I dutifully went to REI and picked one up.


What's on this thing, anyway?

an ELEMNT BOLT on the bars

One interesting property about the ELEMNT devices is that they can sync not just over Bluetooth, but over WiFi. And, beyond that, they seem to do quite a lot over WiFi -- for instance, they update their maps over WiFi, and they also take firmware updates over WiFi! I thought this to be somewhat peculiar, but it would be nice to not have to mess with Bluetooth connectivity on my phone all the time. Before I bought one, I was vaguely interested to see what the firmware for it looked like; after all, Garmin's firmware update process is simply "grab a .gcd file, slam it in the GARMIN folder on your device, reboot, and let it walk away", but to my chagrin, the firmware for the ELEMNT or ELEMNT BOLT simply didn't appear anywhere on the web, and nobody had torn one down to see what was inside of it. Somewhat distrustful of this brave new Internet of Things world, then, I decided that it might be best to see what, exactly, this little designed-in-Atlanta toy was doing on the network before I let it roam free -- and, perhaps, if I was lucky, I'd get to find out what was running on it.


Sidebar: setting up mitmproxy.

an ELEMNT BOLT config screen with Emarhavil MITM as an option

You're not missing anything if you skip this section. I've never used mitmproxy before this, though, so if you want a quickstart, here's how I set it up.

I figured the best way to intercept the device's network traffic would be to put it on its own network segment, and then route that network segment through a 'known good' machine ... like the little server I have sitting next to the speakers, nyus. I've heard of tools for doing this before, like mitmproxy, but I've never used them before. Here's a quick rundown of how I did that.

I first connected an Airport Express to the same segment that I had nyus on. I gave it its own SSID -- "Emarhavil MITM", naturally -- and set it to be its own NAT device. Now the wireless segment on the Airport Express was a different network segment than the outside world segment. I subsequently assigned the Airport its own static IP, and set the Airport's default gateway to be nyus's IP; nyus already had an internal-facing IP:

joshua@nyus:~$ sudo ifconfig br0:1
br0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.1.10.2  netmask 255.255.255.0  broadcast 10.1.10.255
        ether 50:e5:49:50:d8:06  txqueuelen 1000  (Ethernet)

I then set up mitmproxy on nyus, first setting up forwarding to the port that I'd use -- 13337:

joshua@nyus:~$ sudo sysctl -w net.ipv4.ip_forward=1
joshua@nyus:~$ sudo sysctl -w net.ipv6.conf.all.forwarding=1
joshua@nyus:~$ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
joshua@nyus:~$ sudo iptables -t nat -A PREROUTING -i br0 -p tcp '!' -d \
  nyus.joshuawise.com --dport 443 -j REDIRECT --to-port 13337
joshua@nyus:~$ sudo iptables -t nat -A PREROUTING -i br0 -p tcp '!' -d \
  nyus.joshuawise.com --dport 80 -j REDIRECT --to-port 13337
joshua@nyus:~$ sudo ip6tables -t nat -A PREROUTING -i br0 -p tcp \
  --dport 80 -j REDIRECT --to-port 13337
joshua@nyus:~$ sudo ip6tables -t nat -A PREROUTING -i br0 -p tcp \
  --dport 443 -j REDIRECT --to-port 13337

And, finally, launch the web interface to mitmproxy:

joshua@nyus:~$ mitmweb --mode transparent --showhost -p 13337 --set \
  web_open_browser=false --set web_iface=0.0.0.0 --set web_port=13338

From there, all I have to do is connect the ELEMNT to the new MITM WiFi network. I tested it first by connecting my laptop to it; my laptop subsequently got very angry about SSL certificates, and then non-SSL requests showed up in the mitmweb console. This seemed good enough to me, so I let the ELEMNT connect.


You're talking to what?

mitmproxy showing some requests

The ELEMNT had a handful of interesting network traffic when I connected it to the network. It did seem to check certificates for some endpoints -- it tried to connect to parse.wahooligan.com and www.strava.com over HTTPS, and bail out when certificate validation failed for those. It did hit some interesting endpoints, including a bunch of files like https://lepodownload.mediatek.com/EPO_GR_3_1.DAT?key=[...], which I ultimately surmised to be assisted-GPS data ... but this is a solid first indication that the device was powered by commodity hardware. Subsequently, it hit an even more interesting endpoint, though... http://connectivitycheck.android.com/generate_204. A very clever trick, then, using Google's connectivity checks to see if an Internet connection is alive!

I asked it to 'check for updates'. It made a few requests, and informed me that an update was in fact available -- I was running firmware version 'WB09-1337', and the latest firmware was 'WB09-1980'. The first place it went looking for a new firmware was http://bolt.wahoofitness.com/fota/download/checkversion.php (more on that later), which, curiously, responded "Your version is the latest version"; later, it hit http://bolt.wahoofitness.com/boltapp/version.json-bolt, with a very suspicious User-Agent of Dalvik/2.1.0 (Linux; U; Android 5.1; ELEMNT-BOLT Build/LMY47I). The response solidified my lurking fear: that JSON file contained a link to http://bolt.wahoofitness.com/boltapp/1980/BoltApp.apk!

All of the evidence became clear. I had been hoodwinked -- I had bought an Android-powered bike GPS after all! The Wahoo ELEMNT and Wahoo ELEMNT BOLT run Android inside, on a MediaTek SoC. And not just any Android, but a truly archaic version. Huh.


How does it work?

an ELEMNT BOLT in the mountains hung at Installing...

I spent some time that evening messing with what I had found. Was it plausible to get my own code running on it? And what did they have running on it, in the end? I connected it to my computer to see if it'd talk to me on that front.

The device exposes both an ADB and an MTP interface, as you'd expect an Android device to do. The MTP filesystem has an interesting system_update_elemnt directory that contains a staging copy of the "BoltApp" that it downloaded from the boltapp endpoint; it also has a maps directory, which contains some form of map tiles in a format that I haven't spent any time trying to understand. Otherwise, there is nothign terribly exciting in the MTP interface. More interesting, in theory, is the ADB interface; in practice, it won't let you talk to it:

joshua@bruges:~/elemnt$ adb devices
List of devices attached
RG6HCMQKM7DIIBUS        unauthorized

The obvious question, while we're exploring USB, is whether it will speak to you over Fastboot. As it turns out, the answer to that question is "yes"! If you power the device on while holding the zoom-out button (bottom right side button), then it presents a Fastboot interface. Sadly, the bootloader is locked, and anyway, I don't have a boot image to try modifying and booting on it. One potentially even more interesting avenue for exploration is that as part of the device's boot process, even before it launches Fastboot, it spends about a second presenting an "MT65xx Preloader" interface on USB, which is apparently a known quantity; depending on how sketchy you are willing to go in terms of running software on your computer, there exists a Smart Phone Flash Tool that knows how to talk to the preloader. I haven't investigated further than that, yet; such a tool seems like it requires quite a lot of caution.

Another avenue of exploration is to try to figure out how the update mechanism works -- either to get a system image, or to see if it could be convinced to flash something new (ideally, with a plan to restore the device if I break it!). One thing I discovered is that the version number comes in two parts. The first part -- "WB09" -- is for the base Android ROM version. It's had a few updates on the ELEMNT, but that's never been updated on ELEMNT BOLT. The "WB09" part is controlled by the bolt.wahoofitness.com/fota endpoints, from which getting an image from is somewhat difficult. I noticed the device hitting two endpoints in there: login.php, and checkversion.php. There was a weird combination of things happening; login took a sn parameter, and returned a rand parameter (presumably, a nonce?) and a PHP session ID, though the rand response was actually the same for multiple requests in the same second. To call up checkversion, you needed to pass a token. I was feeling somewhat stumped, and grepping through a decompilation of the BoltApp didn't seem to have any reference to these endpoints. In the mean time, [personal profile] jgrafton, also with one of these devices on his bike, spent some time at a console while I glumly stared at mitmweb and permuted parameters, until there was an "Aha!". It turns out that the token is dead simple: it's just MD5(strcat(sn, rand)). It is not clear why they bothered at all, or why these two schemes exist -- one JSON blob, and one complicated token authentication system. Was it the B-team that came up with the base ROM update mechanism, or something?

The answer to that question, of course, was "yes, but not the way that you expect"; it turns out that it wasn't Wahoo's engineers at all that were responsible for the firmware update service, but instead, it belonged to MediaTek. Once we found a few endpoints, we did some Googling, and found some MediaTek documentation describing how some of it works, and we were off! It turns out that checkversion takes a version parameter; if you didn't have a version that it knew about, it would respond telling you that it didn't know how to update you, but version numbers are predictable. So, I iterated over WB01 through WB09, and lo and behold, found some updates; they came in the form of update.zips that one would expect to feed to an Android recovery, but unfortunately, they were all delta images. I still haven't found a full update.zip image yet, unfortunately -- so I still don't have a full boot.img or /system image. Oh well.

I also spent some time with mitmproxy to see whether I could convince the device to take a new BoltApp version. I had to be quite careful with this, because as far as I know, I don't have a way to recover from a bad BoltApp, especially without ADB accessible. I tried one simple way to force it to download a new BoltApp -- I modified the version.json-bolt's version number field, and left the url field the same. Sure enough, it happily downloaded the new APK! I powered it off and was done for the night, and drove myself up to the mountains for some time outside; when I powered it back on and went to record a hike, it spent the next six hours in my pocket instead saying "installing...". This was no good. Apparently it repeatedly saw the new version number in an upgrade staging space, attempted to install the app, restarted with its version number being old, rinsed, and repeated. Luckily for me, this was recoverable. As I noted before, the staging area lives in a region that's accessible over MTP. So, I fired up Android File Transfer on my Mac, deleted the staging area, and rebooted the device -- and lo and behold, it came back to life! What a relief.

This is about as far as I got with experimenting with the device itself. I suspect that it's possible to do more, safely -- but I haven't had time yet, and, as I'll discuss below, I'm not sure that I really want to, either...


But wait, since it's running Android, ...?

my Pixel 2 running the ELEMNT BOLT software

Once I figured out that my ELEMNT BOLT runs Android, there were really two obvious questions in my mind. The first one was perhaps the most experimentally motivated: if it's an ARM-powered Android device, what happens if I run this "BoltApp" thing on my phone? (Spoiler alert: don't do this.) I adb installed the software that runs my bike computer on my phone, and, to my chagrin, it happily fired right up. I realized that I hadn't granted it location permissions, so I went in to app settings on my phone, and gave it location and storage access. And, sure enough, it gave a location and speed reading! The buttons did not seem to do anything, but by investigation of the decompiled source, I came up with a script that would synthesize button presses over ADB.

I noticed other interesting effects from the app. For instance, it took control over my backlight, turning it to full blast when I synthesized a button press, and then turning it back down as low as it goes five seconds later. It turned off the WiFi on my phone, unless it was trying to do something with it. And, it seemed to rename my phone's Bluetooth name! Cute. To keep it from doing anything else 'exciting' while I wasn't looking, I uninstalled it. Two days later, I went to leave my house to teach a yoga class, and found... that all of the music on my phone was gone! And, on top of that, the Downloads folder, and all of my photos, were deleted, too. When I got home, I hit 'page down' in the text editor with the decompilation that I had open, and found... this:

for (String folder : new String[]{"Alarms", "Android", "DCIM", "Download", "Movies",
       "Music", "Notifications", "Pictures", "Podcasts", "Ringtones", "Bolt"}) {
    File folder2 = FileHelper.getExternalStorageFolder(folder, false);
    if (folder2 != null && folder2.isDirectory()) {
        int deleteFolder = FileHelper.deleteFolder(folder2);
        L.i("setupSD", folder, Integer.valueOf(deleteFolder), "items deleted");
    }
}

D'oh. I'm not sure what I expected, but I sure got it. I definitely got what was coming to me, anyway. The conclusion is: the ELEMNT BOLT on-device app will run on your phone, but you shouldn't do it.


The license question.

The other question was one that I was not thrilled to be asking. What was the license story with this thing? I couldn't find any offers for GPL source code, but beyond that, as far as I can tell, the device managed to violate the Apache2 terms, too -- offering attribution for none of the code that was running on it. This, to me, went to Extremely Not Cool territory. In the decompilation for the BoltApp package, I found a bunch of other stuff that was not written by Wahoo. For instance, it uses Mapsforge, an open-source renderer for OpenStreetMap data, which is licensed under the LGPLv3; of course, the hard work of the Mapsforge team is entirely without credit. For that matter, none of the contributors to Android itself are credited. It also uses Twitter4J, also Apache2-licensed, and also entirely without credit.

For better or for worse, it is somewhat standard to violate the GPL in distributing kernel source with Android. It's annoying, and of course, disrespectful of the authors. But not even acknowledging the contributions of people who built core components of your product (the maps renderer; the entire operating system!)? That's properly rude. These libraries really accelerated your development time; stealing them without so much is a word is quite unreasonable indeed. Shame on you, Wahoo.

I sent a support request to Wahoo asking for source for the applicable parts. They said that they'd have to forward the request on to their development team; reasonable enough! A day or two later, I got mail back from their support team saying that I'd have to send mail to one Chip Hawkins. I did, and then looked up who he was... he's the CEO of Wahoo Fitness, apparently.

As of a week later, I don't have any mail back from him. I didn't get mail back from him for a while; it looks like my mail got caught in a spam filter, or otherwise my mailer was fighting with theirs. They seem to be aware of the license issues, and are interested in solving this the best they can. I hope to have more updates in the near future, but they do seem like they'd be interested in opening the device up for a little more hackability, too. I'll write more later tonight.


Conclusion.

an ELEMNT BOLT lying through its teeth about how much elevation I had picked up

The Wahoo ELEMNT BOLT is a somewhat surprising device. What looks like a simple black and white bike GPS is actually a full Android machine, powered by a MediaTek SoC that's usually used in Android Wear devices. It takes updates over HTTP, and although it's feature-rich, it manages to violate not just the GPL, but the Apache2 license too. All that said, it is a cool device.

Oh, right. And I'm presumably supposed to write a gear review, too. Uh, I might keep it as a hackers' toy. But I think I'm probably just going to get an Edge 520 Plus. As far as I can tell, the ELEMNT BOLT does everything the Garmin does, but about 10% worse. The display doesn't really work well with polarized sunglasses; it gets summary statistics dramatically wrong on hikes (claiming that I ascended 231 vertical feet after having summited Mt. Emerson); the boot time is fairly slow; and although the rendering of the maps on screen is quite good, the routing engine is very excited about telling me the cues that I just passed, but not my upcoming turns. It works as a bike computer, and people say it's pretty reliable, and that latter bit is maybe better than Garmin have a reputation for. I just wish it worked better.


Oh, by the way: if you don't have a Dreamwidth account, but you want to get notified so you can read more things like this when I write them, I also have an e-mail list. I promise I'll only send mail for things that I write that go here on this blog. So, you know, if that's your thing, there you go.


Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting