Expand description

Branca - Authenticated and Encrypted API tokens using modern cryptography.

This crate is a pure-Rust implementation of Branca which allows generating authenticated and encrypted tamper-proof tokens. The Branca specification is based on the Fernet specification and is also similar in its token format but it differs from the cipher that it uses for encryption and decryption and the encoding format of the token. Branca uses IETF XChaCha20-Poly1305 AEAD for encryption and decryption and uses Base62 instead of Base64 for encoding the tokens to be URL safe.

A Branca token is encrypted then encoded into Base62, and looks like this:

875GH233T7IYrxtgXxlQBYiFobZMQdHAT51vChKsAIYCFxZtL1evV54vYqLyZtQ0ekPHt8kJHQp0a

The internal format of a Branca token looks like this:

Version (1B) || Timestamp (4B) || Nonce (24B) || Ciphertext (*B) || Tag (16B)

The payload/ciphertext size can be of any arbitrary length, this means that contents of the payload can be anything from Text, JSON, MessagePacks, JWTs, URLs, etc. This allows a Branca Token to be a secure alternative to JWT, since it is authenticated and encrypted by default and supports only one cipher as the standard; unlike JWT.

This blog post explains in more detail on using Branca tokens as an alternative to JWTs.

Also see: branca-spec for more information about the token specification.

Examples

A straightforward example of generating these tokens using the Branca::new() builder:

extern crate branca;
extern crate getrandom;

use branca::Branca;

fn main() {
    let mut key = [0u8; 32];
    getrandom::getrandom(&mut key).unwrap();

    let mut token = Branca::new(&key).unwrap();
    let ciphertext = token.encode(b"Hello World!").unwrap();

    let payload = token.decode(ciphertext.as_str(), 0).unwrap();
    println!("{}", String::from_utf8(payload).unwrap()); // "Hello World!"
}

You can also decide to set the other fields in the token before encoding it if you want to since this is a builder method.

extern crate branca;
extern crate getrandom;

use branca::Branca;

fn main() {
    let mut key = [0u8; 32];
    getrandom::getrandom(&mut key).unwrap();

    let mut token = Branca::new(&key).unwrap();

    // You are able to use the builder to set the timestamp, ttL and the key.
    // However, the nonce cannot be set, as that is a security risk for
    // nonce-reuse misuse.

    // All properties inside of the token can be retrieved.

    let ciphertext = token
                      .set_timestamp(1234567890)
                      .set_key(key.to_vec())
                      .set_ttl(300);
                      //.encode(b"Hello World!").unwrap();

    let timestamp = token.timestamp(); // 1234567890
}

It is also possible to directly encode and decode functions without using the builder function.

Please note that Branca uses Orion to generate secure random nonces when using the encode() and builder methods. By default, Branca does not allow setting the nonce directly since that there is a risk that it can be reused by the user which is a foot-gun.

extern crate branca;
extern crate getrandom;

use branca::{encode, decode};

let mut key = [0u8; 32];
getrandom::getrandom(&mut key).unwrap();

let token = encode(b"Hello World!", &key, 123206400).unwrap();
// token = "875G...p0a"

let ttl = 3600;
// The token will expire at timestamp + ttl
let payload = decode(token.as_str(), &key, 0).unwrap();

println!("{}", String::from_utf8(payload).unwrap());
// payload = "Hello World!"

Modules

Structs

The Branca struct defines the structure of a Branca token for encoding and decoding.

Functions

Decodes a Branca token and returns the plaintext as a String.

Encodes the message and returns a Branca Token as a String.