3ncr.org is a standard for string encryption / decryption (algorithms + storage format), originally intended for encrypting tokens in configuration files but usable for any UTF-8 string. v1 uses AES-256-GCM for authenticated encryption with a 12-byte random IV:
3ncr.org/1#<base64(iv[12] || ciphertext || tag[16])>
Encrypted values look like
3ncr.org/1#pHRufQld0SajqjHx+FmLMcORfNQi1d674ziOPpG52hqW5+0zfJD91hjXsBsvULVtB017mEghGy3Ohj+GgQY5MQ.
This is the official Python implementation.
pip install tokencryptRequires Python 3.9+.
Pick a constructor based on the entropy of your secret — see the 3ncr.org v1 KDF guidance for the canonical recommendation.
If you already have a 32-byte AES-256 key, skip the KDF and pass it directly.
import os
from tokencrypt import TokenCrypt
key = os.urandom(32) # or load from an env variable / secret store
tc = TokenCrypt.from_raw_key(key)For a high-entropy secret that is not already 32 bytes (e.g. a random API token), hash it through SHA3-256:
tc = TokenCrypt.from_sha3("some-high-entropy-api-token")For passwords or passphrases, use TokenCrypt.from_argon2id. It uses the
parameters recommended by the 3ncr.org v1 spec
(m=19456 KiB, t=2, p=1). The salt must be at least 16 bytes.
from tokencrypt import TokenCrypt
tc = TokenCrypt.from_argon2id("correct horse battery staple", b"0123456789abcdef")This library does not implement the legacy PBKDF2-SHA3 KDF that earlier 3ncr.org
libraries (Go, Node.js, PHP, Java) shipped for backward compatibility. If you
need to decrypt data produced by that KDF, derive the 32-byte key with
hashlib.pbkdf2_hmac("sha3_256", ...) yourself and pass it to from_raw_key.
plaintext = "08019215-B205-4416-B2FB-132962F9952F"
encrypted = tc.encrypt_3ncr(plaintext)
# e.g. "3ncr.org/1#pHRu..."
tc.decrypt_if_3ncr(encrypted) # -> plaintextdecrypt_if_3ncr returns the input unchanged when it does not start with the
3ncr.org/1# header. This makes it safe to route every configuration value
through it regardless of whether it was encrypted.
Decryption failures (bad tag, truncated input, malformed base64) raise
tokencrypt.TokenCryptError.
This implementation decrypts the canonical v1 envelope test vectors shared with
the Go,
Node.js, and
PHP reference libraries. The 32-byte
AES key behind those vectors was originally derived via PBKDF2-SHA3-256 with
secret = "a", salt = "b", iterations = 1000; the tests hardcode the
resulting key and verify the AES-256-GCM envelope round-trips exactly. See
tests/test_tokencrypt.py.
MIT — see LICENSE.