README.md

# branca-erl

[![Build Status](https://travis-ci.org/1ma/branca-erl.svg?branch=master)](https://travis-ci.org/1ma/branca-erl)

An Erlang implementation of the [Branca specification] for authenticated and encrypted tokens.

These are symmetrically encrypted, tamper-proof strings of arbitrary contents that can be safely exposed.


## Installation

Add this to your `rebar.config` file to install the library through hex.pm:

```erlang
{deps, [
    {branca_erl, "0.1.1"}
]}.
```


## Basic Usage

```erlang
1> Secret = soda:rand(32). % the spec mandates that secret keys must be exactly 32 bytes long.
<<238,191,60,162,227,35,20,3,135,35,6,69,45,10,213,250,3,
  106,71,133,119,70,131,43,173,147,60,182,122,...>>

2> Message = erlang:term_to_binary({foo, bar, baz, [1,2,3]}).
<<131,104,4,100,0,3,102,111,111,100,0,3,98,97,114,100,0,3,
  98,97,122,107,0,3,1,2,3>>

3> Token = branca:encode(Message, Secret).
<<"9GBoip8wFIboItLRutv335YmhKpa4vRX5qXKFoyABy0f8LOw9hk3Zi4I14H2AL9VKk0i6GRentlKXc9qr">>

4> {ok, Message} = branca:decode(Token, Secret).
{ok,<<131,104,4,100,0,3,102,111,111,100,0,3,98,97,114,
      100,0,3,98,97,122,107,0,3,1,2,3>>}
```


## API

### `branca:encode/2`

Uses `Secret` to turn `PlainText` into a Branca token using the current Unix time as the [timestamp]. Returns the token as an Erlang binary.

### `branca:encode/3`

Same as above, but using a custom timestamp. If used, it must be greater than 0 and less than 2^32 (4 bytes long).

### `branca:decode/2`

Uses `Secret` to turn a Branca token into the original `PlainText`. Returns a two-valued tuple for each possible outcome:

- `{ok, PlainText}` -> successful token decryption.
- `{error, bad_encoding}` -> `CipherText` contains at least one non-base62 character.
- `{error, invalid_token}` -> `CipherText` is base62, but it does not have the [layout] of a valid Branca token.
- `{error, invalid_sig}` -> the `Secret` used to decrypt the token is incorrect, or the token has been tampered.

### `branca:decode/3`

Same as above, but using a `TTL` to determine if the token has to be considered stale. Might return any of the above tuples, plus:

- `{expired, PlainText}` -> the token was successfully decrypted, but it expired (i.e. it was minted more than `TTL` seconds ago).


## Testing

The library includes EUnit and PropEr test suites.

These can be run with the usual rebar3 commands (`rebar3 eunit` and `rebar3 proper`).


## Caveats

- Branca mandates the XChaCha20 variant of the ChaCha20-Poly1305 AEAD construction. However, the
  libsodium binding for Erlang is currently built for version 1.0.11 of libsodium, and the XChaCha20 variant
  was not introduced until version 1.0.12. In the meantime branca-erl uses the IETF ChaCha20 variant,
  that differs in the nonce length (12 bytes instead of 24).

- The base62 encoding and decoding is based on an O(n^2) algorithm involving arithmetic division and is _dog slow_.
  On my development laptop encoding 1KB of random data takes about 100ms, and 5KB jumps to 2.5s.
  Future releases might try to mitigate this problem by implementing `branca_transcoder` as a NIF, or replace
  the base62 algorithm altogether (though that would make the tokens incompatible with the Branca spec
  and the ones produced by other implementations).

## TODO

- [X] Travis CI build
- [X] Timestamp expiration
- [ ] Spec annotations for Dialyzer
- [ ] Improve all modules documentation

[Branca specification]: https://github.com/tuupola/branca-spec
[timestamp]: https://github.com/tuupola/branca-spec#timestamp
[layout]: https://github.com/tuupola/branca-spec#token-format