pycryptodomex review
pycryptodomex 3.23.0 is the isolated-namespace distribution of PyCryptodome. Code imports it as `Cryptodome`, avoiding the `Crypto` package name used by the sibling `pycryptodome` distribution and the abandoned PyCrypto project. It provides low-level symmetric and public-key ciphers, authenticated modes, hashes, MACs, key derivation, random bytes, and key-file parsing. Version 3.23.0 adds AES Key Wrap and Key Wrap with Padding, publishes Windows ARM wheels, and fixes HashEdDSA plus Ed448 operations that changed XOF state. Our wheel had zero direct dependencies, included compiled extensions and `py.typed`, and imported `Cryptodome` in 0.01 seconds.
pycryptodomex 3.23.0 installed as one 7 MB package in 0.4 seconds and imported in 0.01 seconds with no audit findings in our sandbox; install it when the `Cryptodome` namespace or a specific uncommon primitive is required. New general-purpose encryption code is safer to start with `cryptography` or PyNaCl.
We installed it
| Install | ✓ · 0.4s | 1 package on disk · 7 MB |
| Import | ✓ | import Cryptodome in 0.01s · compiled extensions · py.typed · requires Python >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.* |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does pycryptodomex install cleanly?
Yes. In a fresh container with an empty cache, pip install pycryptodomex finished in 0.4s, leaving 1 package and 7 MB on disk. pip-audit reported no known vulnerabilities.
What does pycryptodomex need to run?
Python >=2.7, !=3.0., !=3.1., !=3.2., !=3.3., !=3.4., !=3.5., !=3.6.*, and a platform wheel with compiled extensions. In our run import Cryptodome succeeded in 0.01s, and the package ships py.typed for type checkers.
pycryptodomex or cryptography: which should you use?
cryptography: Use it for mainstream application encryption, higher-level recipes, and an OpenSSL-backed implementation. pycryptodomex 3.23.0 installed as one 7 MB package in 0.4 seconds and imported in 0.01 seconds with no audit findings in our sandbox; install it when the Cryptodome namespace or a specific uncommon primitive is required.
When should you not use pycryptodomex?
You are selecting a default for new application encryption. cryptography offers higher-level recipes that expose fewer nonce, mode, and authentication choices.
Use it if
- An environment already has something using `Crypto`, and your application needs PyCryptodome under the collision-free `Cryptodome` namespace.
- A defined protocol calls for AES-SIV, OCB, KW, KWP, HPKE, or another primitive absent from the higher-level library you normally use.
- Python must read or write encrypted PKCS#8 keys and other supported cryptographic key formats.
- The application requires self-contained algorithm implementations instead of routing operations through a system OpenSSL installation.
- You are selecting a default for new application encryption. `cryptography` offers higher-level recipes that expose fewer nonce, mode, and authentication choices.
- Developers may reuse nonces, choose ECB, consume unauthenticated plaintext, or compare MAC bytes with `==`; pycryptodomex permits all of those mistakes.
- The requirement is password storage. Choose a password-hashing package that records parameters, upgrades hashes, and owns rehash policy.
- The system needs a FIPS-validated cryptographic module. PyCryptodome does not provide that compliance boundary.
- Existing source imports `Crypto`. This distribution exposes `Cryptodome`; use `pycryptodome` only after checking the environment for old PyCrypto conflicts.
- Your security policy rejects a package whose latest stable release is from May 2025, even though repository work continued in July 2026.
Setup reality
We installed pycryptodomex 3.23.0 in a fresh unprivileged Python 3.12 Bookworm sandbox. Installation took 0.4 seconds, leaving one package and 7 MB on disk. import Cryptodome completed in 0.01 seconds. The distribution has zero direct dependencies, ships compiled .so extensions and py.typed, and reports BSD plus public-domain licensing. pip-audit found zero known vulnerabilities in our resolved environment.
A compatible wheel made that 0.4-second result possible. Unsupported interpreters or platforms must compile the native performance modules and need a compiler plus Python headers. Version 3.23.0 added Windows ARM wheels. Its declared Python range begins at 2.7 and excludes Python 3.0 through 3.6, so current applications should enforce their own modern runtime floor rather than copying that broad metadata into policy.
Every import begins with Cryptodome, such as from Cryptodome.Cipher import AES. Documentation snippets using from Crypto belong to the sibling pycryptodome package. Never install that sibling over the abandoned PyCrypto distribution in the same environment. Before changing names, inspect transitive requirements and verify which top-level namespace each dependency imports.
Treat cipher objects as per-message state. For AEAD modes, create a fresh object, save its generated nonce with ciphertext and tag, and use decrypt_and_verify() before releasing plaintext. Associated data must match byte for byte. RSA-OAEP should wrap a short random data key rather than the payload itself. Version 3.23.0 repairs HashEdDSA and Ed448 state mutation, so repeat-sign or repeat-verify tests matter when those algorithms are part of a protocol.
Patterns
Encrypt one message with AES-GCM encrypt-aes-gcm
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
key = get_random_bytes(32)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(b"secret")
nonce = cipher.noncePersist nonce, tag, and ciphertext together. Build a new cipher object for each message so the nonce is never reused with the same key.
Authenticate ciphertext before using plaintext verify-aes-gcm
from Cryptodome.Cipher import AES
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
try:
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
except ValueError as exc:
raise ValueError("ciphertext authentication failed") from excCalling `decrypt()` alone skips the GCM tag check. Do not expose or parse the resulting plaintext until `decrypt_and_verify()` succeeds.
Bind cleartext metadata to an AEAD message authenticate-header
from Cryptodome.Cipher import ChaCha20_Poly1305
cipher = ChaCha20_Poly1305.new(key=key)
cipher.update(b"user=42;version=1")
ciphertext, tag = cipher.encrypt_and_digest(payload)
nonce = cipher.nonceThe receiver must call `update()` with identical associated-data bytes before verification. A changed header makes the tag fail.
Write a password-protected PKCS#8 key export-encrypted-rsa-key
from Cryptodome.PublicKey import RSA
key = RSA.generate(3072)
pem = key.export_key(
format="PEM", passphrase=password, pkcs=8,
protection="scryptAndAES128-CBC",
)
public_pem = key.public_key().export_key()RSA generation belongs in provisioning or another controlled job. Generating a new private key during request handling loses identity and adds latency.
Encrypt a data key with RSA-OAEP wrap-key-rsa-oaep
from Cryptodome.Cipher import PKCS1_OAEP
from Cryptodome.Hash import SHA256
from Cryptodome.PublicKey import RSA
public_key = RSA.import_key(public_pem)
wrapped = PKCS1_OAEP.new(public_key, hashAlgo=SHA256).encrypt(data_key)OAEP is for a short random key, not a large document. Set SHA-256 explicitly because this constructor otherwise defaults to SHA-1.
Create and verify an RSA-PSS signature sign-rsa-pss
from Cryptodome.Hash import SHA256
from Cryptodome.Signature import pss
digest = SHA256.new(message)
signature = pss.new(private_key).sign(digest)
pss.new(private_key.public_key()).verify(SHA256.new(message), signature)A successful `verify()` returns `None`. Invalid signatures raise `ValueError` or `TypeError`, so handle those failures at the trust boundary.
Derive an encryption key from a password derive-key-scrypt
from Cryptodome.Protocol.KDF import scrypt
from Cryptodome.Random import get_random_bytes
salt = get_random_bytes(16)
key = scrypt(password, salt, key_len=32, N=2**17, r=8, p=1)Store the salt and all work parameters with the encrypted data. Benchmark memory and CPU cost on production-class machines before choosing values.
Derive independent keys with HKDF expand-secret-hkdf
from Cryptodome.Hash import SHA256
from Cryptodome.Protocol.KDF import HKDF
encryption_key, mac_key = HKDF(
master_secret, 32, salt, SHA256, num_keys=2, context=b"orders-v1"
)HKDF expands strong input material and separates contexts. It does not slow down password guessing and cannot replace a password KDF.
Check an HMAC without direct byte comparison verify-hmac
from Cryptodome.Hash import HMAC, SHA256
mac = HMAC.new(key, digestmod=SHA256)
mac.update(message)
try:
mac.verify(received_tag)
except ValueError as exc:
raise ValueError("invalid MAC") from excUse the library's verification method instead of `==`, which is not the intended constant-time tag-checking interface.
Read keys and salts from the OS-backed generator generate-random-material
from Cryptodome.Random import get_random_bytes
aes_key = get_random_bytes(32)
salt = get_random_bytes(16)Python's `random` module is designed for simulation and sampling. It must not generate cryptographic keys, nonces, or salts.
Import the isolated package name confirm-cryptodomex-namespace
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA256
from Cryptodome.PublicKey import RSA
import Cryptodome
print(Cryptodome.__version__)`Crypto` belongs to the sibling `pycryptodome` distribution. A successful `Cryptodome` import confirms that source code and installed package agree.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| cryptography | PyPI | Use it for mainstream application encryption, higher-level recipes, and an OpenSSL-backed implementation. |
| PyNaCl | PyPI | Use it for an opinionated libsodium API with a smaller set of safer operations. |
| pycryptodome | PyPI | Use the same project under `Crypto` when replacing old PyCrypto imports inside a controlled virtual environment. |
More security guides
cryptography · pyjwt · jose · requests-oauthlib · oauthlib · dompurify · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

