Package: menta
mentamentais a secure, simple and easy to use API token format. It usesXChaCha20-Poly1305symmetric 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 bev1.
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 byBrancaand 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.UsageThis repository serves as the reference implementation of Menta (in Python). It
can be used as follows:frommentaimportMenta,TokenData# create a new menta instance with an existing keykey=bytes.fromhex("1df408259cdbba9492c2d01ad4dd942de4047f03ff32515fc6f333627f0e22b8")menta=Menta(key)# encode the text "hi!" into a new tokentoken=menta.encode(TokenData("hi!"))print("encoded:",token)# decode the token we just generateddata=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:frommentaimportMenta# create a new menta instance with a freshly generated keymenta=Menta.generate()print(menta.key.hex())FormatMenta tokens start with a version indicator, followed by abase64urlencoding 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
payloadtimestamp (8 bytes) || payload (? bytes)KeyMenta requires a 256-bit key for use withXChaCha20-Poly1305. These 32 bytes
must be randomly generated using the operating system's CSPRNG.FieldsVersion indicatorEvery Menta token starts with a version indicator:v1:BodyTimestampEvery Menta token contains the time at which it was generated: A Unix timestamp
(seconds elapsed since 00:00:00 UTC on 1 January 1970).PayloadThe payload is a binary blob of arbitrary length. We recommend using a
serialization format likeMessagePackorProtocol Buffersto encode the
payload. If you prefer JSON, use a strict library likePydanticto validate the payload.NonceThe 196-bit nonce used in encryption and decryption of the ciphertext. It must
be randomly generated using the operating system's CSPRNG.Authentication tagThe 128-bit tag used to authenticate the ciphertext.GeneratingTo generate a new token, given a key and payload:Take note of the current time for the timestamp field.Construct the token body.timestamp (64-bit big-endian unsigned integer) || payload (? bytes)Generate a random nonce.Construct the AAD by concatenating the version indicator and the nonce:"v1:" (ASCII) || nonce (24 bytes)Calculate the ciphertext and authentication tag by encrypting the token body
usingXChaCha20-Poly1305. Pass the result of the previous step as AAD.Concatenate the nonce, ciphertext and authentication tag. Encode the result
usingbase64urlas defined inRFC
4648. Strip any
padding from the result that may have been added by the base64 encoding.nonce (24 bytes) || ciphertext (? bytes) || tag (16 bytes)Construct the token by concatenating the version indicator and the result of
the previous step:"v1:" || base64url(nonce || ciphertext || tag)DecodingTo decode a token, given a key:Split the token on the:character into two parts calledversionandbody. Verify that the result of the split is exactly 2 parts.Verify that theversionis equal to exactly"v1".Decode thebodyusingbase64urlas defined inRFC
4648. If the base64
library you're using expects padding, artificially add it beforehand.Split up the decoded body as follows:nonce (24 bytes) | ciphertext (? bytes) | tag (16 bytes)Construct the AAD by concatenating the version indicator and the nonce:"v1:" (ASCII) || nonce (24 bytes)Decrypt the ciphertext usingXChaCha20-Poly1305with the given key, and
the nonce and tag obtained in the previous steps.Deconstruct the resulting plaintext as follows:timestamp (64-bit big-endian unsigned integer) | payload (? bytes)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.SecurityThe 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 onlibsodiumthroughPyNaCl. It does not implement any
cryptographic primitives itself.