Skip to content
Authenticated and encrypted API tokens using modern crypto, in Java.
Java
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src/main/java/io/kowalski/branca
.gitignore
LICENSE.md
README.md
pom.xml

README.md

Branca Token

Shamelessly stolen from: https://github.com/tuupola/branca-spec.

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.

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy

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.

$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 he 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 This Library

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:

byte[] key = YOUR_32_BYTE_KEY_IMPORTED_FROM_SOMEWHERE;
byte[] payload = SOME_JSON_STRING.getBytes();

Branca.seal(key, payload);

And you'll get back:

2QqxGJgBdaxuHxixEptLvTQ9MGhlvcJ9NcQ2WJxm0WBCT5LbWZGzEMdPzgzapEcvXirvHC6iJp8b5a1C0QFZzLIjpRUaPfpCVMTw6qRhzC5leFdbgwOWMDTQaQgoxB3rOwfkgjc3fHCXDbe9UOXV19r22rd1jmj6iPAaRFl5GL

Verifying a Token

Given that same 256 bit ie. 32 byte secret key and a rawToken:

byte[] key = YOUR_32_BYTE_KEY_IMPORTED_FROM_SOMEWHERE;
byte[] rawToken = BASE64_KEY_FROM_ABOVE.getBytes();

Optional<io.kowalski.branca.Token> token = Branca.open(key, rawToken);

This token class has 3 fields:

  • a nonce which you can use for tracking replay attacks
  • a timestamp which you can use for expiry
  • the original payload

An illegal argument exception is thrown when a token with an invalid Branca version in its header is provided.

Open returns an Optional.empty() when the symmetric key is incorrect or if the token can't be validated (i.e. was tampered with).

License

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

You can’t perform that action at this time.