Richard Knop
Richard Knop

Reputation: 83735

Python HMAC: TypeError: character mapping must return integer, None or unicode

I am having a slight problem with HMAC. When running this piece of code:

signature = hmac.new(
    key=secret_key,
    msg=string_to_sign,
    digestmod=sha1,
)

I get a strange error:

  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hmac.py", line 133, in new
    return HMAC(key, msg, digestmod)
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hmac.py", line 72, in __init__
    self.outer.update(key.translate(trans_5C))
TypeError: character mapping must return integer, None or unicode

When I print string_to_sign, it is a proper string like this:

GET
\n
\n
application/json
\n
\n
\n

What does the error mean? Is it because of new lines?

Upvotes: 22

Views: 17427

Answers (4)

lotus echo
lotus echo

Reputation: 43

In python 2

If you code it as:

from __future__ import unicode_literals

import base64
import hashlib
import hmac
import time

def gen_signature(key, strsome, expires=0):
    expires = int(time.time()) + 600 if expires == 0 else expires
    _2_signature = '%s\n%s\n' % (expires, strsome)
    # hmac.new(key, msg, digestmod) bytearray(secret, 'utf-8')
    signature = base64.b64encode(hmac.new(key, _2_signature, hashlib.sha1).digest())
    return signature


gen_signature('xxxxxxx', 'strsome')

you get an error like the one you provided. However, if you use bytearray(key, 'utf-8') in place of the raw key variable and bytearray(_2_signature, 'utf-8') in place place of the _2_signature variable, it should work properly.

For example:

def gen_signature(key, strsome, expires=0):
    expires = int(time.time()) + 600 if expires == 0 else expires
    _2_signature = '%s\n%s\n' % (expires, strsome)
    # hmac.new(key, msg, digestmod) bytearray(secret, 'utf-8')
    signature = base64.b64encode(hmac.new(bytearray(key, 'utf-8'), bytearray(_2_signature, 'utf-8'), hashlib.sha1).digest())
    return signature


gen_signature('xxxxxxx', 'strsome')

Upvotes: 1

Danilo Cabello
Danilo Cabello

Reputation: 2963

Since I didn't want to write in our API docs that you should convert the payload to ASCII or drop Unicode characters before comparing digests, I've used the following solution:

import hmac
import hashlib

def sign_request(self, secret, data):
    return hmac.new(
        key=bytearray(secret, 'utf-8'),
        msg=bytearray(data, 'utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()
  • bytearray turns my unicode string into bytes using the utf-8 encoding.
  • key and msg are bytes parameters (like hmac library is expecting).

Upvotes: 1

zhuxiongxian
zhuxiongxian

Reputation: 161

Ensure that "key" and "msg" is a string.such as:

s = hmac.new(str(secretkey), str(message), digestmod=hashlib.sha1).hexdigest()

Upvotes: 16

smeso
smeso

Reputation: 4295

As asked I'll post this as an answer. The error that you faced is a feature of Python's HMAC. It does not accept unicode. This feature is described here.

HMAC is a function which works at byte level. For this reason in Python 3 it accepts only bytes. In Python 2 we don't have bytes so it accepts only str.

Upvotes: 45

Related Questions