Parsing FIDO Raw Messages

I’ve spent the last few weeks working on iOS app to use a Feitian BLE Multipass token. When I get to the final version of that I’ll do a separate posting but parsing the raw messages to and from the device turned out to be pretty challenging. Unfortunately, if you send something that doesn’t conform to the required format, rather than getting an error back, you get no response at all. This makes debugging [particularly at the start, when I really didn’t know what I was doing] tricky.

While the standards for both the FIDO BlueTooth spec and the raw message format are both well written, there are a few bits that aren’t immediately clear, and that I couldn’t find documented anywhere.

The Feitian device enforces a maximum fragment size of 20 bytes. Any arithmetic I use in the rest of this write-up will assume this, but that will vary from one device to another. Also, I will tend to use the actual number of hex characters transmitted. As the payloads are double byte, this will mean that a decimal value will be derived by converting a hex value to decimal, and then doubling it.

Sending An Enrolment Command

The first fragment you send looks like this:   

The FIDO command prefix has the value 830049. The 83 indicates a message, the 00 is a blank high data length, and 49 is the length in hex, which equates to 73 in decimal, 146 characters. This is derived from adding together the APDU command prefix [14 characters], the concatenated challenge and application parameters [128], and the a final 4 characters, all zeroes, which are the APDU Le values. 

The APDU command – the example I’m using is enrolment – has the value 00010000000040. As per section 3.1 of the raw format spec, that’s a preceding 00, then the enrolment command of 01, then a bunch of blank parameters, and ultimately the 40, which equates to the 64 bytes for the combined length of the challenge and application parameters.

For each of the subsequent chunks, you have a payload [in the Feitian’s case] of 19 double bytes, plus one more byte for the order. The last message includes the 4 bytes for the Le values.

Parsing the Response

The response is a bit trickier. First, the device may respond with a FIDO keep alive command, which is 82000101. So before I start concatenating the full response, I check that the individual chunk value doesn’t equal this. Just a quick detour into the implementation: the BlueTooth spec says that the values you get back from the token will be Big Endian double bytes. I couldn’t find a nice way of processing these using an NSUTF value, so I just convert them to text and parse out the angle brackets and spaces.

On to the processing. The first six characters I get are 830259. Per the spec, 83 is playing back the original command. 259 in hex means that the total length of the response will be 1202 characters. That’s going to vary: both the attestation certificate and the signature are variable length. For my implementation, I calculate a running total, so that I know when the final message has been sent.

So, there is no APDU header at all in the response. After the 6 character FIDO header, you get straight into the standard payload as per the spec. You have a legacy 05, then 130 characters for the public key, then a key handle length. For my device, the value is 60, which translates [96 in decimal x 2] to 192 characters. 

The next part took quite a bit of digging, which is figuring out the length of the attestation certificate. The certificate starts with the following: 30820168. While my read of DER encoding of ASN.1 [ugh!] is that the length specification can vary, for me, that 0168 is the length in hex, equating to 360 bytes. Add back in 4 bytes to include the header [30820168], that gives me an attestation cert length of 728 characters. 

What remains is 144 characters. The last 4 are 9000, which per the raw message spec is the SW_NO_ERROR value. 

And that’s it. 6 weeks of trial and error in about 700 words.

Using Sunset to adjust Philips Hue Sensor On/Off Times

I’ve been meaning to do this for ages, and it turned out to be a bit easier than I thought. The Python I’m using is a bit rough and ready – rather than the 8 separate API calls to PUT the date changes back [4 rules for each sensor], I could do this with one API call, based on the single data structure for all of the rules. It doesn’t really matter that much.

I haven’t done anything for sunrise processing, which fits our usage patterns with the sensors.

Here’s the script, which I’m calling from cron, once a day, which is a bit of overkill. I could probably do it weekly:

#!/usr/bin/env python3

import requests
import json
import time
from datetime import datetime, timedelta

fudgeFactorMins = -30
morningOffTime = "T08:00:00"

The fudgeFactorMins variable is an offset for the actual sunset: I eventually subtract this from the sunset date. The rule numbers are not going to be nailed on, but they do appear to be contiguous for each sensor, in groups of 4:

# Format: "T20:00:00/T08:00:00"

# 8am / 8pm: day-on
bathroomDayOnRuleURL = "http://yourIP/api/yourKey/rules/1"
# 8am / 8pm: day-dark-on
bathroomDayDarkOnRuleURL = "http://yourIP/api/yourKey/rules/2"
# 8pm / 8am: night-on
bathroomNightOnRuleURL = "http://yourIP/api/yourKey/rules/3"
# 8pm / 8am: night-dark-on
bathroomNightDarkOnRuleURL = "http://10.40.0.3/yourIP/api/yourKey/rules/4"

# 8am / 8pdm: day-on
livingroomDayOnRuleURL = "http://yourIP/api/yourKey/rules/9"
# 8am / 8pm: day-dark-on
livingroomDayDarkOnRuleURL = "http://yourIP/api/yourKey/rules/10"
# 8pm / 8am: night-on
livingroomNightOnRuleURL = "http://yourIP/api/yourKey/rules/11"
# 8pm / 8am: night-dark-on
livingroomNightDarkOnRuleURL = "http://yourIP/api/yourKey/rules/12"

This is a bit messy but…

allUrls = [bathroomDayOnRuleURL,bathroomDayDarkOnRuleURL,bathroomNightOnRuleURL,bathroomNightDarkOnRuleURL,li
vingroomDayOnRuleURL, livingroomDayDarkOnRuleURL, livingroomNightOnRuleURL, livingroomNightDarkOnRuleURL]

The sunsetAPIurl refers to a free API. I had a look at one implementation on StackOverflow which calculates locally. This endpoint is returning results within a few mins of the app on my phone, which is certainly good enough.

One slight nuisance is that I need to adjust for British SummerTime. What I’m doing for now is using Daylight Saving as the Python datetime library gives you it for free. It’s going to ‘wrong’ for the week or so when DST and BST are out of synch at the start and end of the summer, but I can live with that for now:

sunsetAPIurl = "https://api.sunrise-sunset.org/json?lat=yourLat8&lng=-yourLong"

def getSunset(sunsetAPIurl):
   r = requests.get(sunsetAPIurl)
   jsonData = r.json()
   # date format is 8:08:18 PM
   dateFromJson = datetime.strptime(jsonData["results"]["sunset"], '%I:%M:%S %p')
   if is_dst():
     adjustedSunset = dateFromJson + timedelta(hours=1)
     return adjustedSunset
   else:
     return dateFromJson

def is_dst( ):
    return bool(time.localtime( ).tm_isdst)

So another quick adjustment to reformat to the requirements of the Hue endpoint date handling, after processing the fudge factor:

def adjustSunsetToString(sunset, fudgeFactorMins):
  fudgeAdjusted = sunset + timedelta(minutes=fudgeFactorMins)
  return fudgeAdjusted.strftime('T%H:%M:%S')

def hitEndpoint(allUrls, dayOnString, nightOnString):
  for oneUrl in allUrls:
    r = requests.get(oneUrl)
    jsonData = r.json()
    currentDate = jsonData["conditions"][0]["value"]
    #print("currentDate: " + currentDate)
    ruleName = jsonData["name"]
    #print("rulename: " + ruleName)

The dates that I push back into the JSON need to conditionally flick around [earliest / latest or vice versa], which I do based on the substring search for ‘day’ on the rule name:

 
    if "day" in ruleName:
      jsonData["conditions"][0]["value"] = dayOnString
    else:
      jsonData["conditions"][0]["value"] = nightOnString
    newJsonPayload = {"conditions": jsonData["conditions"], "actions": jsonData["actions"]}
    r = requests.put(oneUrl, json = newJsonPayload)
    #print("response: " + r.text)


sunset = getSunset(sunsetAPIurl)
adjStrDate = adjustSunsetToString(sunset, fudgeFactorMins)

dayOnString = morningOffTime + "/" + adjStrDate
nightOnString = adjStrDate + "/" +  morningOffTime

hitEndpoint(allUrls, dayOnString, nightOnString)

 

Philips’ Hue Sensor Automation

I wrote about my first pass at the Philips’ Hue API to customise the motion sensor functionality back in December. At the time, I tested out my approach to force a third time slot [use of a nightlight setting for part of the the night] using a simple mobile app in Objective C. The approach isn’t that fancy: it’s basically 3 API PUT calls: two to rules, and then a third to a resourcelink all basically doing the same thing: inverting references to scenes [one for full brightness, and the other for the nightlight setting].

At the time I thought it would be handy to use a Raspberry Pi using cron jobs to toggle it off and on, and the long and short of it, that’s what I’ve gone with. It’s a simple Python script [using the Requests module, which is great, to make the API calls], and it’s working really well so far.

A couple of observations. First, I found an article on the Hue discussion forum – which I only found when I got a weird error and which, unfortunately I can’t find any more – that has what’s probably a more purist way of using the API to create extra slots. While it’s smarter than what I’m doing, unfortunately [and the author of the post does point this out] it has the effect of stopping the mobile app from working to set the sensor.

Secondly, my approach is pretty brittle. Between writing the app and implementing the Python script the state had changed in the bridge: there was a new scene [not one we added – at least intentionally] which was causing the API calls to fail.

Currently, my script is doing exactly what my mobile app did, which is creating the PUT payload for the API calls from files, and ‘hoping’ that they work. What the Philips’ mobile app does is to GET the various API resources and then PUT them back. I’ll do this when the current implementation breaks as it’s more trouble than it’s worth for now.

I was telling someone at work [a proper developer] about my results with proxying via ZAP, which I had to revisit to figure out why my original implementation had stopped working, and he couldn’t believe the fact that the app is making around 55 API calls to effect a change in the sensor setup. Based on the diffs I looked at, It seems to pretty blindly do the GETting and PUTting where no changes have been made.

I managed to completely corrupt the setup on the bridge by playing the ‘I wonder what will happen if I do this?’ game: I deleted the scene that was breaking my API calls. I ended up having to do a factory reset – about a five minute job for me, but worth bearing in mind if you are playing with this stuff and have a more sophisticated configuration.