Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
GHLocalApi Update

GHLocalApi Update

The Gist

Until recently, the Google Home app used to communicate with the device over port 8008 (HTTP) and did not require any authentication. Everything in the unofficial documentation worked as expected.

A few days (weeks) ago, Google pushed a new update to all GH devices and all endpoints (except /setup/eureka_info) started returning 403 (forbidden) errors. The app had switched over to port 8443 and HTTPS.

The Fix

Lots happened over at #39. Finally, the only changes required are:

  • Change port from 8008 to 8443
  • Change protocol from http to https
  • Add a new header (for all requests) cast-local-authorization-token

Note: Since this is https, the CA will likely not be trusted by your device. "Enable Insecure Requests" or "Allow Self Signed Certificates" when making requests. For example, pass the -k/--insecure flag with curl and verify=False with python's requests.

The Token

The token required for cast-local-authorization-token can be obtained by 2 methods. As of now, I'm not sure if this token expires or when it does or even how the app gets it in the first place.

/TODO: Add more info

Getting the token

2 ways: From app data directory on android or with Frida.

Both require root. First one recommended.

App Data Dir (Android)

This extracts the token from the app's data folder. The script finds tokens of all devices which might have this token. Only NodeJs is required, a browser friendly page coming soon.

Note: I ported the same code to a website so you don't have to download the script and NodeJs. The website finds all devices and tokens from the file and everything happens offline. Nothing from the file leaves the browser. https://rithvikvibhu.github.io/gh-web-proto-decode/

  • With a root file manager, pull this file: /data/data/com.google.android.apps.chromecast.app/files/home_graph*.proto
  • Run node decodeProtoFile.js <file> to extract tokens. (script attached)

With Frida (Android)

Frida injects and hooks onto running applications. The script logs all requests along with the needed header.

  • Install and set up Frida and ADB
  • Connect the phone to PC and copy Frida Server
  • Open the Google Home app on the phone
  • Use this script (thanks @TheKalin!)
  • Open GH settings in the app. The header with token will be printed.
const fs = require('fs');
const util = require('util');
const rawproto = require('rawproto');
const filename = process.argv[2] || 'home_graph.pb';
console.log(`[*] Reading proto binary... (${filename})`)
var buffer = fs.readFileSync(filename)
console.log(buffer)
console.log(`[*] Parsing file...`)
var data = rawproto.getData(buffer);
// detailedLog(data);
console.log('[*] Extracting tokens...\n')
data.forEach(val => {
if (val['2'] && Array.isArray(val['2'])) {
val['2'].forEach(val2 => {
if (val2['7'] && Array.isArray(val2['7'])) {
var device = null;
var token = null;
try {
var deviceObject = getObjByKey(val2['7'], '17')['17'][0];
device = (deviceObject ? (deviceObject['2'] || deviceObject['1']) + ', ' : '' ) + getObjByKey(val2['7'], '18')['18'][0]['2']
} catch (err) { console.log(err) }
try {
var tokenObject = getObjByKey(val2['7'], '28');
token = tokenObject ? tokenObject['28'] : null;
} catch (err) { console.log(err) }
if (device || token) {
console.log('Device:\t', device)
console.log('Token:\t', token)
console.log('-----')
}
}
})
}
})
console.log('\n[*] Done!\n')
function detailedLog(obj) {
console.log(util.inspect(obj, {showHidden: false, depth: null}))
}
function getObjByKey(arr, key) {
return arr.filter(v => v[key])[0]
}
@magicse

This comment has been minimized.

Copy link

@magicse magicse commented Jan 23, 2020

How it use?
Please give example of curl query with cast-local-authorization-token header

@magicse

This comment has been minimized.

Copy link

@magicse magicse commented Jan 23, 2020

Also name of the file is home_graph_(base64 string of Account email).proto

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Jan 24, 2020

@magicse All the endpoints in the docs at https://rithvikvibhu.github.io/GHLocalApi/ work. Just need 3 changes (under "The Fix" heading above), one of which is an extra request header called cast-local-authorization-token.
For example:

curl --include --insecure -H "cast-local-authorization-token: EXTRACTED_TOKEN" https://192.168.1.15:8443/setup/assistant/alarms

And thanks for the filename tip!

@magicse

This comment has been minimized.

Copy link

@magicse magicse commented Jan 24, 2020

Ah ok thanks. It's work

@jurgenweber

This comment has been minimized.

Copy link

@jurgenweber jurgenweber commented Jan 31, 2020

is there a way to do this without your phone being 'root'ed? :0

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 1, 2020

@jurgenweber No. Not yet, anyway. If you really need it, some people have set up a vm (like android emulators) and rooted them.

@jurgenweber

This comment has been minimized.

Copy link

@jurgenweber jurgenweber commented Feb 1, 2020

yeah, after making this comment I saw found that, its an idea..... All I want to be able to do is reboot the homes/nests at night... A lot of effort to use a reboot command. :0

@oiki

This comment has been minimized.

Copy link

@oiki oiki commented Feb 10, 2020

Hey everyone !!

Sorry but I can't retrieve my token

When I launh the decode script on the file from my android emulator I have this error message :

node decodeProtoFile.js home_graph_b2lraTM5QGdtYWlsLmNvbQ.proto
internal/modules/cjs/loader.js:800
throw err;
^

Error: Cannot find module 'rawproto'
Require stack:

  • C:\decodeProtoFile.js
    �[90m at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)�[39m
    �[90m at Function.Module._load (internal/modules/cjs/loader.js:690:27)�[39m
    �[90m at Module.require (internal/modules/cjs/loader.js:852:19)�[39m
    �[90m at require (internal/modules/cjs/helpers.js:74:18)�[39m
    at Object. (C:\decodeProtoFile.js:3:18)
    �[90m at Module._compile (internal/modules/cjs/loader.js:959:30)�[39m
    �[90m at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)�[39m
    �[90m at Module.load (internal/modules/cjs/loader.js:815:32)�[39m
    �[90m at Function.Module._load (internal/modules/cjs/loader.js:727:14)�[39m
    �[90m at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)�[39m {
    code: �[32m'MODULE_NOT_FOUND'�[39m,
    requireStack: [ �[32m'C:\decodeProtoFile.js'�[39m ]
    }

Anyone can help me ?

Thanks a lot

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 10, 2020

@oiki you'll need to install the rawproto package

npm i rawproto
@oiki

This comment has been minimized.

Copy link

@oiki oiki commented Feb 10, 2020

thanks for your answer but now I have another error :

[] Reading proto binary... (home_graph_b2lraTM5QGdtYWlsLmNvbQ.proto)
[
] Parsing file...
[*] Extracting token...

C:\decodeProtoFile.js:23
var device = getObjByKey(val2['7'], '17')['17'][0]['2'] + ', ' + getObjByKey(val2['7'], '18')['18'][0]['2']
^

TypeError: Cannot read property '2' of undefined
at C:\decodeProtoFile.js:23:67
at Array.forEach ()
at C:\decodeProtoFile.js:21:18
at Array.forEach ()
at Object. (C:\decodeProtoFile.js:19:6)
at Module._compile (internal/modules/cjs/loader.js:959:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
at Module.load (internal/modules/cjs/loader.js:815:32)
at Function.Module._load (internal/modules/cjs/loader.js:727:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 10, 2020

Yeah, looks like that script needs an update. I will look into it in a few hours, but you can try to figure it out by open the file in a text editor. One of the long string near your email should be it.

@oiki

This comment has been minimized.

Copy link

@oiki oiki commented Feb 10, 2020

Thanks a lot

I think I have Find the token.

A part of functions works but I can't for example retrieve alarm data and timer data.

The only part who work is the song and the radio menu

Thanks for your help

@oiki

This comment has been minimized.

Copy link

@oiki oiki commented Feb 13, 2020

Hey @rithvikvibhu

I didn't find the right token I have just access to the anonymous api.

Can you explain me how to use your script or how to find the right token in the text file. It seems I didn't have the right string near to my email.

Thank's in advance and have a nice day

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 13, 2020

Hey @oiki, I'm working on the script and need a few samples to test it. Can you email me your file at rithvikvibhu@gmail.com?

@oiki

This comment has been minimized.

Copy link

@oiki oiki commented Feb 13, 2020

who is the format of the token string please ?

@minscof

This comment has been minimized.

Copy link

@minscof minscof commented Feb 16, 2020

Hello,

I have some trouble with the script, here is information about the error, I hope, it can help

` var token = getObjByKey(val2['7'], '28')['28'];
^

TypeError: Cannot read property '28' of undefined
at C:\adbv2\platform-tools\protodecode.js:24:57
at Array.forEach ()
at C:\adbv2\platform-tools\protodecode.js:21:18
at Array.forEach ()
at Object. (C:\adbv2\platform-tools\protodecode.js:19:6)
at Module._compile (internal/modules/cjs/loader.js:959:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
at Module.load (internal/modules/cjs/loader.js:815:32)
at Function.Module._load (internal/modules/cjs/loader.js:727:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)`

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 20, 2020

@minscof I need a few sample files to fix the script as I don't have any GH with me (at least for a few days). Can you email me your proto file at rithvikvibhu@gmail.com?

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Feb 20, 2020

@oiki the new script should now work properly. @minscof thanks for sending the file!

Also, I ported the same code to a website so you don't have to download the script and download nodejs.
The website finds all devices and tokens from the file and everything happens offline. Nothing from the file leaves the browser.

https://rithvikvibhu.github.io/gh-web-proto-decode/

I'll update the gist with the link in a while, but it's dead simple. Just pick the .proto file and click "Decode".

@roflcoopter

This comment has been minimized.

Copy link

@roflcoopter roflcoopter commented Mar 28, 2020

I suddenly have an issue with this, its no longer able to decode my proto file using the website.
This is the error in the console, seems there is something new with the .proto file??

main.js:1 Uncaught RangeError: index out of range: 11 + 4260323311 > 4714
    at a (main.js:1)
    at u.bytes (main.js:1)
    at o (main.js:7)
    at FileReader.e.onload (main.js:7)
@benjamingwynn

This comment has been minimized.

Copy link

@benjamingwynn benjamingwynn commented Jun 3, 2020

I'm able to get the tokens for all of the devices on my network, but the device always returns HTTP 401.

x@nanite:~$ curl https://192.168.0.33:8443/setup/bluetooth/status -k -H "cast-local-authorization-token: TOKEN" --verbose
*   Trying 192.168.0.33:8443...
* TCP_NODELAY set
* Connected to 192.168.0.33 (192.168.0.33) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: OU=Cast; L=Mountain View; O=Google Inc; ST=California; C=US; CN=AXFJJU FA8FCA3ADCC6
*  start date: Feb 22 22:52:17 2017 GMT
*  expire date: Feb 17 22:52:17 2037 GMT
*  issuer: C=US; ST=California; L=Mountain View; O=Google Inc; OU=Cast; CN=Chromecast ICA 6 (Audio Assist)
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /setup/bluetooth/status HTTP/1.1
> Host: 192.168.0.33:8443
> User-Agent: curl/7.68.0
> Accept: */*
> cast-local-authorization-token: TOKEN
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Access-Control-Allow-Headers:Content-Type
< Cache-Control:no-cache
< Content-Length:0
<
* Connection #0 to host 192.168.0.33 left intact

On firmware 195690 & cast firmware 1.46.195690 in GB - any idea what I'm missing? I think the token is up to date, I grabbed them immediately after interfacing with the device through the Google Home app so I don't see how they'd be wrong/out of date.

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Jun 4, 2020

@benjamingwynn I am on the same firmware and the same curl just worked for me. The only thing I can think of is an invalid token. Can you re-check if the whole token was copied (maybe a letter missed at the end, etc.)?

@roflcoopter Sorry for the late reply, I just saw your comment. I just used it with my file and there weren't any errors. But I don't have a lot of devices so it is probably breaking in some specific cases. If you don't mind, can you send me the proto file at rithvikvibhu@gmail.com? I'll fix the script to handle that kind of case.

@roflcoopter

This comment has been minimized.

Copy link

@roflcoopter roflcoopter commented Jun 4, 2020

@rithvikvibhu the problem went away after a while, sadly i dont have the proto-file anymore. Must have been something temporarily wrong with it as both your implementation and another python implementation I've been using was having the same issue decoding it.

@benjamingwynn

This comment has been minimized.

Copy link

@benjamingwynn benjamingwynn commented Jun 4, 2020

@rithvikvibhu thanks for the quick reply, I think the key is correct, although we have a lot of Chromecast devices on the network so it could be misreported maybe? Do you know how many characters long a key should be?

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Jun 5, 2020

@benjamingwynn which method are you using? (a) Frida or (b) locally running nodejs script with proto file or (c) the web decoding webpage?

In the case of Frida, the script doesn't process anything. It just lists all network requests. Since you say there are a lot of devices, then maybe the token is for the next/previous request to a different device instead?

All the tokens I've seen till now have been 108 chars long.

@benjamingwynn

This comment has been minimized.

Copy link

@benjamingwynn benjamingwynn commented Jun 5, 2020

@rithvikvibhu I used the web decoding page on my Android phone directly after grabbing the file with Solid Explorer and moving it to the data directory.

I tried two of the speakers listed, but the one I was trying to get to work originally (Benjamin's Room) had really weird formatting. Here's a very obfuscated copy of the table extracted. The key for the top speaker didn't work either, even though the formatting seemed correct.

google-key.png

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Jun 6, 2020

That's really weird (and interesting :p)

If I'm understanding correctly, out of the 7 tokens, the two tokens you've tested (1st and 10th), didn't work.
See if you can test a few others too.

As for the weird object, I've never seen that before and I'd like to take a look at that proto file.

@benjamingwynn

This comment has been minimized.

Copy link

@benjamingwynn benjamingwynn commented Jun 6, 2020

@rithvikvibhu I just spent some time retesting this, really weird behavior though.

I can access the first device via port HTTP:8008 without a token just fine, but the tenth device (the one I want to actually use for this project) returns 403 Forbidden over HTTP:8008 and 401 Unauthorized over HTTPS:8443 with the cast-local-authorization-token above (the one with weird JS object surrounding it).

Weirdly, both devices are marked with the exact same version number, cast version number, country code and language, and both are on the preview program and linked to the same accounts/home.

I can send the file via email or something, not all of the devices on this network are mine and I don't want to expose their keys.

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Jun 7, 2020

For a few weeks, while Google was rolling out the switch from 8008 to 8443, some devices kept fluctuating with every reboot. But then, 8443 became more frequent and 8008 didn't work anymore. All this happened without the version number changing. Probably with feature flags.

Maybe somehow the first GH is still using 8008. Rebooting it a few times might switch it to 8443. You may(not) want to try this, whatever suits you.

For the 10th GH, it looks like the token isn't being extracted properly (the js object instead of string points to that). Of course, you wouldn't want to upload the proto file here on GitHub. If possible, please send it to rithvikvibhu@gmail.com. FYI, the tokens expire every few days.

@benjamingwynn

This comment has been minimized.

Copy link

@benjamingwynn benjamingwynn commented Jun 7, 2020

@rithvikvibhu That's fascinating, I can mess around with those two devices some more later and ask a couple of my flatmates if I can experiment on their devices too. I'll make sure to renew the token later.

I've sent you an email with the proto file attached, if you have telegram/whatever chat app we can try resolve this privately there to save spamming this gist?

@zapphyre

This comment has been minimized.

Copy link

@zapphyre zapphyre commented Nov 14, 2020

well, I just got my chromecast, can't connect from pc, casting is being distupted from phone, so I figured out to update. current firmware is 1.42.172094. device is not finding new firmware when rebooting, so I wanted to force it. I got to all this gist stuff and I found out my master token and access token. I got my local access token so I should be able to make autnenticated calls but they fail with:

~> curl -H "cast-local-authorization-token: i7YQu6bgnYqBm6+TpO5fRM+ekXm+ASnIuKmZ7/1eBu0kDnT733TLzLwW7rNFth7/HebEOrttqKECtva/7ItFwAfq8NFOgq2Pjmb3TxQA7mA=" --verbose --insecure https://192.168.0.122:8443/setup/bluetooth/status

*   Trying 192.168.0.122:8443...
* Connected to 192.168.0.122 (192.168.0.122) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=California; L=Mountain View; O=Google Inc; OU=Cast; CN=ADW2ZFQ FA8FCA7DC655
*  start date: Mar  5 08:00:00 2019 GMT
*  expire date: Apr 13 06:33:28 2040 GMT
*  issuer: C=US; ST=California; L=Mountain View; O=Google Inc; OU=Cast; CN=Chromecast ICA 12
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /setup/bluetooth/status HTTP/1.1
> Host: 192.168.0.122:8443
> User-Agent: curl/7.73.0
> Accept: */*
> cast-local-authorization-token: i7YQu6bgnYqBm6+TpO5fRM+ekXm+ASnIuKmZ7/1eBu0kDnT733TLzLwW7rNFth7/HebEOrttqKECtva/7ItFwAfq8NFOgq2Pjmb3TxQA7mA=
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Content-Length:0
< 
* Connection #0 to host 192.168.0.122 left intact

just want to say it's so disappointing when nothing works... returning the device
thank's for all support, good luck in life

@rithvikvibhu

This comment has been minimized.

Copy link
Owner Author

@rithvikvibhu rithvikvibhu commented Nov 14, 2020

@zapphyre I don't think Chromecasts have bluetooth modules, only Google Home devices do. But the rest of the docs should apply to Chromecasts as well.

@Mylmer

This comment has been minimized.

Copy link

@Mylmer Mylmer commented May 4, 2021

Hi, I am trying gh-web-proto-decode with my home_graphXXXX.proto but no devices found. Does gh-web-proto-decode working ?

Thanks for your reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment