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

Apache License 2.0. See LICENSE. Free for commercial and proprietary use. See deny.sh/licensing.

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.1.0.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.

deny_sh-1.1.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: deny_sh-1.1.0.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for deny_sh-1.1.0.tar.gz
Algorithm Hash digest
SHA256 540c0fa571e70f1ce243cb24c9f700fef439fb3e5b406cdb44a7a66bdb8c62e8
MD5 54eb2cb01bd22726dd5e42e43097e368
BLAKE2b-256 d735afe22106d1537557b475330c5e5d79f08fe8201f055c02cc3a5edc9e706a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: deny_sh-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for deny_sh-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac7bde702778db03f1f1cdee92e9b009bb763d34f6a945361b96395adc96f107
MD5 9361f33a8797517733a74d7e8d86146a
BLAKE2b-256 c641b7e09007d687dc4f67553e7f9375691eb85194daf5704063d57f72e2afa6

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