easy-hmac 0.0.6
A python package that creates and verifies HMAC signatures
License: MIT
Requires Python: >=3.6

# HMAC Authentication (easy-hmac)

A pure python package with no dependencies to easily handle the generation and verification of HMAC
signatures.

# Installation

This package is hosted at https://pypi.org/project/easy-hmac/

## User
If you don't need to customise this package, just install the package from
[here](https://pypi.org/project/easy-hmac/).

Ie. `pip install easy-hmac`


## Developer
1. Clone this repository
2. Build the package in your local environment.

```shell
pipenv shell
pipenv install build
python -m build easy-hmac
```
3. Install the easy-hmac package in editing mode by running:

```shell
pip install -e easy-hmac
```

4. If you want to make sure everything went well, try running the tests from
    test_easy-hmac

```shell
python easy_hmac/test/test_easy-hmac.py
```

# Usage
`easy-hmac` provides two helper functions for HMAC authentication:

- `generate_hmac_sha256` - generates a SHA256 HMAC from two strings (a secret
    key and a http message)
- `verify_hmac` - given an HMAC and a message, verifies if the HMAC generated by
    the message is equal to the one passed as argument



## Step 1
Import the package.
`python
import datetime
from easy_hmac import core
import hmac
import hashlib 
from base64 import b64encode
from typing import Dict, Any
`

## Step 2
Create some vars we can use to generate and verify the HMAC.

```python

# fake identifier used to retrieve the secret from a db.
secret = "79721503-d1ef-46b7-b4ca-fec39ece902f"
body = '{"event": "lifecycle_updated", "payload": {"uuid": "cb8c79cd-8d79-4698-90a2-662eeab8da98", "timestamp": "2021-12-10T00:16:08.048401Z", "status": "PROCESSING"}}'
method = "POST"
timestamp = datetime.datetime.now(datetime.UTC).strftime("%a, %d %b %Y %H:%M:%S GMT")
path = "/api/v1/my/path"

# vars required for verifying the HMAC.
# This step is a little long because we need to fake some parameters we'd usually already have.
identifier = "2e42a19593f047e080285e49864b0fb6"
hash = hashlib.md5(body.encode())
content_type = "application/json"
content_md5 = b64encode(hash.digest()).decode('utf-8')
message = "\n".join([method, content_md5, content_type, timestamp, path])
signature = hmac.new(bytes(secret, "latin-1"), bytes(message, "latin-1"), digestmod=hashlib.sha256)
hmac_base64 = b64encode(signature.digest()).decode("utf-8")

headers = {
    "Date": timestamp,
    "Content-MD5": content_md5,
    "Content-Type": content_type,
    "Authorization": "HMAC {}:{}".format(identifier, hmac_base64),
}
request = {"method": method, "body": body, "path": path, "headers": headers}
```

## Step 3
Create and verify the signature.

```
result_digest = core.generate_hmac_sha256(secret, method, body, path, timestamp)
actual_signature = b64encode(result_digest).decode()
expected_signature = "d8laojz+oDCPizTL1a401mHq5IpR1A9f9QK3+RQ/6hA="

core.verify_hmac(secret, hmac_base64, headers["Content-MD5"], body.encode(), headers["Date"], headers["Content-Type"], path, method)

``

You can raise an exception that the request has expired by changing the timestamp to;
`timestamp = "Fri, 10 Dec 2021 00:16:57 GMT"`
and then rerunning the second set of vars above to generate an incoming request that is too old and verifying
the signature again.