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

- 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