HMAC(wiki) is hash-based message authentication code
which verifies data integrity and authenticity of a message. Basically, it ensure sender is really who they are and whatever they sent wasn’t changed.
The algorithm can be found on wiki which is easy enough to write(i am using sha256 from hashlib :)).
function hmac is
input:
key: Bytes // Array of bytes
message: Bytes // Array of bytes to be hashed
hash: Function // The hash function to use (e.g. SHA-1)
blockSize: Integer // The block size of the hash function (e.g. 64 bytes for SHA-1)
outputSize: Integer // The output size of the hash function (e.g. 20 bytes for SHA-1)
// Compute the block sized key
block_sized_key = computeBlockSizedKey(key, hash, blockSize)
o_key_pad ← block_sized_key xor [0x5c blockSize] // Outer padded key
i_key_pad ← block_sized_key xor [0x36 blockSize] // Inner padded key
return hash(o_key_pad ∥ hash(i_key_pad ∥ message))
function computeBlockSizedKey is
input:
key: Bytes // Array of bytes
hash: Function // The hash function to use (e.g. SHA-1)
blockSize: Integer // The block size of the hash function (e.g. 64 bytes for SHA-1)
// Keys longer than blockSize are shortened by hashing them
if (length(key) > blockSize) then
key = hash(key)
// Keys shorter than blockSize are padded to blockSize by padding with zeros on the right
if (length(key) < blockSize) then
return Pad(key, blockSize) // Pad key with zeros to make it blockSize bytes long
return key
Python implementation Link to heading
The main issue here is getting the bytes arithmetic right. I wrote small utility function xor_bytes
for inner and outter hashes.
import hmac
import base64
import hashlib
def Pad(key, blockSize):
return key + (bytes("\x00", "UTF-8") * (blockSize - len(key)))
def computeBlockSizedKey(key, hash_, blockSize):
# Keys longer than blockSize are shortened by hashing them
if (len(key) > blockSize):
key = hash_(key)
# Keys shorter than blockSize are padded to blockSize by padding with zeros on the right
if (len(key) < blockSize):
return Pad(key, blockSize) # Pad key with zeros to make it blockSize bytes long
return key
def xor_bytes(v1 ,v2):
x = [ ((a) ^ (b)) for (a,b) in zip(v1, v2) ]
return bytes(x)
def my_hmac(key, message, hash_, blockSize, outputSize):
block_sized_key = computeBlockSizedKey(key, hash_, blockSize)
o_key_pad = xor_bytes(block_sized_key, bytes("\x5c", "UTF-8") * blockSize)
i_key_pad = xor_bytes(block_sized_key, bytes("\x36", "UTF-8") * blockSize)
h = hash_()
h.update(i_key_pad + message)
i = h.digest()
h = hash_()
h.update(o_key_pad + i)
o = h.digest()
return o
app_secret = "key"
access_token= "The quick brown fox jumps over the lazy dog"
digest = hmac.new(bytes(app_secret, 'UTF-8'), bytes(access_token, 'UTF-8'), hashlib.sha256)
signature = digest.hexdigest()
print(signature)
my = my_hmac(bytes(app_secret, 'UTF-8'), bytes(access_token, 'UTF-8'), hashlib.sha256, 64, 32)
print(''.join('{:02x}'.format(x) for x in my))