Waboodoo / HTTP-Shortcuts

Android app to create home screen shortcuts that trigger arbitrary HTTP requests
https://http-shortcuts.rmy.ch
MIT License
1.17k stars 113 forks source link

Feature Request: Binary HMAC would make it possible to do AWS API requests #241

Closed lassebomh closed 3 years ago

lassebomh commented 3 years ago

Is your feature request related to a problem? Please describe. AWS API requests from a regular HTTP client is surprisingly complicated but it is incredibly close to being possible with a JavaScript shortcut. One of the things it requires is creating a signed signature but this is currently (extremely close but) impossible to do.

Describe the solution you'd like 2 functions:

  1. A HMAC function that takes a byte array as key and returns the hash as a byte array instead of a hex string (the message can remain a string).
  2. A function that turns a byte array into a hex string

Describe alternatives you've considered Option 1: Creating my own implementation of the HMAC function from scratch. ~Option 2: Pray to the gods that it's possible to hash a hex string that has been converted to binary and then to a regular string.~

Additional context This is how you create a signed signature in python. I have marked all places that cannot be done in a JavaScript shortcut.

import os, datetime, hashlib, hmac 

method = 'GET'
service = 'ec2'
region = 'us-east-1'
host = f'{service}.amazonaws.com'
endpoint = f'https://{host}'
request_parameters = 'Action=DescribeRegions&Version=2016-11-15'

secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')

t = datetime.datetime.utcnow()
datestamp = t.strftime('%Y%m%d')
amzdate = t.strftime('%Y%m%dT%H%M%SZ')

def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() # Impossible

def getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp) # Impossible
    kRegion = sign(kDate, regionName) # Impossible
    kService = sign(kRegion, serviceName) # Impossible
    kSigning = sign(kService, 'aws4_request') # Impossible
    return kSigning

canonical_querystring = request_parameters
canonical_uri = '/' 
canonical_headers = f'host:{host}\nx-amz-date:{amzdate}\n'
signed_headers = 'host;x-amz-date'

payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
print(payload_hash)
canonical_request = f'{method}\n{canonical_uri}\n{canonical_querystring}\n{canonical_headers}\n{signed_headers}\n{payload_hash}'

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = f'{datestamp}/{region}/{service}/aws4_request'
string_to_sign = f'{algorithm}\n{amzdate}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()}'

signing_key = getSignatureKey(secret_key, datestamp, region, service)

signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()  # Impossible

print(signature)

The complete example can be found here

I would personally use this to get HTTP-Shortcuts to start a EC2 Spot instance (basically a unused and very cheap server) to run a workload once every day. Really cool if I can control this from my phone.

Oh yeah and great work on the app. It's awesome

Waboodoo commented 3 years ago

I'll look into it. I will have to make some changes to the JS engine, as currently it can only deal with strings, not byte arrays.

Just to confirm, by byte array you mean Uint8Array?

Waboodoo commented 3 years ago

@lassebomh Version 2.10.0 is now available for beta testing in the Play Store. It contains some key changes to enable this usecase. Could you give it a try and let me know if it addresses your issue? Beta testing signup can be found here: https://play.google.com/apps/testing/ch.rmy.android.http_shortcuts

lassebomh commented 3 years ago

Awesome! I'll check it out tomorrow and make sure to provide an example if anyone else is interested.

lassebomh commented 3 years ago

It got it working :) I have created a repository that includes an example and a guide. Thanks for the help!

Waboodoo commented 3 years ago

@lassebomh Thanks for confirming that it works for you. I will then close this issue, as version 2.10.0 is now released.

For your repository, I recommend that you export a working solution from the app and include the exported file into your repository, such that others can easily download and import it, or even import it directly via a URL. Just make sure to remove the secret values from the variables in the exported file.