Skip to main content

Native cryptographic library for authenticated encryption and token management

Project description

nx.v2-native

A high-performance native Python library for authenticated encryption, key management, and token-based secret handling. All cryptographic operations run in C++ via OpenSSL.

Features

  • ChaCha20-Poly1305 authenticated encryption with AAD
  • Deterministic encryption (SIV-style) for indexable ciphertext
  • Key derivation via scrypt (password) and HKDF-SHA256 (subkeys)
  • Token format -- self-contained, versioned, base64url-encoded binary tokens
  • Key rotation -- KeyRing with multiple keys + seamless migration
  • Replay protection -- built-in TTL-based replay guard
  • Cross-compatible -- tokens produced here can be decrypted by the Node.js nx.v2-native package and vice versa

Install

pip install nx.v2-native

Quick Start

from nx import generate_key, encrypt, decrypt, KeyRing

# Create a key ring and add a key
ring = KeyRing()
ring.add("key-2026", generate_key())

# Encrypt
token = encrypt("sensitive data", key_ring=ring)
# -> "nx.v2.AgAHa2V5LTIwMjY..."

# Decrypt
result = decrypt(token, key_ring=ring)
print(result["data"])  # "sensitive data"

API

Encryption & Decryption

encrypt(plaintext, *, key_ring, aad=None, metadata=None)

Encrypts a value and returns an nx.v2. token string.

  • plaintext -- str
  • key_ring -- KeyRing instance (required)
  • aad -- additional authenticated data (str, dict, or None)
  • metadata -- dict of string key-value pairs embedded in the token
token = encrypt("hello", key_ring=ring, aad="user:123", metadata={"purpose": "greeting"})

encrypt_deterministic(plaintext, *, key_ring, aad=None, metadata=None)

Same as encrypt, but produces identical tokens for identical inputs. Useful for indexing or deduplication.

a = encrypt_deterministic("hello", key_ring=ring)
b = encrypt_deterministic("hello", key_ring=ring)
assert a == b

decrypt(token, *, key_ring=None, global_decryptor=None, aad=None)

Decrypts a token. Raises RuntimeError on failure.

Returns:

{
    "data": "hello",
    "metadata": {},
    "key_id": "key-2026",
    "deterministic": False,
}

Key Management

generate_key()

Returns a cryptographically random 32-byte bytes object.

key = generate_key()

derive_key_from_password(password, salt_hex="", N=131072, r=8, p=1)

Derives a key from a password using scrypt.

result = derive_key_from_password("my-password")
key = result["key"]
salt = result["salt_hex"]

# Deterministic re-derivation
same = derive_key_from_password("my-password", salt_hex=salt)
assert result["key"] == same["key"]

derive_subkey(master_key, context, salt=None)

Derives a subkey using HKDF-SHA256.

master = generate_key()
db_key = derive_subkey(master, "database")
api_key = derive_subkey(master, "api-tokens")

KeyRing

Manages multiple encryption keys with a designated current key.

from nx import KeyRing, generate_key

ring = KeyRing()
ring.add("v1", generate_key())
ring.add("v2", generate_key())
ring.set_current("v2")

ring.current_id          # "v2"
ring.size                # 2
ring.has("v1")           # True
ring.resolve("v1")       # bytes
ring.key_ids             # ["v2", "v1"] -- current first
ring.remove("v1")        # removes key
ring.wipe_all()          # clears all keys

GlobalDecryptor

Resolves keys across multiple KeyRings. Useful for multi-tenant or multi-service decryption.

from nx import GlobalDecryptor

service_a = KeyRing()
service_a.add("a-key", generate_key())

service_b = KeyRing()
service_b.add("b-key", generate_key())

gd = GlobalDecryptor()
gd.add_ring(service_a)
gd.add_ring(service_b)

# Decrypts tokens from either service
result = decrypt(token, global_decryptor=gd)

ReplayGuard

Prevents token reuse with an in-memory TTL store.

from nx import ReplayGuard

guard = ReplayGuard(ttl_seconds=300)  # 5 minutes

# First use -- succeeds
result = guard.decrypt(token, key_ring=ring)

# Second use -- raises "nx: token has already been used."
guard.decrypt(token, key_ring=ring)

Custom store (e.g. Redis):

class RedisStore:
    def __init__(self, client):
        self.r = client

    def has(self, key):
        return self.r.exists(key)

    def add(self, key, ttl_seconds):
        self.r.set(key, "1", ex=int(ttl_seconds))

guard = ReplayGuard(store=RedisStore(redis_client))

Key Rotation

migrate(token, *, key_ring, aad=None, metadata=None)

Re-encrypts a token under the current key if it was encrypted with an older key. Returns the original token unchanged if already current.

ring = KeyRing()
ring.add("v1", old_key)
ring.add("v2", new_key)
ring.set_current("v2")

migrated = migrate(old_token, key_ring=ring)
# migrated is now encrypted under "v2"

Utilities

peek_key_id(token)

Extract the key ID from a token without decrypting. Returns None on invalid tokens.

key_id = peek_key_id(token)  # "my-key"

decode_token(token)

Decode a token into its raw components. Returns None on invalid tokens.

parsed = decode_token(token)
# {
#     "version": 2,
#     "flags": 0,
#     "deterministic": False,
#     "key_id": "my-key",
#     "nonce": b"...",
#     "tag": b"...",
#     "ciphertext": b"...",
#     "metadata": {},
# }

token_fingerprint(token)

Get the fingerprint (keyId:nonceHex) of a token. Returns None on invalid tokens.

wipe_buffer(buf)

Securely zero a writable buffer using OpenSSL's OPENSSL_cleanse.

key = bytearray(generate_key())
wipe_buffer(key)  # all bytes are now 0x00

Token Format

Tokens are prefixed with nx.v2. followed by a base64url-encoded binary payload:

Field Size Description
version 1 byte 0x02
flags 1 byte 0x01 = deterministic
keyId length 1 byte 1--255
keyId variable UTF-8 encoded
nonce 12 bytes Random or SIV-derived
auth tag 16 bytes Poly1305 tag
metadata length 2 bytes Big-endian uint16
metadata variable Canonical JSON (sorted keys)
ciphertext remaining Encrypted payload

Cross-Compatibility

Tokens produced by this Python library are fully compatible with the Node.js nx.v2-native package. You can encrypt in Python and decrypt in Node.js, or vice versa.

# Python
token = encrypt("hello from python", key_ring=ring)
// Node.js
const result = decrypt(token, { keyRing: ring });
console.log(result.data); // "hello from python"

Security

  • ChaCha20-Poly1305 AEAD with 12-byte nonce and 16-byte authentication tag
  • HMAC-SHA256 for deterministic (SIV) nonce derivation
  • scrypt for password-based key derivation with configurable cost
  • HKDF-SHA256 for domain-separated subkey derivation
  • OPENSSL_cleanse for secure memory wiping
  • AAD is cryptographically bound -- tampering with keyId, metadata, or context is detected
  • All crypto runs in compiled C++ via OpenSSL -- no Python crypto code

Platform Support

Platform Architecture Status
Windows x64 Prebuilt
macOS arm64 Coming soon
macOS x64 Coming soon
Linux x64 Coming soon

Requirements

  • Python >= 3.10

License

MIT

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

nx_v2_native-2.0.2.tar.gz (2.9 MB view details)

Uploaded Source

Built Distribution

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

nx_v2_native-2.0.2-cp312-cp312-win_amd64.whl (2.9 MB view details)

Uploaded CPython 3.12Windows x86-64

File details

Details for the file nx_v2_native-2.0.2.tar.gz.

File metadata

  • Download URL: nx_v2_native-2.0.2.tar.gz
  • Upload date:
  • Size: 2.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for nx_v2_native-2.0.2.tar.gz
Algorithm Hash digest
SHA256 8b269d51ee0a5a43c9548f31926a0699c293efe8aa9b9e501a116fd044955e64
MD5 b444e5d8696d3738b9dcb2d40f63faf2
BLAKE2b-256 fa192e7bd60ff90d9dbf51ab4d2171f50e0e66b6448f629515743507d7222cb0

See more details on using hashes here.

File details

Details for the file nx_v2_native-2.0.2-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for nx_v2_native-2.0.2-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 f86aa763f8ab8b3cf8f6954a1547df86f04dc13bd396734c54d9297364d35f9f
MD5 41b27d1b050eae5d10f0c6f9af1ea719
BLAKE2b-256 cdb29e229508c4aa2268e56ee3d05c3c3e3de8523abf5fbed54a548cdeaf77da

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