Pawel Miech Pawel Miech - 4 months ago 12
Python Question

What is the reason for _secret_backdoor_key variable in Python SL HMAC library source code?

When I was browsing Python HMAC module source code today I found out that it contains global variable

_secret_backdoor_key
. This variable is then checked to interrupt object initialization.

The code looks like this

# A unique object passed by HMAC.copy() to the HMAC constructor, in order
# that the latter return very quickly. HMAC("") in contrast is quite
# expensive.
_secret_backdoor_key = []

class HMAC:
"""RFC 2104 HMAC class. Also complies with RFC 4231.

This supports the API for Cryptographic Hash Functions (PEP 247).
"""
blocksize = 64 # 512-bit HMAC; can be changed in subclasses.

def __init__(self, key, msg = None, digestmod = None):
"""Create a new HMAC object.

key: key for the keyed hash object.
msg: Initial input for the hash, if provided.
digestmod: A module supporting PEP 247. *OR*
A hashlib constructor returning a new hash object.
Defaults to hashlib.md5.
"""

if key is _secret_backdoor_key: # cheap
return


Full code is here.

Does anyone know what is the reason for this variable? Comment says it is there for HMAC to return quicker than blank string (""). But why would user want to pass empty key to HMAC function? Is variable naming just a joke from HMAC developer or is it really some sort of backdoor?

Answer

To create a copy of the HMAC instance, you need to create an empty instance first.

The object is used as a sentinel to exit __init__ early and not run through the rest of the __init__ functionality. The copy method then sets the instance attributes directly:

def copy(self):
    """Return a separate copy of this hashing object.

    An update to this copy won't affect the original object.
    """
    other = self.__class__(_secret_backdoor_key)
    other.digest_cons = self.digest_cons
    other.digest_size = self.digest_size
    other.inner = self.inner.copy()
    other.outer = self.outer.copy()
    return other

You could get the same effect with self.__class__('') (an empty string), but then HMAC.__init__ does a lot of unnecessary work as the attributes on the instance created are going to be replaced anyway. Note that using HMAC('') is a valid way to create an instance, you'd not want an instance devoid of any state in that case. By passing in the sentinel, HMAC.copy() can avoid all that extra work.

You could use a different 'flag' value, like False, but it is way too easy to pass that in because of a bug in your own code. You'd want to be notified of such bugs instead. By using a 'secret' internal sentinel object instead, you avoid such accidental cases.

Using [] as a sentinel unique object is quite an old practice. These days you'd use object() instead. The idea is that the sentinel is a unique, single object that you test against for identity with is. You can't re-create that object elsewhere, the is test only works if you pass in an reference to the exact same single object.