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))