Skip to main content

Secure, simple and easy to use API token format

Project description

menta build coverage Code style: black PyPi version

menta is a secure, simple and easy to use API token format. It uses XChaCha20-Poly1305 symmetric authenticated encryption to create encrypted tokens and to ensure their integrity. There is no asymmetric option, and there is zero cryptographic agility.

Menta is currently experimental. The first stable version will be v1. When it's released, it'll be frozen and it will never be changed. A completely new version will be released if we ever need to make changes or want to add new features. If you start using this library today, you can be certain that it'll still only accept v1 tokens tomorrow. Support for new versions will never be silently introduced into your existing authentication code path.

Menta was inspired by Branca and is very similar to it. There are a couple of differences:

  • The timestamp is included in the ciphertext and thus is not readable to anyone without the key. This makes it less likely that users will trust the timestamp without verifying the token first.
  • The timestamp is a 64-bit unsigned integer. So instead of overflowing in 2106, we'll be good for the next couple of hundred billion years.
  • Base64 instead of base62 for better library support across programming languages.

Usage

This repository serves as the reference implementation of Menta (in Python). It can be used as follows:

from menta import Menta, TokenData

# create a new menta instance with an existing key
key = bytes.fromhex("1df408259cdbba9492c2d01ad4dd942de4047f03ff32515fc6f333627f0e22b8")
menta = Menta(key)

# encode the text "hi!" into a new token
token = menta.encode(TokenData("hi!"))
print("encoded:", token)

# decode the token we just generated
data = menta.decode(token)
print("decoded:", data)

# encoded: v1:uhViDSxQNyaSd0BjXPqgmT53N6t2uSwC3KzxhMEsGis00pSgcqmfaLlhkAFJIun8mZCH
# decoded: TokenData(payload=b'hi!', timestamp=1653137637)

There's also a utility for generating a new key:

from menta import Menta

# create a new menta instance with a freshly generated key
menta = Menta.generate()
print(menta.key.hex())

Format

Menta tokens start with a version indicator, followed by a base64url encoding of a concatenation of the nonce, the ciphertext and authentication tag:

"v1:" || nonce (24 bytes) || body ciphertext (? bytes) || tag (16 bytes)

The contents of the body consists of a concatenation of a timestamp and the payload

timestamp (8 bytes) || payload (? bytes)

Key

Menta requires a 256-bit key for use with XChaCha20-Poly1305. These 32 bytes must be randomly generated using the operating system's CSPRNG.

Fields

Version indicator

Every Menta token starts with a version indicator: v1:

Body

Timestamp

Every Menta token contains the time at which it was generated: A Unix timestamp (seconds elapsed since 00:00:00 UTC on 1 January 1970).

Payload

The payload is a binary blob of arbitrary length. We recommend using a serialization format like MessagePack or Protocol Buffers to encode the payload. If you prefer JSON, use a strict library like Pydantic to validate the payload.

Nonce

The 196-bit nonce used in encryption and decryption of the ciphertext. It must be randomly generated using the operating system's CSPRNG.

Authentication tag

The 128-bit tag used to authenticate the ciphertext.

Generating

To generate a new token, given a key and payload:

  1. Take note of the current time for the timestamp field.

  2. Construct the token body.

    timestamp (64-bit big-endian unsigned integer) || payload (? bytes)
    
  3. Generate a random nonce.

  4. Construct the AAD by concatenating the version indicator and the nonce:

    "v1:" (ASCII) || nonce (24 bytes)
    
  5. Calculate the ciphertext and authentication tag by encrypting the token body using XChaCha20-Poly1305. Pass the result of the previous step as AAD.

  6. Concatenate the nonce, ciphertext and authentication tag. Encode the result using base64url as defined in RFC 4648. Strip any padding from the result that may have been added by the base64 encoding.

    nonce (24 bytes) || ciphertext (? bytes) || tag (16 bytes)
    
  7. Construct the token by concatenating the version indicator and the result of the previous step:

    "v1:" || base64url(nonce || ciphertext || tag)
    

Decoding

To decode a token, given a key:

  1. Split the token on the : character into two parts called version and body. Verify that the result of the split is exactly 2 parts.

  2. Verify that the version is equal to exactly "v1".

  3. Decode the body using base64url as defined in RFC 4648. If the base64 library you're using expects padding, artificially add it beforehand.

  4. Split up the decoded body as follows:

    nonce (24 bytes) | ciphertext (? bytes) | tag (16 bytes)
    
  5. Construct the AAD by concatenating the version indicator and the nonce:

    "v1:" (ASCII) || nonce (24 bytes)
    
  6. Decrypt the ciphertext using XChaCha20-Poly1305 with the given key, and the nonce and tag obtained in the previous steps.

  7. Deconstruct the resulting plaintext as follows:

    timestamp (64-bit big-endian unsigned integer) | payload (? bytes)
    
  8. Optionally, if the user has configured a TTL, verify that the token hasn't expired by adding the TTL to the timestamp and compare the result with the current time.

Security

The format and implementation have not undergone a third-party security audit. The goal is to keep Menta so simple that you can confidently say: "I trust menta, because I trust XChaCha20-Poly1305".

Menta depends on libsodium through PyNaCl. It does not implement any cryptographic primitives itself.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

menta-0.0.1a2.tar.gz (8.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

menta-0.0.1a2-py3-none-any.whl (7.4 kB view details)

Uploaded Python 3

File details

Details for the file menta-0.0.1a2.tar.gz.

File metadata

  • Download URL: menta-0.0.1a2.tar.gz
  • Upload date:
  • Size: 8.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.12 CPython/3.9.12 Linux/5.10.115

File hashes

Hashes for menta-0.0.1a2.tar.gz
Algorithm Hash digest
SHA256 646660ac7a2b9762291e5b5d9a0fcb485db95f469f7388ae6ae3bcfb3fde99e0
MD5 218eaa3d21b87298c37d32b84caeddbc
BLAKE2b-256 55b830366e9ce589e8170506caf5832c09b532f5fa9e2aa3cead9b1ce4a0962a

See more details on using hashes here.

File details

Details for the file menta-0.0.1a2-py3-none-any.whl.

File metadata

  • Download URL: menta-0.0.1a2-py3-none-any.whl
  • Upload date:
  • Size: 7.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.12 CPython/3.9.12 Linux/5.10.115

File hashes

Hashes for menta-0.0.1a2-py3-none-any.whl
Algorithm Hash digest
SHA256 f95319cfaf2f91e5d148ba8db9af0c8a995012899a099688d0b51d9d93902de6
MD5 9e1fb046897f427ff9971a907f8af857
BLAKE2b-256 452b44f6a9bba46cb370ac8afd36f68c6d819af8d4210dd4ed6b1ea9511e2f0d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page