Okay, we got an Philips Airfryer from Santa and I’m more than curious to find a way to integrate it into my HA environment.
I live on the iPhone world and used Charles proxy to decode the traffic.
What I found out so far is, that the app is using UPNP to find the Airfryer on the local network.
curl http://192.168.42.96/upnp/description.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>2</major>
<minor>1</minor>
</specVersion>
<device>
<deviceType>urn:philips-com:device:DiProduct:1</deviceType>
<friendlyName>Cosmos</friendlyName>
<manufacturer>Royal Philips Electronics</manufacturer>
<modelName>AirfryerConnected</modelName>
<modelNumber>HD9285/00</modelNumber>
<UDN>uuid:12345678-1234-1234-1234-e4bc9601cb4c</UDN>
<cppId>e4:bc:96:01:cb:4c</cppId>
</device>
</root>
Not sure if we need the “ccpid” for generating the key (that’s why I posted it here).
But I’m not looking into the UPNP implementation for now, this should be kind of easy to implement.
The next step is a POST request to the following URL:
curl -X POST -i --insecure https://192.168.42.96/di/v1/products/0/firmware
HTTP/1.1 401 Unauthorized
Content-Length: 0
Content-Type: text/plain
WWW-Authenticate: PHILIPS-Condor VnX/m5uWc6CV/H38ztbzog==
So the Authenticate header is giving you a base64 encoded Challenge.
This string changes from time to time and can be forced to change after a power-cycle.
If you have the correct Aithorizartion header, you can use it to call the Airfryer for a status:
curl -X GET -i --insecure --header "User-Agent: cml" --header "Content-Type: application/json" --header "Authorization: PHILIPS-Condor ssyjLmLPQSeIh/0nGNJYjgXeKfz66Px5QFU5PFwAc7/5G47//zT7CXhDoLhzL+lH" https://192.168.42.96/di/v1/products/1/airfryer
HTTP/1.1 200 OK
Content-Length: 229
Content-Type: application/json
Strict-Transport-Security: max-age=31536000
X-Condor-Features: changeindication-port
{"time":60,"cur_time":60,"timestamp":"2024-01-02T12:48:51Z","temp":180,"temp_unit":false,"drawer_open":false,"preset":0,"error":0,"prev_status":"idle","status":"standby","step_id":"","recipe_id":"","shaker_reminder_active":false}
I collected a couple of challenges and keys:
C: VnX/m5uWc6CV/H38ztbzog==
K: ssyjLmLPQSeIh/0nGNJYji/s2TonjTGzlRaCw/aKbVC50sqMbgxUffIjZhAijTSn
C: H1dhEZly5ek24AgRjpO8Kg==
K: ssyjLmLPQSeIh/0nGNJYjkVncSZUm2a0mTaYZ5eBnUc7SSWdFalGsdyqRm3ZMFst
C: tLpTA68q+rl2mhTBKtV7LA==
K: ssyjLmLPQSeIh/0nGNJYjgXeKfz66Px5QFU5PFwAc7/5G47//zT7CXhDoLhzL+lH
eismac:~/Downloads/airfryer $ echo "ssyjLmLPQSeIh/0nGNJYji/s2TonjTGzlRaCw/aKbVC50sqMbgxUffIjZhAijTSn" | base64 -d | od -x
0000000 ccb2 2ea3 cf62 2741 8788 27fd d218 8e58
0000020 ec2f 3ad9 8d27 b331 1695 c382 8af6 506d
0000040 d2b9 8cca 0c6e 7d54 23f2 1066 8d22 a734
eismac:~/Downloads/airfryer $ echo "ssyjLmLPQSeIh/0nGNJYjkVncSZUm2a0mTaYZ5eBnUc7SSWdFalGsdyqRm3ZMFst" | base64 -d | od -x
0000000 ccb2 2ea3 cf62 2741 8788 27fd d218 8e58
0000020 6745 2671 9b54 b466 3699 6798 8197 479d
0000040 493b 9d25 a915 b146 aadc 6d46 30d9 2d5b
eismac:~/Downloads/airfryer $ echo "ssyjLmLPQSeIh/0nGNJYjgXeKfz66Px5QFU5PFwAc7/5G47//zT7CXhDoLhzL+lH" | base64 -d | od -x
0000000 ccb2 2ea3 cf62 2741 8788 27fd d218 8e58
0000020 de05 fc29 e8fa 79fc 5540 3c39 005c bf73
0000040 1bf9 ff8e 34ff 09fb 4378 b8a0 2f73 47e9```
So the interesting part is, that the first couple of characters are always the same.
Continuing my research I found an reposity on GitHub which has a decompiled version of the NutriU App:
I think this interesting code can be found in this file:
smali_classes3/com/philips/connectivity/condor/lan/authentication/AuthenticationCredentials.smali
There is a Java methode defined:
.method private createAuthenticationCredentials()Ljava/lang/String;
[...]
NetworkNode;->getClientId()Ljava/lang/String;
[...]
NetworkNode;->getClientSecret()
[...]
getSHA256Hash
[...]
ByteUtil;->concatenate([B[B)[B
[...]
ByteUtil;->encodeToBase64
So it combines some values, concatenate them, build a sha256 checksum and then do a base64 encode.
Maybe someone with better Java skills can have a look at the decoded application linked above.
The algorithm is in this code, we “just” need to understand and extract it…