#aead #cryptography #fernet #xchacha20-poly1305

branca

Authenticated encrypted API tokens for Rust. A secure alternative to JWT.

10 releases (5 breaking)

0.10.1 Feb 1, 2022
0.10.0 Nov 29, 2020
0.9.2 Aug 31, 2020
0.9.1 May 10, 2020
0.1.1 Nov 26, 2018

#99 in Authentication

Download history 9/week @ 2023-12-15 66/week @ 2023-12-22 24/week @ 2023-12-29 28/week @ 2024-01-05 43/week @ 2024-01-12 19/week @ 2024-01-19 32/week @ 2024-01-26 76/week @ 2024-02-02 43/week @ 2024-02-09 57/week @ 2024-02-16 157/week @ 2024-02-23 112/week @ 2024-03-01 75/week @ 2024-03-08 46/week @ 2024-03-15 35/week @ 2024-03-22 72/week @ 2024-03-29

232 downloads per month
Used in 3 crates

MIT license

40KB
556 lines

Branca - A secure alternative token format to JWT

Crate Documentation License CI
Crates.io Docs License CI

Branca is a secure alternative token format to JWT. This implementation is written in pure Rust and uses the XChaCha20-Poly1305 AEAD (Authenticated Encryption with Associated Data) stream cipher for generating authenticated and encrypted tamper-proof tokens. More information about the Branca token specification can be found here in branca-spec.

Security

NOTE: Branca uses Orion for its cryptographic primitives and due to Orion not receiving any formal security audit, the same security risks that Orion has also applies to this Branca implementation if one uses it in production. For a better understanding about the security risks involved, see the Orion wiki.

⚠️ Use at your own risk. ⚠️

Requirements

  • Rust 1.52
  • Cargo

Installation

Add this line into your Cargo.toml under the dependencies section:

[dependencies]
branca = "^0.10.0"
getrandom = "^0.2.3"

Then you can import the crate into your project with these lines:

extern crate getrandom;
extern crate branca;
use branca::{Branca, encode, decode};

Example Usage

The simplest way to use this crate is to use Branca::new() in this example below:

    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!("{}", payload); // "Hello World!"

See more examples of setting fields in the Branca struct and in the Documentation section.

Direct usage without Branca builder.

Encoding:

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

let message = b"Hello world!";
let timestamp = 123206400;
let branca_token = encode(message, &key, timestamp).unwrap();

// branca_token = 875GH233T7.......

Decoding:

let ciphertext = branca_token.as_str();
let ttl = 0; // The ttl can be used to determine if the supplied token has expired or not.
let decoded = decode(ciphertext, &key, ttl);

if decoded.is_err() {
    // Error
} else {
    let msg = decoded.unwrap(); 
    // msg = "Hello world!"
}

Encode/Decode arbitrary data structures with Serde.

Since Branca is able to work with any format of data in the payload, it is possible for the payload to be anything from a JSON object, plaintext, raw bytes, protocol buffers or even a JWT.

Here is an example of using Branca to encode/decode a typical JSON object with serde_json.

Add the following into your Cargo.toml file:

[dependencies]
branca = "^0.10.0"
serde_json = "^1.0"
serde_derive = "1.0.97"

#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate branca;
extern crate getrandom;

use branca::{encode, decode};

#[derive(Serialize, Deserialize, Debug)]
struct User {
    user: String,
    scope: Vec<String>,
}

fn main(){

    let message = json!({
        "user" : "someone@example.com",
        "scope":["read", "write", "delete"],
    }).to_string();

    let mut key = [0u8; 32];
    getrandom::getrandom(&mut key).unwrap();
    
    let mut token = Branca::new(&key).unwrap();
    
    // Encode Message
    let branca_token = token.encode(message.as_bytes()).unwrap();
    
    // Decode Message
    let payload = token.decode(branca_token.as_str(), 0).unwrap();

    let json: User = serde_json::from_str(payload.as_str()).unwrap();

    println!("{}", branca_token);
    println!("{}", payload);
    println!("{:?}", json);
}

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.

The nonce generated must be 24 bytes in length. Keys must be 32 bytes in length.

Building

cargo build

Testing

cargo test

Contributing

Contributions and patches are welcome! Fork this repository, add your changes and send a PR.

Before you send a PR, make sure you run cargo test first to check if your changes pass the tests.

If you would like to fix a bug or add a enhancement, please do so in the issues section and provide a short description about your changes.

License

MIT

Dependencies

~4.5MB
~94K SLoC