# PYTHON-CRYPTO-SEC-002a: Blowfish Cipher Usage via PyCryptodome

> **Severity:** HIGH | **CWE:** CWE-327 | **OWASP:** A02:2021

- **Language:** Python
- **Category:** Cryptography
- **URL:** https://codepathfinder.dev/registry/python/cryptography/PYTHON-CRYPTO-SEC-002a
- **Detection:** `pathfinder scan --ruleset python/PYTHON-CRYPTO-SEC-002a --project .`

## Description

This rule detects calls to `Crypto.Cipher.Blowfish.new()` from the PyCryptodome
library. Blowfish operates on 64-bit blocks, making it susceptible to the Sweet32
birthday attack (CVE-2016-2183) once approximately 32GB of data is encrypted under
the same key.

The birthday paradox dictates that with a 64-bit block cipher in CBC or CFB mode,
block collisions become probable after 2^32 encrypted blocks. An attacker who can
observe sufficient ciphertext can leverage these collisions to recover plaintext
segments, most notably session tokens in web traffic. Following the public disclosure
of Sweet32 in 2016, all major browsers and TLS stacks disabled 64-bit block cipher
suites.

The rule matches `PyCryptoCipherBlowfish.method("new")` -- any instantiation of a
Blowfish cipher object via PyCryptodome. Blowfish's wide key range (32 to 448 bits)
does not mitigate the block size problem. The companion rule PYTHON-CRYPTO-SEC-002
covers Blowfish in the `cryptography` library.


## Vulnerable Code

```python
from Crypto.Cipher import Blowfish

bf = Blowfish.new(b'secret_key_16by', Blowfish.MODE_CBC, b'\x00' * 8)
```

## Secure Code

```python
from Crypto.Cipher import AES
import os

# SECURE: AES-GCM provides authenticated encryption
key = os.urandom(32)  # 256-bit key
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(b"sensitive data")

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, flows, QueryType
from codepathfinder.presets import PropagationPresets

class PyCryptoCipherBlowfish(QueryType):
    fqns = ["Crypto.Cipher.Blowfish", "Cryptodome.Cipher.Blowfish"]


@python_rule(
    id="PYTHON-CRYPTO-SEC-002a",
    name="Insecure Blowfish Cipher (PyCryptodome)",
    severity="HIGH",
    category="cryptography",
    cwe="CWE-327",
    tags="python,pycryptodome,blowfish,weak-cipher,CWE-327",
    message="Blowfish has a 64-bit block size vulnerable to birthday attacks. Use AES instead.",
    owasp="A02:2021",
)
def detect_blowfish_cipher_pycrypto():
    """Detects Blowfish in PyCryptodome."""
    return PyCryptoCipherBlowfish.method("new")
```

## How to Fix

- Replace Crypto.Cipher.Blowfish with AES in GCM mode (AES.new(key, AES.MODE_GCM)) for authenticated encryption
- Use ChaCha20-Poly1305 via the cryptography library if AES hardware acceleration is unavailable
- If a block cipher mode other than GCM is required, always pair it with HMAC-SHA256 to detect ciphertext tampering
- Re-encrypt any data stored with Blowfish under AES-256-GCM and rotate all Blowfish key material
- Confirm no Blowfish cipher suites remain enabled in TLS configuration alongside this code-level fix

## Security Implications

- **Sweet32 Birthday Attack After 32GB of Ciphertext:** PyCryptodome's Blowfish operates on 8-byte blocks. After 2^32 blocks (~32GB)
under the same key, block collisions become statistically likely. In CBC mode,
a collision between two ciphertext blocks allows an attacker who knows one
corresponding plaintext to recover the other. For services encrypting session
data or bulk files with a persistent key, 32GB is reachable within hours.

- **CBC Mode Without MAC Creates Padding Oracle Exposure:** PyCryptodome's Blowfish in CBC mode without a separate HMAC produces unauthenticated
ciphertext. Applications that return different error responses for invalid padding
versus decryption failure inadvertently expose a padding oracle. An attacker can
decrypt arbitrary ciphertext blocks by sending carefully crafted inputs and
measuring response behavior.

- **Superseded by AES Over Two Decades Ago:** AES was standardized in 2001 specifically to replace aging 64-bit block ciphers.
PyCryptodome includes Blowfish only for legacy compatibility. Any new code that
selects Blowfish over AES is choosing a demonstrably weaker algorithm for no
technical benefit.

- **No Hardware Acceleration Path:** Modern CPUs include AES-NI instructions that make AES-GCM exceptionally fast.
Blowfish has no hardware acceleration path and is slower than AES-NI-accelerated
AES in typical deployments. There is no performance argument for Blowfish.


## FAQ

**Q: We use Blowfish with a unique key per file. Is the Sweet32 attack still relevant?**

If keys are truly unique per file and never reused, the risk from Sweet32 is
minimal for small files -- you need 32GB of ciphertext under one key for the
birthday bound to be significant. However, per-key uniqueness is operationally
difficult to guarantee, key management complexity increases, and there is still
no integrity protection without a separate MAC. AES-GCM solves all of this
cleanly with a single primitive.


**Q: PyCryptodome still includes Blowfish -- doesn't that mean it's safe to use?**

PyCryptodome maintains Blowfish for interoperability with legacy systems that
require it for decryption of existing data. Inclusion in a library does not
indicate safety for new encryption. The library's own documentation marks
Blowfish as a legacy cipher and recommends AES for new applications.


**Q: How do I migrate a file-encryption system from Blowfish to AES-GCM?**

Read each Blowfish-encrypted file, decrypt using the stored key, then
re-encrypt using AES.new(key256, AES.MODE_GCM) and store the new ciphertext
along with the nonce and authentication tag. Update your key storage to hold
32-byte AES keys instead of Blowfish keys. Delete the original Blowfish-
encrypted copies and the old keys after successful migration.


**Q: What cipher modes are available for Blowfish in PyCryptodome, and are any of them safe?**

PyCryptodome supports Blowfish in CBC, CFB, OFB, and ECB modes. None of them
are safe for new code. ECB is the worst (identical plaintext blocks produce
identical ciphertext). CBC, CFB, and OFB are all affected by the Sweet32 block
size problem when sufficient data is encrypted. AES in GCM mode is the correct
replacement for all of these.


**Q: Does this rule apply to bcrypt password hashing, which uses Blowfish internally?**

No. This rule targets `Crypto.Cipher.Blowfish.new()` -- the symmetric cipher.
PyCryptodome's bcrypt implementation (Crypto.Protocol.KDF) uses the Blowfish
key schedule as a one-way function for password hashing, which is a different
and appropriate use case. Bcrypt is not flagged by this rule.


## References

- [CWE-327: Use of a Broken or Risky Cryptographic Algorithm](https://cwe.mitre.org/data/definitions/327.html)
- [Sweet32: Birthday attacks on 64-bit block ciphers in TLS and OpenVPN](https://sweet32.info/)
- [CVE-2016-2183: Sweet32 vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2016-2183)
- [NIST SP 800-131A Rev 2: Transitioning the Use of Cryptographic Algorithms](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final)
- [OWASP Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)
- [PyCryptodome Blowfish Documentation](https://pycryptodome.readthedocs.io/en/latest/src/cipher/classic.html#blowfish)

---

Source: https://codepathfinder.dev/registry/python/cryptography/PYTHON-CRYPTO-SEC-002a
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
