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.1.tar.gz (12.8 kB 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.1-cp312-cp312-win_amd64.whl (101.8 kB view details)

Uploaded CPython 3.12Windows x86-64

File details

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

File metadata

  • Download URL: nx_v2_native-2.0.1.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • 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.1.tar.gz
Algorithm Hash digest
SHA256 ed5121566dd2185a0be2addc002c1d39b8f21055d7c0ed3e4c9aa508fe37f2af
MD5 dabf12cfd70abd2f69ca53c266ed18b6
BLAKE2b-256 434e70b537cb381015dd5599c52fc31eb2258f8d49031656b4df13acdaaee8a2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for nx_v2_native-2.0.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 5eb7ad1d33ca8b203e30ada8754cfc153241bc77426b033e3459ff92fb3b874c
MD5 b7d498f408302c6130a5823c1f167e4e
BLAKE2b-256 4dddb77f1a1ca78a51952a23940ffeda1542e4c1079ae81f9ee0c2d2b4d2167b

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