deepbrook
deepbrook

Reputation: 2656

Unicode Objects must be encoded before hashing error

Questions dealing with similar issues: SO 1, SO 2, SO 3.

I've tried their answers, encoding pretty much any string to utf-8, but hmac still tells me to encode my unicoe chars. The biggest problem is that I can't even identify the offending variable; print outputs tell me they're either strings or bytes; in the case of the former I attach .encode(), but that hasn't helped.

I'm trying to query the GDAX API and am also using the code as given on their API page. Being written for Python2.7, I figured there might be issues with encoding and all, but this is just not making sense to me.

My code:

class CoinbaseExchangeAuth(AuthBase):
    def __init__(self, api_key, secret_key, passphrase):
        self.api_key = api_key.encode()
        self.secret_key = secret_key.encode()
        self.passphrase = passphrase.encode()

    def __call__(self, request):
        timestamp = str(time.time())
        message = timestamp + request.method + request.path_url + (request.body or '')
        hmac_key = base64.b64decode(self.secret_key)
    #print(hmac_key, type(hmac_key))
    #print(message, type(message))
    signature = hmac.new(hmac_key, message, hashlib.sha256)

    signature_b64 = signature.digest().encode('base64').rstrip('\n')


    request.headers.update({
        'CB-ACCESS-SIGN': signature_b64,
        'CB-ACCESS-TIMESTAMP': timestamp,
        'CB-ACCESS-KEY': self.api_key,
        'CB-ACCESS-PASSPHRASE': self.passphrase,
        'Content-Type': 'application/json'
    })
    return request

The error:

File "F:\git\knowhere\Private\bitex-crawler\gdax_client\gdaxex\api.py", line 47, in __call__
signature = hmac.new(hmac_key, message, hashlib.sha256)
File "C:\Users\nls\Anaconda3\lib\hmac.py", line 144, in new
return HMAC(key, msg, digestmod)
File "C:\Users\nls\Anaconda3\lib\hmac.py", line 84, in __init__
self.update(msg)
File "C:\Users\nls\Anaconda3\lib\hmac.py", line 93, in update
self.inner.update(msg)
TypeError: Unicode-objects must be encoded before hashing

When I type check the objects I feed to my hmac.new() call, it tells me I have a str object and a bytes object.

print(type(hmac_key)) # <bytes>
print(type(message))  # <str>

Naturally, I thought I need to encode that sucker too:

signature = hmac.new(hmac_key, message.encode(), hashlib.sha256)

which resulted in an error on this line:

signature_b64 = signature.digest().encode('base64').rstrip('\n')

namely:

File "F:/git/knowhere/Private/bitex-crawler/gdax_client/client.py",
[..]
File "F:\git\knowhere\Private\bitex-crawler\gdax_client\gdaxex\api.py", line 123, in _query
r = api_query(url, json=req, auth=auth)
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\api.py", line 67, in get
return request('get', url, params=params, **kwargs)
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\api.py", line 53, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\sessions.py", line 454, in request
prep = self.prepare_request(req)
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\sessions.py", line 388, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\models.py", line 297, in prepare
self.prepare_auth(auth, url)
File "C:\Users\nls\Anaconda3\lib\site-packages\requests\models.py", line 490, in prepare_auth
r = auth(self)
File "F:\git\knowhere\Private\bitex-crawler\gdax_client\gdaxex\api.py", line 49, in __call__
signature_b64 = signature.digest().encode('base64').rstrip('\n')
AttributeError: 'bytes' object has no attribute 'encode'

..So I can't have unencoded unicode objects, but I cant have bytes later on either? How on earth do I fix this? Appreciating any help on this, because I'm profoundly confused.

Upvotes: 4

Views: 4127

Answers (2)

Abhijeet Kamble
Abhijeet Kamble

Reputation: 1

For Python 3.9, I was getting the same error,

self._inner.update(msg) TypeError: Unicode-objects must be encoded before hashing

The following worked for me,

signature = hmac.new(key.encode('utf-8'), msg.encode('utf-8'), hashlib.sha256)
auth_token = str(signature.hexdigest())

Upvotes: 0

Simon Kirsten
Simon Kirsten

Reputation: 2577

"Parameter msg can be of any type supported by hashlib.".

"Note: Feeding string objects into is not supported, as hashes work on bytes, not on characters.".

Thus, your message must be of type bytes. Use .encode() on the message wich will give you the bytes object.

Note: This is only necessary for python 3!

To encode the digest to base64 use the base64 library.

import base64
signature_b64 = base64.b64encode(signature.digest())

Upvotes: 4

Related Questions