Skip to main content

Deniable encryption — AES-256 with plausible deniability via control files

Project description

deny-sh — Python SDK

Deniable encryption for Python. Same algorithm as the TypeScript reference implementation. Ciphertext is byte-for-byte compatible across SDKs.

Install

pip install deny-sh

Quick Start

from deny_sh import encrypt, decrypt, generate_deniable_control

# Encrypt a secret message
ciphertext, control = encrypt(b"seed phrase: abandon ability ...", "pw1", "pw2")

# Decrypt with the real control data
message = decrypt(ciphertext, "pw1", "pw2", control)
# b"seed phrase: abandon ability ..."

# Generate deniable control data — same ciphertext decrypts to a decoy
fake_control = generate_deniable_control(ciphertext, "pw1", "pw2", b"nothing here")
decoy = decrypt(ciphertext, "pw1", "pw2", fake_control)
# b"nothing here"

How It Works

  1. Dual-password key derivation — SHA-256(pw1) || SHA-256(pw2) → scrypt → AES-256 key
  2. XOR deniability layer — plaintext is XOR'd with control data before encryption
  3. AES-256-CTR encryption — standard authenticated encryption
  4. Length prefix inside encrypted zone — no metadata leaks the real message size

The control data is what makes it deniable. Different control data + same ciphertext + same passwords = different plaintext. Both the real and fake control data look like random bytes — there's no way to tell which is "real".

API

encrypt(plaintext, password1, password2, control_data=None)

Encrypt plaintext with dual passwords and optional control data.

  • plaintext (bytes) — data to encrypt
  • password1 (str) — first password
  • password2 (str) — second password
  • control_data (bytes | None) — control file data; auto-generated if None

Returns (ciphertext: bytes, control_data: bytes).

decrypt(ciphertext, password1, password2, control_data)

Decrypt ciphertext with dual passwords and control data.

Returns bytes — the decrypted plaintext.

generate_deniable_control(ciphertext, password1, password2, desired_plaintext)

Generate new control data that makes existing ciphertext decrypt to a different message.

Returns bytes — new control data.

generate_control_data(size)

Generate cryptographically secure random control data.

Returns bytes.

derive_key(password1, password2, salt)

Derive the AES-256 key from two passwords and a salt. Exposed for cross-implementation testing.

Returns bytes (32 bytes).

Algorithm Compatibility

The ciphertext format is identical across all deny-sh SDKs:

salt (32 bytes) | iv (16 bytes) | AES-256-CTR(key, payload XOR control_data)

Where payload = length_prefix (4 bytes LE) + plaintext.

KDF parameters: scrypt(N=16384, r=8, p=1, dklen=32).

A file encrypted with the Python SDK can be decrypted with the TypeScript SDK, and vice versa.

Dependencies

  • pycryptodome — AES-256-CTR encryption
  • hashlib (stdlib) — SHA-256 and scrypt KDF
  • os (stdlib) — urandom for secure random bytes

License

AGPL-3.0. Commercial licenses available.

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

deny_sh-1.0.0.tar.gz (20.6 kB view details)

Uploaded Source

Built Distribution

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

deny_sh-1.0.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file deny_sh-1.0.0.tar.gz.

File metadata

  • Download URL: deny_sh-1.0.0.tar.gz
  • Upload date:
  • Size: 20.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for deny_sh-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2818c3b2c822fba66f97ee4b08f6b9dc418dc10e9f9b3b36a83eeedc890f9a73
MD5 4203af87f42641b1b98630ed41c2a6d5
BLAKE2b-256 6b3c8cb98a1967c63f560571f1e96699e9919eb5986676381973d1433da943ad

See more details on using hashes here.

Provenance

The following attestation bundles were made for deny_sh-1.0.0.tar.gz:

Publisher: publish.yml on deny-sh-crypto/deny-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file deny_sh-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: deny_sh-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for deny_sh-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 61f18fa273a1d64894747aba2475737204d43254795d5aa30150962c6ce01905
MD5 2762f8c4ac20ad36ad2adf7931e6310b
BLAKE2b-256 4413b0e25ed51a59d48c390676997abb3a03f2e23f57dc6984b43fe133e7e052

See more details on using hashes here.

Provenance

The following attestation bundles were made for deny_sh-1.0.0-py3-none-any.whl:

Publisher: publish.yml on deny-sh-crypto/deny-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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