Sunday, April 10, 2016

WeatherClock - Part 4

I need to describe the server-side part of this project in more detail since I've added to my scope. Here is a simple diagram of our entire system. In this post, I'm focused on the non-Arduino portion.

While I have no expectation that anyone else will use the service I've built, every call to the service results in a corresponding call to the Forecast.io API -- and my free account with them only allows 2000 calls a day. If more than 2000 calls happen, I stop getting a response from Forecast.IO, so my clock will have no data. The risk is small, both in it's likelihood of occurrence (nobody will really care about this service if they do find it) and in it's impact - my clock will stop working. While this isn't a big deal, I'd rather it not happen. So, how do I ensure that my public-facing Azure service doesn't get used/abused by anyone other than me?

Web services often use API keys to handle authentication -- you send a request with a valid key, you are authenticated. Forecast.IO does this, in fact. The downside of course is that if your API key is exposed, anyone can pretend to be you. I'm not concerned about this authentication mechanism b/w Azure and Forecast.IO -- that channel is relatively safe. I AM concerned about the channel between my Arduino client and my Azure service.

I could certainly use a static API key to authenticate my Arduino to my Azure service, but that's a bit riskier than a typical setup because I don't have the ability to create an encrypted channel. While Azure makes it free and easy to use SSL/TLS, my Arduino client is incapable of consuming it. So, our traffic will be in the clear over HTTP. How can I authenticate in the clear such that an eavesdropper can't make use of sniffed traffic? Here's what I came up with. Note this does NOTHING to stop a man-in-the-middle, it's just giving the observer of the traffic a harder job to make use of what is seen.

My Azure service exposes a single API: /api/Forecast/{latitude}/{longitude}/ . The header of the request must include two values: id and key. The value of id is essentially an "account identifier". Were I building a service that had multiple accounts/users, this would be the value to identify the account to which we want to associate the call. For me, this is a static, hard-coded value since I am the only account in use. The value of the key is the fun trick I'm using to get a semblance of authentication. The value of key is a calculated one-time-password, such as that used by Google Authenticator or any other 2-factor/OTP system. In fact, the OTP algorithm I'm using is fully compatible with Google Auth. So, here's how it works. My Azure service has a secret key associated with my account (again, this is hard-coded in my setup since I'm the only user), and the exact same key is also embedded in my Arduino. Assuming my service and my Arduino have their clocks in sync (to a reasonable degree), the two sides can calculate an identical 6-digit OTP that changes every 30 seconds. What does this do for us? It means that every 30 seconds, the header value that has to pass to my service to get a successful response will change. Neat - I have a non-static, unpredictable (from the outside) authentication mechanism! Obviously this is not bulletproof. I've done nothing to stop an eavesdropper from re-using a valid OTP within the same 30 second window for which it was calculated (tho' that wouldn't be too hard to stop). I'm sure there are plenty of other vulnerabilities as well. But I think this is a secure-enough solution for what I'm trying to accomplish. And given that I only have 2k of RAM to work with on the Arduino and a ton of other stuff to do in that memory space, I'm pleased with it.

The code for my server component is here: https://github.com/jimnelson2/WeatherClock-Server I put the source there for folks to see/use, but I used visualstudio.com and continue to use that for my development work.

Here's a quick walkthrough of the code. This is a standard .net WebAPI project, I've created only two unique classes - the ForecastController to handle the call/response with the Forecast.IO service, and the ApiKeyHandler to do the OTP/authentication work. The Forecast controller is wired in with the WebApiConfig, which also sets up the ApiKeyHandler. Small bits and pieces are scattered in to try reducing the size of the response back to the Arduino. By default, responses include lots of header data that just doesn't matter for my purposes. I could probably reduce a bit more, but this is good enough.

I use AppSettings to hold several handy values. First and foremost, the Forecast,io API key I use to authenticate my Azure service to the Forecast.io service. In development, this value is in the web.config. In production, this value is set via my Azure configuration so there's never a need to put my key into source control. In addition to the API key, I also use a setting for the OTP key (again, I'm the only user of this sytem - if it were multiuser, the OTP would be in a database along with my account id, etc.)  I use a few other values to help with development/debugging. For example, if I've set app settings for latitude and/or longitude, those values will override anything that comes in a request.

I've also made extensive use of application tracing commands. These are handy for when you need to crank up the logging level of your app. A simple toggle in your Azure panel will start tracing and you can pull the traces back either via the VisualStudio IDE, the web, or Powershell. Search the help docs on Azure for details.

As I've mentioned before, very little of this code is mine. I've documented in the code where I've sourced, primarily the Forecast.IO client and the OTP code.

About the response I send from the service back down to the client. I had initially designed things such that the response would be 60 RGB tuples - this would let me control server-side the exact color of any LED in the ring. e.g. 0,0,255,0,0,255...would set the first two LEDs of the ring to bright blue. After LOTS of frustrating testing I discovered what I should've known instantly - that's a lot of data for the Arduino to handle. So, I redesigned a bit. Instead of 60 RGB tuples (potentially 60 * 3 bytes, comma-separated), I send just 60 single characters. Each character is a hex value from 0 to f. That gives me 16 potential colors to specify, 0 is for an off/dark LED, while the remaining 15 are used to map into an array of 15 RGB tuples set as constants on the Arduino. This shrinks the network payload dramatically at the small expense of flexibility. Should I want to change my colors, I have to jump into the Arduino code. The smaller payload has dramatically improved Arduino stability, but I'll talk about that more in the next post when I go over the client.

Here's a look at the final product as a teaser - it shows a band of medium/heavy rain passing through in the next 15 minutes, followed by light rain wrapping up within the next 45 minutes.