Crypto Implementation Audit Checklist#
Last audited: 2026-03-23 Auditor: Claude (automated) + manual review
1. Constant-Time Comparisons#
Target files: i2p_crypto/chacha20.py, i2p_crypto/aes_cbc.py, i2p_crypto/hmac_generator.py, i2p_crypto/eddsa.py, i2p_crypto/dsa.py, i2p_crypto/noise.py, i2p_crypto/garlic_crypto.py, i2p_data/i2np_codec.py
Check: Every MAC/tag/signature/hash verification uses hmac.compare_digest().
| Location | Pattern | Status |
|---|---|---|
hmac_generator.py:63 |
hmac.compare_digest() |
PASS |
password.py:31 |
secrets.compare_digest() |
PASS |
console/auth.py:28 |
secrets.compare_digest() |
PASS |
garlic_crypto.py:257 |
hmac.compare_digest() on SHA-256 hash |
PASS (fixed 2026-03-23) |
i2np_codec.py:80 |
hmac.compare_digest() on checksum byte |
PASS (fixed 2026-03-23) |
dsa.py, eddsa.py |
Delegated to cryptography library verify() |
PASS |
noise.py CipherState |
Delegated to ChaCha20Poly1305.decrypt() |
PASS |
Result: PASS -- All comparisons on security-sensitive data use constant-time functions.
2. Key Zeroization#
Target files: i2p_crypto/x25519.py, i2p_crypto/noise.py, i2p_crypto/hkdf.py, i2p_data/session_key_manager.py
Check: Ephemeral keys stored in bytearray, explicitly zeroed after use.
| Location | Key Type | Storage | Zeroed? | Status |
|---|---|---|---|---|
noise.py:32 |
CipherState key | bytes (immutable) |
No | KNOWN LIMITATION |
noise.py:162 |
Ephemeral private key | bytes tuple |
No | KNOWN LIMITATION |
noise.py:360 |
_x25519_private |
bytes |
No | KNOWN LIMITATION |
hkdf.py:20-21 |
Output buffers | bytearray |
No explicit zeroing | ADVISORY |
session_key_manager.py:103 |
Session keys | bytes |
No | KNOWN LIMITATION |
Result: ADVISORY -- Python's immutable bytes type makes key zeroization impossible without switching to bytearray. This is a language-level limitation. The cryptography library handles its own key zeroization in C. For pure-Python key material, use bytearray with explicit zeroing in finally blocks.
Note: Key zeroization in Python is best-effort. The garbage collector may retain copies, and bytearray zeroing can be optimized away. For production deployments, rely on the cryptography library's C-level key handling.
3. CSPRNG Usage#
Target files: i2p_crypto/, i2p_transport/
Check: All randomness sourced from os.urandom() or secrets.*.
| Location | Function | Source | Status |
|---|---|---|---|
elgamal.py:38-41 |
Key generation | os.urandom() |
PASS |
garlic_crypto.py:19-20 |
Padding/tags | os.urandom() |
PASS |
ssu2_handshake.py |
Connection IDs | os.urandom(8) |
PASS |
ssu2_server.py |
Nonces | os.urandom(4) |
PASS |
ntcp2_real_handshake.py |
Padding | os.urandom() |
PASS |
ssu2_connection.py:194 |
Challenge data | os.urandom(8) |
PASS |
Result: PASS -- Zero uses of random.* in crypto or transport modules.
4. No Secrets in Logs#
Target: All files in i2p_crypto/, i2p_transport/, i2p_data/
Check: No logging.*, print(), repr() calls that include key material.
| Location | Log Content | Status |
|---|---|---|
ssu2_server.py |
Addresses, packet sizes | PASS |
manager.py |
Peer hash prefix (first 8 bytes only) | PASS |
upnp.py |
URLs only | PASS |
Result: PASS -- No key material found in any logging statements.
5. Nonce Management#
Target files: NTCP2 and SSU2 frame encryption in i2p_transport/
Check: Nonce counters increment monotonically, overflow detection, separate nonce spaces.
| Check | Location | Status |
|---|---|---|
| Monotonic increment (Noise) | noise.py:51,60 |
PASS -- _n += 1 after each op |
| Overflow detection (Noise) | noise.py:48,57 |
PASS (fixed 2026-03-23) -- checks _n >= MAX_NONCE |
| Overflow detection (SSU2) | ssu2_connection.py:119 |
PASS (fixed 2026-03-23) -- checks >= 0xFFFFFFFF |
| Separate nonce spaces | ssu2_connection.py:65-67 |
PASS -- send/recv use separate CipherState |
| Nonce construction (SSU2) | ssu2_connection.py:105-107 |
PASS -- 12-byte nonce from packet number |
Result: PASS -- All nonce management issues addressed.
6. AEAD Correctness#
Target files: i2p_crypto/chacha20.py, NTCP2/SSU2 frame processing
Check: AD parameter includes header bytes; decryption returns no partial plaintext on auth failure.
| Check | Location | Status |
|---|---|---|
| AD binding (SSU2) | ssu2_connection.py:127,170 |
PASS -- header bound as AD |
| AD binding (NTCP2) | ntcp2_real_connection.py:81,103 |
PASS -- empty AD (per NTCP2 data phase spec) |
| Atomic decryption failure | cryptography library |
PASS -- InvalidTag raised, no partial plaintext |
| Nonce increment on failure | noise.py:60 |
PASS -- _n += 1 only reached after successful decrypt |
Result: PASS -- AEAD semantics correct.
7. HKDF Usage#
Target: i2p_crypto/hkdf.py, i2p_crypto/noise.py
Check: Two-phase (Extract + Expand) usage; info parameters are protocol-specific strings.
| Check | Location | Status |
|---|---|---|
| Extract + Expand | hkdf.py |
PASS -- separate extract() and expand() functions |
| Protocol-specific info | noise.py MixKey/MixHash |
PASS -- chaining key derived per Noise spec |
| Output length check | hkdf.py |
PASS -- expand() output <= 255 * HashLen |
Result: PASS
8. Integer Overflow Masking#
Target: i2p_crypto/siphash.py, nonce counters
Check: All arithmetic ported from Java applies explicit masks.
| Check | Location | Status |
|---|---|---|
| 64-bit mask in SipHash | siphash.py:9,14,66,75,79 |
PASS -- & MASK64 on all operations |
| IV masking | siphash.py:98 |
PASS -- iv & MASK64 on init |
| SipHash return | siphash.py:61 |
PASS -- (v0 ^ v1 ^ v2 ^ v3) & MASK64 |
Result: PASS -- All Java overflow semantics correctly ported with explicit masks.
9. String Encoding#
Target: RouterInfo properties, address book, SU3 metadata
Check: All string-to-bytes conversions use explicit .encode('utf-8').
| Check | Status |
|---|---|
| RouterInfo encoding | PASS -- explicit UTF-8 in data serialization |
| Password hashing | PASS -- password.encode("utf-8") |
| Console auth | PASS -- explicit UTF-8 encoding |
Result: PASS
Summary#
| Section | Result | Fixes Applied |
|---|---|---|
| 1. Constant-time comparisons | PASS | 2 fixes (garlic_crypto, i2np_codec) |
| 2. Key zeroization | ADVISORY | Language limitation documented |
| 3. CSPRNG usage | PASS | None needed |
| 4. No secrets in logs | PASS | None needed |
| 5. Nonce management | PASS | 2 fixes (noise overflow, SSU2 overflow) |
| 6. AEAD correctness | PASS | None needed |
| 7. HKDF usage | PASS | None needed |
| 8. Integer overflow masking | PASS | None needed |
| 9. String encoding | PASS | None needed |
Overall: 8/9 PASS, 1 ADVISORY (key zeroization -- Python language limitation) Fixes applied: 4 security hardening changes in this audit cycle