mrkeyoor.com_
Sun 20 Sept 14:46 UTC
PyPISecurityupdated 20 Sept 2026

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.

Verdict

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

Lab card: what happened when we installed pycryptodomexScreenshot of pycryptodomex documentation
Install✓ · 0.4s1 package on disk · 7 MB
Importimport 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 vulns0(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.

API stability5/5PyCryptodome 3.x has kept its module tree and the `Algorithm.new(...)` construction pattern across releases. Version 3.23.0 adds AES KW and KWP modes without changing existing cipher calls, and its HashEdDSA repair changes internal state handling rather than the signing method. The `Cryptodome` namespace is the defining promise of this distribution, so applications can depend on it remaining separate from the sibling package's `Crypto` imports.
Docs4/5The official site has per-algorithm pages with parameters, exceptions, key sizes, file formats, and executable examples. Its changelog names specific fixes, while the installation page explains the two distribution names and warns against mixing PyCrypto with `pycryptodome`. The material assumes readers can design cryptographic constructions: unsafe modes and low-level RSA calls remain documented beside safer authenticated options, with less application-level guidance than `cryptography` recipes provide.
Maintenance3/5Version 3.23.0 was released on May 17, 2025, while the repository remained unarchived and received a push on July 18, 2026. GitHub reports 3,260 stars and 92 open issues plus pull requests. Ongoing source activity is visible, but more than a year without a later stable version is a meaningful concern for a primitives package, especially when teams rely on released wheels and cannot consume unreleased fixes.
Ecosystem4/5The supplied snapshot lists 14,338,083 weekly downloads for `pycryptodomex`, separate from the sibling distribution's traffic. The project publishes wheels for many targets, added Windows ARM in 3.23.0, and implements algorithms beyond the usual application set. Its two package names plus the abandoned PyCrypto history remain a recurring source of import confusion that users of `cryptography` and PyNaCl do not face.

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.
Skip it if

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

Persist 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 exc

Calling `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.nonce

The 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 exc

Use 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

PackageRegistryPick it when
cryptographyPyPIUse it for mainstream application encryption, higher-level recipes, and an OpenSSL-backed implementation.
PyNaClPyPIUse it for an opinionated libsodium API with a smaller set of safer operations.
pycryptodomePyPIUse 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.