dhcrypt
OpenSSL-interoperable Finite-Field Diffie-Hellman (FFDH) key exchange and AES-256-GCM authenticated encryption in Python.
Features
- OpenSSL Interoperability: Seamlessly exchanges keys with
openssl genpkey -paramfile,openssl pkeyutl -derive, and standard OpenSSL 3.x / legacy 1.x workflows. - PKCS#3 ASN.1 Engine: Built-in parser that correctly handles optional
privateValueLengthfields fromopenssl genpkey -genparam. - Authenticated Encryption: AES-256-GCM authenticated cipher preventing unauthorized tampering and ciphertext manipulation.
- Robust Key Derivation: HKDF-SHA256 context-bound symmetric key derivation.
- Active Subgroup Protection: Miller-Rabin safe-prime subgroup checks ($y^q \equiv 1 \pmod p$) guarding against small-subgroup confinement attacks.
- Secure File Permissions: Enforces restrictive
0600POSIX file permissions for private keys by default. - Zero-Dependency CLI & Library: Run directly as a CLI tool or import into any Python project.
Installation
Using pip
pip install dhcrypt
Using Poetry
poetry add dhcrypt
Quick Start (CLI)
Perform a complete Diffie-Hellman key exchange and encrypted communication in five commands:
# 1. Generate DH group parameters
dhcrypt genparams --bits 2048 -o dhp.pem
# 2. Alice and Bob generate keypairs
dhcrypt keygen -p dhp.pem --priv alice.key --pub alice.pub
dhcrypt keygen -p dhp.pem --priv bob.key --pub bob.pub
# 3. Derive shared secret (or encrypt directly using peer keys)
dhcrypt derive --priv alice.key --peer bob.pub --fingerprint -o secret.bin
# 4. Encrypt message
dhcrypt encrypt -k secret.bin -p message.txt -o message.enc
# 5. Decrypt message
dhcrypt decrypt -k secret.bin -p message.enc -o recovered.txt
Command-Line Interface Reference
usage: dhcrypt [-h] {genparams,keygen,derive,encrypt,decrypt} ...
genparams
Generate safe-prime Diffie-Hellman parameters serialized in PKCS#3 PEM format.
dhcrypt genparams [--bits BITS] [-o OUT]
--bits: Bit length of the prime modulus (default:2048).-o,--out: Output path (-for stdout, default:-).
keygen
Generate a private key (PKCS#8 format, mode 0600) and public key (SPKI format) from group parameters.
dhcrypt keygen -p PARAMS --priv PRIV --pub PUB [--private-length BITS] [--force]
-p,--params: Path to DH parameters PEM (or any DH key PEM carrying parameters).--priv: Output path for the private key (mode 0600).--pub: Output path for the public key.--private-length: Exponent bit length (e.g.,256for short exponent generation).--force: Overwrite existing private key file if present.
derive
Derive the raw shared secret between our private key and a peer's public key.
dhcrypt derive --priv PRIV --peer PEER [-o OUT] [--hex] [--fingerprint] [--strip-zeros]
--priv: Path to our private key PEM.--peer: Path to peer's public key PEM.-o,--out: Output path (-for stdout).--hex: Encode secret as hexadecimal text.--fingerprint: Output SHA-256 fingerprint of the shared secret to stderr.--strip-zeros: Strip leading zero bytes (reproduces legacy OpenSSL 1.x /DH_compute_keybehavior).
encrypt
Encrypt a payload using AES-256-GCM.
# Using derived secret file:
dhcrypt encrypt -k SECRET -p PAYLOAD [-o OUT]
# Or deriving directly from keys:
dhcrypt encrypt --priv PRIV --peer PEER -p PAYLOAD [-o OUT]
-k,--pem: Path to file containing the shared secret (raw bytes or hex).--privand--peer: Derive secret directly using our private key and peer's public key.-p,--payload: Path to input plaintext (-for stdin).-o,--out: Path to output ciphertext (-for stdout).
decrypt
Verify authentication tag and decrypt an AES-256-GCM ciphertext payload.
# Using derived secret file:
dhcrypt decrypt -k SECRET -p PAYLOAD [-o OUT]
# Or deriving directly from keys:
dhcrypt decrypt --priv PRIV --peer PEER -p PAYLOAD [-o OUT]
Python API Usage
dhcrypt can be imported and integrated directly into Python applications:
from dhcrypt import (
generate_parameters_pem,
generate_keypair_pem,
derive_shared_secret,
encrypt_message,
decrypt_message,
)
# 1. Generate DH group parameters
params_pem = generate_parameters_pem(key_size=2048)
# 2. Parties generate keypairs
alice_priv, alice_pub, alice_bits = generate_keypair_pem(params_pem)
bob_priv, bob_pub, bob_bits = generate_keypair_pem(params_pem)
# 3. Derive identical shared secrets
secret_alice = derive_shared_secret(alice_priv, bob_pub)
secret_bob = derive_shared_secret(bob_priv, alice_pub)
assert secret_alice == secret_bob
# 4. Encrypt and decrypt payloads
plaintext = b"Encrypted transmission over secure channel."
ciphertext = encrypt_message(secret_alice, plaintext)
decrypted = decrypt_message(secret_bob, ciphertext)
assert decrypted == plaintext
Low-Level Primitives
from dhcrypt import (
DHParams,
compute_shared_secret,
derive_symmetric_key,
encrypt_payload,
decrypt_payload,
validate_peer_public,
)
params = DHParams(p=0xFFFFFFFFFFFFFFFF, g=2)
validate_peer_public(peer_public_int, params)
raw_secret = compute_shared_secret(private_int, peer_public_int, params)
# HKDF key derivation & AES-GCM
key = derive_symmetric_key(raw_secret, salt=b"custom-salt", info=b"app-context")
encrypted = encrypt_payload(key, b"Sensitive data")
decrypted = decrypt_payload(key, encrypted)
Security & Wire Format Specifications
- Wire Format:
Ciphertext payloads output by
encryptuse the format: $$\text{Payload} = \text{Nonce}{12\text{ bytes}} \parallel \text{Ciphertext} \parallel \text{Tag}{16\text{ bytes}}$$ - Key Derivation:
Shared secrets derived from Diffie-Hellman exchange are passed through HKDF-SHA256 (
info=b"stompzone-dh-kex-context") to derive a 256-bit symmetric encryption key. - Subgroup Validation: Peer public values are strictly validated against $[2, p - 2]$. For safe-prime groups where generator $g$ generates an order-$q$ subgroup, $y^q \equiv 1 \pmod p$ is enforced to prevent small-subgroup confinement attacks.
- OpenSSL Interoperability:
Handles PKCS#3 ASN.1 structures with optional
privateValueLengthparameter attributes that cause errors in naive OpenSSL/cryptography integrations.
Test Suite & Quality Assurance
Run the test suite with coverage reporting:
poetry run pytest
Test Coverage Summary
Name Stmts Miss Cover
----------------------------------------------
dhcrypt\__init__.py 14 0 100%
dhcrypt\__main__.py 2 0 100%
dhcrypt\cli.py 89 0 100%
dhcrypt\constants.py 6 0 100%
dhcrypt\dh.py 33 0 100%
dhcrypt\exceptions.py 6 0 100%
dhcrypt\io.py 45 0 100%
dhcrypt\math.py 52 0 100%
dhcrypt\models.py 18 0 100%
dhcrypt\serialization.py 97 0 100%
dhcrypt\service.py 27 0 100%
dhcrypt\symmetric.py 44 0 100%
----------------------------------------------
TOTAL 433 0 100%
License
MIT License. Copyright (c) 2026 DJ Stomp.
Metadata
Release files for dhcrypt 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dhcrypt-1.0.0.tar.gz | 13.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dhcrypt-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.9 kB
Release files / dhcrypt-1.0.0.tar.gz
| Download URL | dhcrypt-1.0.0.tar.gz |
|---|---|
| Size | 13.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ec95e1e277011be0e567b9ba435053871bc089d9b6dc5d97376c59aca1481088
|
|
BLAKE2b-256 checksum How to use checksums |
0907bda903c2f989b5c95a9614c69a655df069e61fc6f2bdd4792626d4c80e50
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.4 CPython/3.13.0 Windows/11
|
Release files / dhcrypt-1.0.0-py3-none-any.whl
| Download URL | dhcrypt-1.0.0-py3-none-any.whl |
|---|---|
| Size | 18.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
833af9403e64926554ac4d726f60f2ae53661363a45f9a4987206ddc5868baeb
|
|
BLAKE2b-256 checksum How to use checksums |
b7cd0dd4dfabe45a9b9f94fb585147b8f8ecf78ca2d263505c9002e437901586
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.4 CPython/3.13.0 Windows/11
|