Reverbrain wiki

Site Tools


rift:signature

Authorization signature

Base string generating

To generate signature firstly we have to prepare base string.

It's first line is HTTP method. Second line is requests's url without scheme and host part. URL queries must be sorted in lexicographical order. Next lines consists of sorted list of all HTTP headers which name starts from X-ELL- in special form:

  • Header name must be lower-cased. Line must be formed as header-name:header-value without any trailing spaces around colon.
  • All headers must be sorted by key in lexicographical order.

All lines are separated by \n. There is always a \n in the end of the string.

Example

Let's suppose that we have GET request to url http://example.com:8080/get?name=test&country=ru&lang=ru&namespace=qwerty and headers are:

X-ELL-TIME: 1386258035
X-ELL-OFFSET: 1024
Range: 0-49

We take in account only X-ELL- headers and transform their keys to lower-case:

x-ell-offset:1024
x-ell-time:1386258035

Make attention that headers and url queries are sorted in lexicographical order.

Then the whole text looks like:

GET
/get?country=ru&lang=ru&name=test&namespace=qwerty
x-ell-offset:1024
x-ell-time:1386258035

Following snippet shows you python function to calculate the signature:

http_auth.py
#!/usr/bin/python
 
import requests
import hmac
import hashlib
import urlparse
import urllib
import argparse
 
def generate_signature(key, method, url, headers=None):
    parsed_url = urlparse.urlparse(url)
    queries = urlparse.parse_qsl(parsed_url.query)
    queries.sort()
    text = ''
    text += method + '\n'
    text += parsed_url.path
    if len(queries) > 0:
        text += '?' + urllib.urlencode(queries)
    text += '\n'
    if headers:
        headers = map(lambda x: (x[0].lower(), x[1]), headers.iteritems())
        headers = filter(lambda x: x[0].startswith('x-ell-'), headers)
        headers.sort()
 
        for header in headers:
            text += header[0] + ':' + header[1] + '\n'
 
    return hmac.new(key, text, hashlib.sha512).hexdigest()
 
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Send request to rift.')
    parser.add_argument('url', metavar='URL', help='Url for processing')
    parser.add_argument('--file', dest='file', action='store', default=None, help='File to send with POST request')
    parser.add_argument('--user', dest='user', action='store', default=None, help='Token owner to sign request')
    parser.add_argument('--token', dest='token', action='store', default=None, help='Secure token to sign request')
    args = parser.parse_args()
 
    headers = {}
    if args.token and args.user:
        headers['Authorization'] = 'riftv1 {0}:{1}'.format(args.user, generate_signature(args.token, 'POST' if args.file else 'GET', args.url))
    elif args.token or args.user:
		raise Exception('Both --user and --token must be specified at the same time')
 
    if not args.file:
        r = requests.get(args.url, headers=headers)
    else:
        with open(args.file) as f:
            data = f.read()
            r = requests.post(args.url, data, headers=headers)
 
    print r.status_code
    print r.content

Signature calculating

Resulted signature is just HMAC(SHA512) of base string as text and your bucket's token as key.

So SHA512(text) should be c61d02eed0614bf59c6a7a41835cc255124eb89faa056e362499944a5aa40978b5bd0b45d9726a0a4972acd2525fbc2dbc07de54e4321e326ebd6433b41d23d3

Final signature for secret key secret_key is 56d6accac6bea2782191f8c5337b7ddfe8c71627b7c33e91ba7efcd2fa8d12166ec56c9f3a3275c6e43ab3c9560be154aca112e56287c2f4dc5cafdc26c653a5

Using above headers your test code might look like this:

headers = {'x-ell-offset' : '1024', 'X-Ell-Time' : '1386258035'}
generate_signature("secret_key", "GET", "/get?country=ru&lang=ru&name=test&namespace=qwerty", headers=headers)
'56d6accac6bea2782191f8c5337b7ddfe8c71627b7c33e91ba7efcd2fa8d12166ec56c9f3a3275c6e43ab3c9560be154aca112e56287c2f4dc5cafdc26c653a5'

It must be added to your request as Authorization header:

Authorization: riftv1 username:56d6accac6bea2782191f8c5337b7ddfe8c71627b7c33e91ba7efcd2fa8d12166ec56c9f3a3275c6e43ab3c9560be154aca112e56287c2f4dc5cafdc26c653a5

where username will be matched against appropriate name in ACL list.

rift/signature.txt ยท Last modified: 2017/05/16 13:27 by zbr