branca-spec

by tuupola

tuupola / branca-spec

Authenticated and encrypted API tokens using modern crypto

144 Stars 4 Forks Last release: Not found 50 Commits 0 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Branca Token

Authenticated and encrypted API tokens using modern crypto.

What?

Branca is a secure easy to use token format which makes it hard to shoot yourself in the foot. It uses IETF XChaCha20-Poly1305 AEAD symmetric encryption to create encrypted and tamperproof tokens. Payload itself is an arbitrary sequence of bytes. You can use for example a JSON object, plain text string or even binary data serialized by MessagePack or Protocol Buffers.

Although not a goal, it is possible to use Branca as an alternative to JWT. Also see getting started instructions.

This specification defines the external format and encryption scheme of the token to help developers create their own implementations. Branca is closely based on Fernet specification.

Design Goals

  1. Secure
  2. Easy to implement
  3. Small token size

Token Format

Branca token consists of header, ciphertext and an authentication tag. Header consists of version, timestamp and nonce. Putting them all together we get following structure.

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

String representation of the above binary token must use base62 encoding with the following character set.

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Version

Version is 8 bits ie. one byte. Currently the only version is

0xBA
. This is a magic byte which you can use to quickly identify a given token. Version number guarantees the token format and encryption algorithm.

Timestamp

Timestamp is 32 bits ie. unsigned big endian 4 byte UNIX timestamp. By having a timestamp instead of expiration time enables the consuming side to decide how long tokens are valid. You cannot accidentally create tokens which are valid for the next 10 years.

Storing timestamp as unsigned integer allows us to avoid 2038 problem. Unsigned integer overflow will happen in the year 2106. Possible values are 0 - 4294967295.

Nonce

Nonce is 192 bits ie. 24 bytes. It should be cryptographically secure random bytes. A nonce should never be used more than once with the same secret key between different payloads. It should be generated automatically by the implementing library. Allowing end user to provide their own nonce is a foot gun.

If you do not trust your systems CSPRNG you can generate a misuse-resistant nonce by hashing the cleartext payload using the CSPRNG generated bytes as a key.

$size = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES;
$random = random_bytes($size);
$nonce = sodium_crypto_generichash($payload, $random, $size);

Ciphertext

Payload is encrypted and authenticated using IETF XChaCha20-Poly1305. Note that this is Authenticated Encryption with Additional Data (AEAD) where the header part of the token is the additional data. This means the data in the header (version, timestamp and nonce) is not encrypted, it is only authenticated. In laymans terms, header can be seen but it cannot be tampered.

Tag

The authentication tag is 128 bits ie. 16 bytes. This is the Poly1305 message authentication code. It is used to make sure that the payload, as well as the non-encrypted header have not been tampered with.

Working With Tokens

Instructions below assume your crypto library supports combined mode. In combined mode the authentication tag and the encrypted message are stored together. If your crypto library does not provide combined mode the

tag
is last 16 bytes of the
ciphertext|tag
combination.

Generating a Token

Given a 256 bit ie. 32 byte secret

key
and an arbitrary
payload
, generate a token with the following steps in order:
  1. Generate a 24 byte cryptographically secure random
    nonce
    .
  2. If user has not provided
    timestamp
    use the current unixtime.
  3. Construct the
    header
    by concatenating
    version
    ,
    timestamp
    and
    nonce
    .
  4. Encrypt the user given payload with IETF XChaCha20-Poly1305 AEAD with user provided secret
    key
    . Use
    header
    as the additional data for AEAD.
  5. Concatenate the
    header
    and the returned
    ciphertext|tag
    combination from step 4.
  6. Base62 encode the entire token.

Verifying a Token

Given a 256 bit ie. 32 byte secret

key
and a
token
to verify that the
token
is valid and recover the original unencrypted
payload
, perform the following steps, in order.
  1. Base62 decode the token.
  2. Make sure the first byte of the decoded token is
    0xBA
    .
  3. Extract the
    header
    ie. the first 29 bytes from the decoded token.
  4. Extract the
    nonce
    ie. the last 24 bytes from the
    header
    .
  5. Extract the
    timestamp
    ie. bytes 2 to 5 from the
    header
    .
  6. Extract
    ciphertext|tag
    combination ie. everything starting from byte 30.
  7. Decrypt and verify the
    ciphertext|tag
    combination with IETF XChaCha20-Poly1305 AEAD using the secret
    key
    and
    nonce
    . Use
    header
    as the additional data for AEAD.

Working With the Timestamp

Optionally the implementing library may use the

timestamp
for additional verification. For example the library might provide a
ttl
parameter which is used to check if token is expired by adding the
ttl
to
timestamp
and comparing the result to the current unixtime.

Libraries

Currently known implementations in the wild.

| Language | Repository | License | Crypto library used | | -------- | ---------- | ------- | ------------------- | | Elixir | tuupola/branca-elixir | MIT | ArteMisc/libsalty | | DotNet | thangchung/branca-dotnet | MIT | NaCl.Core | | DotNet | scottbrady91/IdentityModel | Apache-2.0 | Bouncy Castle | | Erlang | 1ma/branca-erl | MIT | jedisct1/libsodium | | Go | essentialkaos/branca | MIT | golang/crypto | Go | hako/branca | MIT | golang/crypto | Go | juranki/branca | MIT | golang/crypto | Java | Kowalski-IO/branca | MIT | Bouncy Castle | | Java | bjoernw/jbranca | Apache-2.0 | Bouncy Castle | | JavaScript | tuupola/branca-js | MIT | jedisct1/libsodium.js | | Kotlin | petersamokhin/kbranca | Apache-2.0 | Bouncy Castle | | PHP | tuupola/branca-php | MIT | paragonie/sodium_compat | | Python | tuupola/branca-python | MIT | jedisct1/libsodium | | Ruby | crossoverhealth/branca | MIT | RubyCrypto/rbnacl | Rust | return/branca | MIT | brycx/orion

Acceptance Test Vectors

TODO... In the meanwhile see JavaScript and PHP example tests.

Similar Projects

  • PASETO ie. Platform-Agnostic Security Tokens.
  • Fernet which provides AES 128 in CBC mode tokens.

License

The MIT License (MIT). Please see License File for more information.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.