Design principles for signing REST content using signParams and signature

29 Views Asked by At

I am trying to recreate the behavior of an app to become an integration in Home Assistant. I am able authenticate and communicate with the server using HTTPS, but for certain endpoints - the server expects the data to be signed.

Sniffing the protocol gives the following (relevant) data:

appProjectName: fop4lite
appVersion:     1.5.0
clientDate:     2022-06-19
lang:           en
language:       en
pageNo:         1
pageSize:       100
passKey:        1A9E769E-F67C-4243-BB42-7D1CE2CCF614
platform:       iOS
signParams:     appVersion,clientDate,lang,pageNo,pageSize,passKey,platform,timeStamp,token
signature:      B619F120519488AD00A0C760546038356ABD69D9
timeStamp:      1655623282899
token:          D247E1771E4A6323011E330353BA4C26EC1A

The signature appears on surface to be a SHA1 that are built upon the parameters in signParams.

I'm suspecting that the data is encoded with some secret salt, which I obviously don't have access to. I have asked the provider of the service, but since this is a non-public API they are unlikely to support.

Therefore I seek some pointers and ideas how produce the signature.

I've created the following script with to perform different trial-and-error to attempt to understand how the signature was produced.

import hashlib
import urllib
import urllib.parse
import hmac
import base64

data = {            
    "appProjectName": "fop4lite",
    "appVersion": "1.5.0",
    "clientDate": "2022-06-19",
    "lang": "en",
    "language": "en",
    "pageNo": "1",
    "pageSize": "100",
    "passKey": "1A9E769E-F67C-4243-BB42-7D1CE2CCF614",
    "platform": "iOS",
    "signParams": "appVersion,clientDate,lang,pageNo,pageSize,passKey,platform,timeStamp,token",
    "signature": "B619F120519488AD00A0C760546038356ABD69D9",
    "timeStamp": "1655623282899",
    "token": "D247E1771E4A6323011E330353BA4C26EC1A",
    }

sign_data_tuple = (
    data['appVersion'], 
    data['clientDate'], 
    data['lang'], 
    data['pageNo'], 
    data['pageSize'], 
    data['passKey'], 
    data['platform'], 
    data['timeStamp'],
    data['token'],
    )

sign_data_dict = {
    "appVersion" : data['appVersion'], 
    "clientDate" : data['clientDate'], 
    "lang" : data['lang'], 
    "pageNo" : data['pageNo'], 
    "pageSize" : data['pageSize'], 
    "passKey" : data['passKey'], 
    "platform" : data['platform'], 
    "timeStamp" : data['timeStamp'],
    "token" : data['token'],
    }

def test_enc(my_data):
    my_hash = hashlib.sha1(my_data.encode()).hexdigest().upper()
    print("STR: ", my_data)
    if data['signature'] == my_hash:
        print("SUCCESS")
    else:
        print("FAIL")
        print("SIG: ", data['signature'])
        print("TST: ", my_hash, "\n")


test_enc(",".join(sign_data_tuple))
test_enc(",".join(sign_data_tuple)+",")
test_enc(", ".join(sign_data_tuple))
test_enc(", ".join(sign_data_tuple)+", ")
test_enc(urllib.parse.urlencode(sign_data_dict))

The output from this script is:

STR:  1.5.0,2022-06-19,en,1,100,1A9E769E-F67C-4243-BB42-7D1CE2CCF614,iOS,1655623282899,D247E1771E4A6323011E330353BA4C26EC1A
FAIL
SIG:  B619F120519488AD00A0C760546038356ABD69D9
TST:  D24688B6E3766E84D1DD16667C55D66315189D16

STR:  1.5.0,2022-06-19,en,1,100,1A9E769E-F67C-4243-BB42-7D1CE2CCF614,iOS,1655623282899,D247E1771E4A6323011E330353BA4C26EC1A,
FAIL
SIG:  B619F120519488AD00A0C760546038356ABD69D9
TST:  CAD390C2FBF0AE1B8A04EDF045DE2797E18D06E8

STR:  1.5.0, 2022-06-19, en, 1, 100, 1A9E769E-F67C-4243-BB42-7D1CE2CCF614, iOS, 1655623282899, D247E1771E4A6323011E330353BA4C26EC1A
FAIL
SIG:  B619F120519488AD00A0C760546038356ABD69D9
TST:  57C1499F38D2547B8AB737B5221FFC64C1741DCD

STR:  1.5.0, 2022-06-19, en, 1, 100, 1A9E769E-F67C-4243-BB42-7D1CE2CCF614, iOS, 1655623282899, D247E1771E4A6323011E330353BA4C26EC1A,
FAIL
SIG:  B619F120519488AD00A0C760546038356ABD69D9
TST:  DB8C0996D1C86ABDFAE21B0B9CE3396374EB681B

STR:  appVersion=1.5.0&clientDate=2022-06-19&lang=en&pageNo=1&pageSize=100&passKey=1A9E769E-F67C-4243-BB42-7D1CE2CCF614&platform=iOS&timeStamp=1655623282899&token=D247E1771E4A6323011E330353BA4C26EC1A
FAIL
SIG:  B619F120519488AD00A0C760546038356ABD69D9
TST:  64E1FE858566AF375E5B00198F0ADA9624CA1609

Now I'm out of ideas and seek some pointers and guidance.

1

There are 1 best solutions below

2
inf3rno On

It is unlikely you can do this. It can be brute forced if you figure out how the parameters are converted into a string and how they are salted, but it is unlikely you have the computing power to do so. You need to build a rainbow table and hope that the algorithm is SHA1 and the iteration count is relative low. If there is match in your rainbow table then you figured the right conversion mechanism, the right salt and the right iteration count. Hopefully there is only one salt for one account, so after that it will work for a while.

I would rather try to use the relevant part of the script the sign the request and mock it around somehow. If it is signed on the server, then send it to the server to sign it, if it is signed locally, then just cut out that part of the code somehow and use it if you can.