README.md

hairnet
=====

A take on [fernet](https://github.com/fernet/fernet-erl) using AES-GCM
([AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption))
rather than fernet's AES-128 in CBC mode + HMAC.

The idea is to take what is now a fairly weak crypto library and bring
it up to a higher standard while maintaining a similar interface.

The objective is to:

> "takes a user-provided message (an arbitrary sequence of
> bytes), a key (256 bits), and the current time, and produces a token, which
> contains the message in a form that can't be read or altered without the key."

In terms of AEAD, the PlainText will be the user-provided message, the key
should be any Erlang binary (please use a different one from the one in
`fernet` if you are upgrading), and the current time is going to be used as
the additional authenticated data (AAD), which can be used by the party
doing decryption to validate for staleness.


## Interface

```erlang
1> Key = hairnet:generate_encoded_key().
<<"BVt1_R20scbTwz9t05PrtE4EFAauMeRKTxbwYmUiafY=">>
2> Token = hairnet:generate_token("hello", Key).
<<"AQAAAABXH4wKw8DqUtDjJxAX3BuEHGP9xke0tfY-73uzVCpa1iT5f1wgAAAABbhBUeFl">>
3> hairnet:verify_and_decrypt_token(Token, Key, infinity).
{ok, <<"hello">>}
4> TTL = 10. % 10 Seconds
10
5> hairnet:verify_and_decrypt_token(Token, Key, TTL).
{error, too_old}
```

### Difference from fernet

- `encode_key/2` is gone, since there is no longer a distinction between
  a signing and an encryption key; the AES-GCM algorithm handles this.
- The format changed since the data size is more variable given the AAD,
  and prefixed lengths are being used to carry the content.
- pkcs7 is no longer needed, and the padding-related errors are no longer
  returnable, since this is all handled by the crypto library.
- block sizes warnings are gone, instead an overall `payload_format`
  error value can be returned.
- Dropped compatibility for pre-18 Erlang/OTP copies

Build
-----

    $ rebar3 compile

Test
----

    $ rebar3 do eunit, dialyzer

Warnings
--------

The current code is pending any form of review. Using it in production
should be done at your own risk.

Additionally, the [publications from NIST on the
algorithm](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf)
have the following specifications:

> The following requirement applies to all implementations that use either 1) the
> deterministic construction with IVs whose length is not 96, or 2) the RBG-based
> construction, for IVs of any length. In other words, unless an implementation
> only uses 96-bit IVs that are generated by the deterministic construction:
> 
> **The total number of invocations of the authenticated encryption function shall
> not exceed 2^32, including all IV lengths and all instances of the
> authenticated encryption function with the given key.**
> 
> This is a “global” requirement that can be achieved by appropriate “local”
> limits on each instance of the authenticated encryption function with a given
> key. For example, suppose an implementation consists of 2^10 devices that only
> support 64-bit, 96-bit, and 128-bit IVs. One way to satisfy the above
> requirement would be to limit each device to 2^20 invocations with 64-bit IVs,
> 2^21 invocations with 96-bit IVs, and 2^20 invocations with 128-bit IVs.

This implementation uses Erlang's `crypto:strong_rand_bytes(16)` to generate
128-bit IVs. As such, it would be recommended to rotate the key used to
encrypt content once such limits are reached to avoid weakening the security
of the private key.