Asherah application-layer encryption for Python with automatic key rotation, powered by the native Rust implementation.
Project description
asherah
Python bindings for the Asherah envelope encryption and automatic key rotation library.
Prebuilt wheels for Python 3.8+ (stable ABI): Linux x64/ARM64 (manylinux + musl), macOS universal2, Windows x64/ARM64.
Installation
pip install asherah
Quick Start (Static API)
The static API manages a global session factory internally. Call setup() once, then encrypt/decrypt with partition-scoped functions.
import os
os.environ["STATIC_MASTER_KEY_HEX"] = "22" * 32 # testing only
import asherah
asherah.setup({
"ServiceName": "my-service",
"ProductID": "my-product",
"Metastore": "memory", # testing only — use "rdbms" or "dynamodb" in production
"KMS": "static", # testing only — use "aws" in production
"EnableSessionCaching": True,
})
ciphertext = asherah.encrypt_string("partition-1", "sensitive data")
plaintext = asherah.decrypt_string("partition-1", ciphertext)
print(plaintext) # "sensitive data"
asherah.shutdown()
Session-Based API
The SessionFactory class reads configuration from environment variables (set them before construction). Each Session is scoped to a partition. Both support context managers.
import os
os.environ["SERVICE_NAME"] = "my-service"
os.environ["PRODUCT_ID"] = "my-product"
os.environ["Metastore"] = "memory" # testing only
os.environ["KMS"] = "static" # testing only
os.environ["STATIC_MASTER_KEY_HEX"] = "22" * 32
os.environ["SESSION_CACHE"] = "1"
import asherah
with asherah.SessionFactory() as factory:
with factory.get_session("partition-1") as session:
ciphertext = session.encrypt_bytes(b"secret")
plaintext = session.decrypt_bytes(ciphertext)
print(plaintext) # b"secret"
# Text variants
ct = session.encrypt_text("hello")
pt = session.decrypt_text(ct)
print(pt) # "hello"
Async API
Async wrappers dispatch to the default thread pool executor via asyncio.run_in_executor. The GIL is released during the native call.
import asyncio
import os
os.environ["STATIC_MASTER_KEY_HEX"] = "22" * 32
import asherah
async def main():
await asherah.setup_async({
"ServiceName": "my-service",
"ProductID": "my-product",
"Metastore": "memory",
"KMS": "static",
})
ciphertext = await asherah.encrypt_string_async("partition-1", "data")
plaintext = await asherah.decrypt_string_async("partition-1", ciphertext)
print(plaintext) # "data"
await asherah.shutdown_async()
asyncio.run(main())
Async Behavior
- The event loop is not blocked -- work runs on a thread pool thread.
- The GIL is released during the native Rust call.
- Overhead: ~37 us vs ~1 us sync (hot cache, 64B payload).
- Best for: I/O-bound asyncio applications that need non-blocking encryption.
For CPU-bound batch encryption, use the sync API directly.
Configuration
The setup() function accepts a dict (or any JSON-serializable object) with PascalCase keys matching the Go canonical API:
| Key | Type | Required | Description |
|---|---|---|---|
ServiceName |
str | Yes | Service identifier for key hierarchy |
ProductID |
str | Yes | Product identifier for key hierarchy |
Metastore |
str | Yes | "rdbms", "dynamodb", "memory" (testing) |
KMS |
str | No | "static" (default) or "aws" |
ConnectionString |
str | Conditional | Required for sqlite and rdbms metastores |
RegionMap |
dict | Conditional | Required for aws KMS. Maps preferred region to ARN. |
PreferredRegion |
str | No | Preferred AWS region for KMS |
EnableRegionSuffix |
bool | No | Append region suffix to system key IDs |
EnableSessionCaching |
bool | No | Enable session caching (default: true) |
SessionCacheMaxSize |
int | No | Max cached sessions |
SessionCacheDuration |
int | No | Cache TTL in seconds |
ExpireAfter |
int | No | Key expiration in seconds |
CheckInterval |
int | No | Revocation check interval in seconds |
DynamoDBEndpoint |
str | No | Custom DynamoDB endpoint URL |
DynamoDBRegion |
str | No | DynamoDB region |
DynamoDBSigningRegion |
str | No | Signing region (overrides DynamoDBRegion) |
DynamoDBTableName |
str | No | DynamoDB table name |
ReplicaReadConsistency |
str | No | DynamoDB read consistency |
SQLMetastoreDBType |
str | No | "mysql" or "postgres" hint for rdbms |
Verbose |
bool | No | Enable verbose logging |
EnableCanaries |
bool | No | Enable canary buffer overflow detection |
NullDataCheck |
bool | No | Enable null data validation |
DisableZeroCopy |
bool | No | Disable zero-copy optimization |
AWS KMS Example
asherah.setup({
"ServiceName": "my-service",
"ProductID": "my-product",
"Metastore": "dynamodb",
"KMS": "aws",
"RegionMap": {
"us-west-2": "arn:aws:kms:us-west-2:123456789012:key/mrk-abc123"
},
"PreferredRegion": "us-west-2",
"DynamoDBTableName": "EncryptionKey",
"EnableSessionCaching": True,
})
Performance
Approximate latency on Apple M4 Max (hot cache, 64-byte payload):
| Operation | Latency |
|---|---|
| Encrypt | ~1,049 ns |
| Decrypt | ~791 ns |
This Rust-backed implementation replaces the Go Cobhan-based canonical asherah PyPI package. Run scripts/benchmark.sh for head-to-head comparisons.
API Reference
Static Functions
| Function | Description |
|---|---|
setup(config) |
Initialize the global session factory from a config dict |
shutdown() |
Shut down the global session factory and release resources |
get_setup_status() |
Returns True if setup() has been called |
encrypt_bytes(partition_id, data) |
Encrypt bytes, returns JSON str (DataRowRecord) |
encrypt_string(partition_id, text) |
Encrypt str, returns JSON str (DataRowRecord) |
decrypt_bytes(partition_id, drr) |
Decrypt JSON DataRowRecord, returns bytes |
decrypt_string(partition_id, drr) |
Decrypt JSON DataRowRecord, returns str |
setenv(env_dict) |
Set environment variables from a dict (both os.environ and Rust) |
set_metrics_hook(callback) |
Register a callback for metrics events, or None to clear |
set_log_hook(callback) |
Register a callback for log events, or None to clear |
version() |
Returns the native library version string |
Async Functions
| Function | Description |
|---|---|
setup_async(config) |
Async version of setup() |
shutdown_async() |
Async version of shutdown() |
encrypt_bytes_async(partition_id, data) |
Async version of encrypt_bytes() |
encrypt_string_async(partition_id, text) |
Async version of encrypt_string() |
decrypt_bytes_async(partition_id, drr) |
Async version of decrypt_bytes() |
decrypt_string_async(partition_id, drr) |
Async version of decrypt_string() |
Classes
SessionFactory
Constructed from environment variables (not a config dict). Supports context manager protocol.
| Method | Description |
|---|---|
SessionFactory() |
Create from env vars |
SessionFactory.from_env() |
Same as constructor |
get_session(partition_id) |
Create a Session for the given partition |
close() |
Release resources |
Session
Scoped to a single partition. Supports context manager protocol.
| Method | Description |
|---|---|
encrypt_bytes(data) |
Encrypt bytes, returns JSON str |
encrypt_text(text) |
Encrypt str, returns JSON str |
decrypt_bytes(drr) |
Decrypt JSON DataRowRecord, returns bytes |
decrypt_text(drr) |
Decrypt JSON DataRowRecord, returns str |
close() |
Release resources |
Hooks
Metrics Hook
def on_metric(event):
# event is a dict with "type" and additional fields
# type: "encrypt", "decrypt", "store", "load" -> has "duration_ns"
# type: "cache_hit", "cache_miss" -> has "name"
print(event)
asherah.set_metrics_hook(on_metric)
asherah.set_metrics_hook(None) # clear
Log Hook
def on_log(record):
# record is a dict with "level", "message", "target"
print(f"[{record['level']}] {record['target']}: {record['message']}")
asherah.set_log_hook(on_log)
asherah.set_log_hook(None) # clear
Cross-Language Compatibility
Ciphertext produced by any Asherah implementation (Go, Node.js, Java, .NET, Ruby) can be decrypted by any other, as long as they share the same metastore and KMS configuration. The DataRowRecord JSON format is the interchange format.
License
Licensed under the Apache License, Version 2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file asherah-0.5.31.tar.gz.
File metadata
- Download URL: asherah-0.5.31.tar.gz
- Upload date:
- Size: 200.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20736d33dfc4abbb6841304f6be4255bd5644caca111d191aff43be4e8112a54
|
|
| MD5 |
04991041646fbde76983b61182e32035
|
|
| BLAKE2b-256 |
1db6707554e71e58dc9e6c854c3eb5172e91531b6b499e668cf798e31c19a474
|
File details
Details for the file asherah-0.5.31-cp38-abi3-win_arm64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-win_arm64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.8+, Windows ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2b2f4ee49e64f73466ed55eb91e23c3915fe08a0c1a9192e341a175977957b4
|
|
| MD5 |
ecd9cd95296f2490bb1e49f0e901b6a6
|
|
| BLAKE2b-256 |
f57ed975efc4d26e4adcf48af14257be324956dcc1ef6c4dd940e6c81b5d7090
|
File details
Details for the file asherah-0.5.31-cp38-abi3-win_amd64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-win_amd64.whl
- Upload date:
- Size: 8.2 MB
- Tags: CPython 3.8+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf3ac7552ebe431df5e78a08e610c1e9d52faa64d016a65fbc0dadf88562752c
|
|
| MD5 |
948044b94b06efa3035c1d5f60908cd4
|
|
| BLAKE2b-256 |
8b9663ed21bee0c67d39f47b37dccb55dcc3115872a0f6a5233c480b8959aa64
|
File details
Details for the file asherah-0.5.31-cp38-abi3-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 13.2 MB
- Tags: CPython 3.8+, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a3babccbbab0152ae756e74f228ae65730908228925e6c28df25a1848e8224a
|
|
| MD5 |
3f6693eb684f8d3c85c3bc00c33047e8
|
|
| BLAKE2b-256 |
e335f545cb08e347d39314d8c2e9641306cc1b0c173548dc964abd8fe1d859a8
|
File details
Details for the file asherah-0.5.31-cp38-abi3-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 13.3 MB
- Tags: CPython 3.8+, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b3f6ddfd3e7827a94eb5e54f64c98d10f8698bd076edcfae22bfeb048a15777
|
|
| MD5 |
99e27ba48b009a96d14c511732ac7062
|
|
| BLAKE2b-256 |
12e22219d252a196dd096df8973c01319756bc1f436311ef629d570266469766
|
File details
Details for the file asherah-0.5.31-cp38-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 12.3 MB
- Tags: CPython 3.8+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abdfa85e90d3c598abd3e5acfa531eb62b6399e08b1fef9fc706b5d29a88b313
|
|
| MD5 |
f5980e118f1ccbc3eeb15d7d6febe355
|
|
| BLAKE2b-256 |
0bfc73403b123c1ad0fb850942532047cb7fdf563bbcbc1c4a1a39d8e8ed0e9d
|
File details
Details for the file asherah-0.5.31-cp38-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 12.8 MB
- Tags: CPython 3.8+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76b4dd1b276314ad216b4b01041d34acd749772728be216e2620f124ac3b0ac2
|
|
| MD5 |
af3b7df2360df6a2ab7ad051225b1477
|
|
| BLAKE2b-256 |
696d0385dd6916c7941a6a327c25080cb29ce83f45970d079a3c5a3d8ce371e1
|
File details
Details for the file asherah-0.5.31-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.
File metadata
- Download URL: asherah-0.5.31-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 19.0 MB
- Tags: CPython 3.8+, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7958220a1d0e9508edb1dc7ce78c778065547d9ad2031a88b47be95d32870bee
|
|
| MD5 |
624f2a5091a4eb3e27d39086ddbdcdfd
|
|
| BLAKE2b-256 |
feda01253ce879a333535f635577cb093ef8cb2ed90442dbd88dc21645647e9a
|