Agent skill
cryptography
Cryptographic primitives and recipes for Python including symmetric encryption, asymmetric cryptography, key derivation, and X.509 certificate handling
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/skills-skilldoai-skilldo-11
SKILL.md
Imports
from cryptography import x509
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes, hmac, serialization
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.asymmetric import rsa, padding, ec
from cryptography.hazmat.primitives.twofactor.totp import TOTP
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
Core Patterns
High-Level Symmetric Encryption with Fernet ✅ Current
from cryptography.fernet import Fernet
import os
# Generate a key and save it securely
key = Fernet.generate_key() # Returns URL-safe base64-encoded 32-byte key
# Put this somewhere safe!
# Encrypt data
f = Fernet(key)
token = f.encrypt(b"my secret message")
# Decrypt data
plaintext = f.decrypt(token)
- Fernet provides authenticated encryption with automatic key rotation support
- Best choice for simple symmetric encryption needs
- Handles IV generation, authentication, and timestamp automatically
Authenticated Encryption with AESGCM ✅ Current
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# Generate a random 256-bit key
key = os.urandom(32)
aesgcm = AESGCM(key)
# Encrypt with associated data (AAD)
nonce = os.urandom(12) # 96-bit nonce for GCM
plaintext = b"sensitive data"
associated_data = b"header info"
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
# Decrypt
decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data)
- Use AESGCM for performance-critical authenticated encryption
- Nonce must be unique for each encryption operation with the same key
- AAD provides additional context that's authenticated but not encrypted
Key Derivation with PBKDF2 ✅ Current
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
# Derive a key from a password
password = b"user_password"
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=600000, # OWASP recommendation as of 2023
)
key = kdf.derive(password)
# Verify a password
kdf2 = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=600000,
)
kdf2.verify(password, key) # Raises InvalidKey if wrong
- Use PBKDF2 for password-based key derivation
- Store salt with the derived key for verification
- Use at least 600,000 iterations for SHA256
X.509 Certificate Loading and Verification ✅ Current
from cryptography import x509
from cryptography.x509.oid import NameOID
import datetime
# Load a PEM certificate
with open("cert.pem", "rb") as f:
cert = x509.load_pem_x509_certificate(f.read())
# Extract certificate information
subject = cert.subject
common_name = subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
issuer = cert.issuer
not_valid_before = cert.not_valid_before_utc
not_valid_after = cert.not_valid_after_utc
# Load DER certificate
with open("cert.der", "rb") as f:
cert_der = x509.load_der_x509_certificate(f.read())
# Certificate chain verification
from cryptography.x509 import verification
import certifi
with open(certifi.where(), "rb") as f:
store = verification.Store(x509.load_pem_x509_certificates(f.read()))
verifier = (
verification.PolicyBuilder()
.store(store)
.time(datetime.datetime.now(datetime.timezone.utc))
.build_server_verifier(x509.DNSName("example.com"))
)
verifier.verify(leaf_cert, [intermediate_cert])
- Use
load_pem_x509_certificatefor PEM-encoded certificates - Use
load_der_x509_certificatefor DER-encoded certificates - PolicyBuilder provides flexible certificate chain verification
Two-Factor Authentication (TOTP) ✅ Current
from cryptography.hazmat.primitives.twofactor.totp import TOTP
from cryptography.hazmat.primitives.hashes import SHA1
from cryptography.hazmat.primitives.twofactor import InvalidToken
import os
import time
# Generate a secret key (minimum 128 bits, recommend 160 bits)
key = os.urandom(20) # 160 bits
# Create TOTP instance
totp = TOTP(key, 8, SHA1(), 30) # 8 digits, 30 second window
# Generate current token
token = totp.generate(time.time())
# Verify token
try:
totp.verify(token, time.time())
print("Token valid")
except InvalidToken:
print("Invalid token")
# Generate provisioning URI for QR code
uri = totp.get_provisioning_uri("[email protected]", "MyApp")
- Key must be at least 128 bits (160 bits recommended)
- Implement throttling to prevent brute force attacks
- Time window allows for clock drift between client and server
Configuration
Random Number Generation
import os
# Always use os.urandom() for cryptographic random bytes
random_bytes = os.urandom(32)
# For random integers
random_int = int.from_bytes(os.urandom(4), byteorder='big')
# For text-based tokens, use secrets module
import secrets
token = secrets.token_urlsafe(32)
Cipher Block Sizes and Key Lengths
# AES supports 128, 192, or 256-bit keys
from cryptography.hazmat.primitives.ciphers import algorithms
aes128 = algorithms.AES(os.urandom(16)) # 128-bit
aes192 = algorithms.AES(os.urandom(24)) # 192-bit
aes256 = algorithms.AES(os.urandom(32)) # 256-bit
# Block size is always 128 bits (16 bytes) for AES
# IV must match block size
HOTP Counter Management
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
from cryptography.hazmat.primitives.hashes import SHA1
def verify_hotp_with_lookahead(key, token, counter, look_ahead=10):
"""Verify HOTP with counter resynchronization window."""
hotp = HOTP(key, 6, SHA1())
for count in range(counter, counter + look_ahead):
try:
hotp.verify(token, count)
return count # Return new counter value
except InvalidToken:
continue
return None # Token invalid
Pitfalls
Wrong: Using standard random module for cryptography
import random
# NEVER do this - not cryptographically secure!
key = bytes([random.randint(0, 255) for _ in range(32)])
iv = bytes([random.randint(0, 255) for _ in range(16)])
Right: Use os.urandom() for cryptographic randomness
import os
# Always use os.urandom() for cryptographic purposes
key = os.urandom(32)
iv = os.urandom(16)
# Or use secrets module for tokens
import secrets
token = secrets.token_bytes(32)
- The standard
randommodule is not cryptographically secure and can lead to predictable keys/IVs os.urandom()provides cryptographically strong random bytes from the OS
Wrong: Using decrepit cipher modes for new applications
from cryptography.hazmat.decrepit.ciphers.modes import CFB
from cryptography.hazmat.primitives.ciphers import algorithms
# Don't use decrepit modes like CFB, CFB8, OFB in new code
cipher = Cipher(algorithms.AES(key), CFB(iv))
Right: Use modern authenticated encryption
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# Use AESGCM or ChaCha20Poly1305 for new applications
key = os.urandom(32)
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(os.urandom(12), plaintext, associated_data)
- CFB, CFB8, and OFB modes are in the
decrepitmodule and should only be used for legacy compatibility - Modern AEAD modes like AESGCM provide both encryption and authentication
Wrong: No throttling on HOTP/TOTP verification
def verify_totp(user_token):
totp = TOTP(get_user_key(), 6, SHA1(), 30)
try:
totp.verify(user_token, time.time())
return True
except InvalidToken:
return False
Right: Implement rate limiting and account lockout
import time
from collections import defaultdict
failed_attempts = defaultdict(int)
lockout_until = {}
def verify_totp_secure(user_id, user_token, max_attempts=5):
# Check if account is locked out
if user_id in lockout_until:
if time.time() < lockout_until[user_id]:
raise Exception("Account temporarily locked")
else:
del lockout_until[user_id]
failed_attempts[user_id] = 0
totp = TOTP(get_user_key(user_id), 6, SHA1(), 30)
try:
totp.verify(user_token, time.time())
failed_attempts[user_id] = 0 # Reset on success
return True
except InvalidToken:
failed_attempts[user_id] += 1
if failed_attempts[user_id] >= max_attempts:
lockout_until[user_id] = time.time() + 900 # 15 min lockout
return False
- HOTP/TOTP tokens are only 6-8 digits and vulnerable to brute force
- Always implement throttling with exponential backoff or account lockout
Wrong: Using weak key lengths for HOTP/TOTP
# 80-bit key (too weak, will raise ValueError by default)
key = os.urandom(10)
hotp = HOTP(key, 6, SHA1()) # Raises ValueError
Right: Use minimum 128-bit keys (160 bits recommended)
import os
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
from cryptography.hazmat.primitives.hashes import SHA1
# 160-bit key (recommended)
key = os.urandom(20)
hotp = HOTP(key, 6, SHA1())
# If you MUST support 80-bit keys (not recommended):
key_80bit = os.urandom(10)
hotp = HOTP(key_80bit, 6, SHA1(), enforce_key_length=False)
# Add additional security checks before using
- Keys should be at least 128 bits, preferably 160 bits
enforce_key_length=True(default) prevents weak keys
Wrong: Reusing nonces in AESGCM
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = os.urandom(32)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
# NEVER reuse the same nonce with the same key!
ct1 = aesgcm.encrypt(nonce, b"message 1", None)
ct2 = aesgcm.encrypt(nonce, b"message 2", None) # CATASTROPHIC
Right: Generate a unique nonce for every encryption
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
key = os.urandom(32)
aesgcm = AESGCM(key)
# Generate fresh nonce for each encryption
nonce1 = os.urandom(12)
ct1 = aesgcm.encrypt(nonce1, b"message 1", None)
nonce2 = os.urandom(12)
ct2 = aesgcm.encrypt(nonce2, b"message 2", None)
# Store nonce with ciphertext: nonce1 + ct1, nonce2 + ct2
- Nonce reuse with the same key completely breaks GCM security
- Generate a new random nonce for every encryption operation
- Store the nonce alongside the ciphertext for decryption
References
Migration from v45.x
No breaking changes or major deprecations were introduced between 45.x and 46.0.5.
See the official changelog for any minor fixes or security updates.
Current Library State (from source analysis)
API Surface
{
"library_category": "general",
"apis": [
{
"name": "cryptography.__author__",
"type": "variable",
"signature": "",
"signature_truncated": false,
"return_type": "str",
"module": "cryptography.__about__",
"publicity_score": "high",
"module_type": "public",
"decorators": [],
"deprecation": {
"is_deprecated": false
}
},
{
"name": "cryptography.__copyright__",
"type": "variable",
"signature": "",
"signature_truncated": false,
"return_type": "str",
"module": "cryptography.__about__",
"publicity_score": "high",
"module_type": "public",
"decorators": [],
"deprecation": {
"is_deprecated": false
}
},
{
"name": "cryptography.__version__",
"type": "variable",
"signature": "",
"signature_truncated": false,
"return_type": "str",
"module": "cryptography.__about__",
"publicity_score": "high",
"module_type": "public",
"decorators": [],
"deprecation": {
"is_deprecated": false
}
},
{
"name": "cryptography.hazmat.backends.default_backend",
"type": "function",
"signature": "default_backend() -> Any",
"signature_truncated": false,
"return_type": "Any",
"module": "cryptography.hazmat.backends",
"publicity_score": "high",
"module_type": "public",
"decorators": [],
"deprecation": {
"is_deprecated": false
}
},
{
"name": "cryptography.hazmat.primitives.ciphers.Cipher",
"type": "class",
"signature": "Cipher(algorithm: CipherAlgorithm, mode: Optional[Mode] = None, backend: Optional[Any] = None)",
"signature_truncated": false,
"return_type": "Cipher",
"module": "cryptography.hazmat.primitives.ciphers.base",
"publicity_score": "high",
"module_type": "public",
"decorators": [],
"class_hierarchy": {
"bases": [],
"is_abstract": false
},
"deprecation": {
"is_deprecated": false
},
"type_hints": {
"algorithm": {
"base_type": "CipherAlgorithm",
"is_optional": false,
"default_value": null
},
"mode": {
"base_type": "Optional[Mode]",
"is_optional": true,
"default_value": "None"
},
"backend": {
"base_type": "Optional[Any]",
"is_optional": true,
"default_value": "None"
}
}
},
// ... see above for all other public/documented APIs
]
}
Usage Patterns
[
{
"api": "x509.ObjectIdentifier",
"setup_code": [
"from cryptography import x509"
],
"usage_pattern": "x509.ObjectIdentifier('1.3.6.1.4.1.11129.2.4.5')",
"assertions": "No assertion; used as benchmark target.",
"test_infrastructure": "pytest-benchmark's benchmark fixture",
"deprecation_status": "current"
},
// ... see above for all other usage patterns
]
Documentation & Changelog
{
"documented_apis": [
"cryptography.fernet.Fernet",
"cryptography.fernet.Fernet.generate_key",
"cryptography.hazmat.decrepit.ciphers.algorithms.ARC4",
"cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES",
"cryptography.hazmat.decrepit.ciphers.algorithms.CAST5",
"cryptography.hazmat.decrepit.ciphers.algorithms.SEED",
"cryptography.hazmat.decrepit.ciphers.algorithms.Blowfish",
"cryptography.hazmat.decrepit.ciphers.algorithms.IDEA",
"cryptography.hazmat.primitives.ciphers.Cipher",
"cryptography.hazmat.primitives.ciphers.modes.CBC",
"cryptography.hazmat.primitives.ciphers.modes",
"cryptography.hazmat.primitives.ciphers.Cipher.encryptor",
"cryptography.hazmat.primitives.ciphers.Cipher.decryptor",
"cryptography.hazmat.primitives.twofactor.InvalidToken",
"cryptography.hazmat.primitives.twofactor.hotp.HOTPHashTypes",
"cryptography.hazmat.primitives.twofactor.hotp.HOTP",
"cryptography.hazmat.primitives.twofactor.hotp.HOTP.generate",
"cryptography.hazmat.primitives.twofactor.hotp.HOTP.verify",
"cryptography.hazmat.primitives.twofactor.hotp.HOTP.get_provisioning_uri",
"cryptography.hazmat.primitives.twofactor.totp.TOTP",
"cryptography.hazmat.primitives.twofactor.totp.TOTP.generate",
"cryptography.hazmat.primitives.twofactor.totp.TOTP.verify"
],
"conventions": [
"Always use your operating system's random number generator (os.urandom) or the secrets module for cryptographically secure random numbers.",
"Use the 'recipes' layer for cryptographic operations whenever possible; only use the 'hazmat' layer if you need low-level primitives and understand the risks.",
"Keys for cryptographic operations should be generated using secure random sources and kept secret.",
"For two-factor authentication (HOTP/TOTP), keys should be at least 128 bits, and preferably 160 bits, and generated randomly.",
"Implement throttling on HOTP/TOTP verification endpoints to reduce brute force risk.",
"When using HOTP, implement a look-ahead window to handle counter desynchronization.",
"For block ciphers, use properly sized IVs generated from a secure random source (e.g., os.urandom(16) for AES).",
"Do not use deprecated or insecure algorithms (such as ARC4, Blowfish, IDEA, TripleDES, CAST5, SEED) except for legacy interoperability.",
"Store cryptographic keys securely; do not hardcode them in your source code."
],
"pitfalls": [
{
"category": "Random number generation",
"wrong": "import random\niv = bytes([random.randint(0, 255) for _ in range(16)])",
"why": "The random module is not cryptographically secure and can lead to predictable results, which is insecure for cryptography.",
"right": "import os\niv = os.urandom(16)"
},
{
"category": "Insecure algorithm usage",
"wrong": "from cryptography.hazmat.decrepit.ciphers.algorithms import ARC4\nalgorithm = ARC4(key)\n# ... use ARC4",
"why": "ARC4 is known to have serious weaknesses and should only be used for legacy compatibility as a last resort.",
"right": "Use modern algorithms such as AES from cryptography.hazmat.primitives.ciphers.algorithms instead."
},
{
"category": "Key generation",
"wrong": "key = b'mysecretkey12345' # Hardcoded or non-random key",
"why": "Keys must be generated from a secure random source and should not be hardcoded, as this allows attackers to easily guess them.",
"right": "import os\nkey = os.urandom(16) # For AES-128, for example"
},
{
"category": "Key length for HOTP/TOTP",
"wrong": "key = os.urandom(10) # 80 bits, too short for recommended usage",
"why": "Keys shorter than 128 bits are insecure and not recommended; HOTP/TOTP expects at least 128 bits, preferably 160 bits.",
"right": "key = os.urandom(20) # 160 bits"
},
{
"category": "HOTP/TOTP brute force",
"wrong": "def verify(hotp):\n return HOTP.verify(hotp)",
"why": "Not throttling verification attempts allows for brute force attacks.",
"right": "Implement throttling/lockout after several failed attempts to prevent brute force."
},
{
"category": "HOTP counter resynchronization",
"wrong": "server_counter = client_counter # Always trust client",
"why": "The server counter should be incremented only on successful authentication to avoid desynchronization.",
"right": "Implement look-ahead window and increment server counter only after successful verification."
}
],
"breaking_changes": [
{
"version_from": "pre-43.0.0",
"version_to": "43.0.0",
"change": "Moved insecure/deprecated symmetric ciphers (ARC4, TripleDES, CAST5, SEED, Blowfish, IDEA) to cryptography.hazmat.decrepit.ciphers.algorithms. Their use is strongly discouraged and not part of the main cryptography.hazmat.primitives.ciphers.algorithms package.",
"migration": "Update imports to use cryptography.hazmat.decrepit.ciphers.algorithms for these ciphers. Refactor code to avoid using these algorithms when possible."
}
],
"migration_notes": "See the official documentation and changelog for details on the movement of insecure algorithms to the decrepit namespace in v43.0.0+. Update imports accordingly and migrate to modern algorithms where possible."
}
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?