A comprehensive, production-grade repository for cryptographic algorithms, protocols, and implementations. This project provides educational resources, best practices, and practical examples of modern cryptographic techniques.
cryptography or PyCryptodome.
- Overview
- Features
- Quick Start
- Project Structure
- Core Concepts
- Usage Examples
- Security Best Practices
- Contributing
- Resources
- License
This repository serves as a centralized hub for understanding and implementing cryptographic concepts. It covers:
- Symmetric encryption (AES, ChaCha20)
- Asymmetric encryption (RSA, ECC, Diffie-Hellman)
- Cryptographic hashing (SHA-256, SHA-512, BLAKE2)
- Digital signatures (HMAC, DSA, ECDSA)
- Key derivation (PBKDF2, Argon2, Scrypt)
- Key exchange protocols (ECDH, DH)
Each implementation includes detailed documentation, security considerations, and practical examples suitable for learning and reference.
| Category | Algorithms |
|---|---|
| Symmetric Encryption | AES (ECB, CBC, CTR, GCM), ChaCha20-Poly1305 |
| Asymmetric Encryption | RSA (OAEP, PKCS#1), ECC (secp256r1, secp384r1) |
| Hash Functions | SHA-256, SHA-512, BLAKE2b, BLAKE2s |
| Digital Signatures | HMAC, DSA, ECDSA, RSA-PSS |
| Key Derivation | PBKDF2, Argon2, Scrypt |
| Key Exchange | Diffie-Hellman, ECDH |
| Documentation | Detailed explanations, threat models, security analysis |
| Examples | Real-world patterns for file encryption, authentication, secure communication |
- Python 3.8 or later
- pip or your preferred package manager
- Git
# Clone the repository
git clone https://github.com/Ali-hey-0/Cryptography.git
cd Cryptography
# Install dependencies
pip install -r requirements.txtfrom cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
message = b"Hello, Cryptography!"
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(message)
hash_result = digest.finalize()
print(hash_result.hex())from cryptography.fernet import Fernet
# Generate a secure key
key = Fernet.generate_key()
cipher = Fernet(key)
# Encrypt
plaintext = b"Secret message"
ciphertext = cipher.encrypt(plaintext)
# Decrypt
decrypted = cipher.decrypt(ciphertext)
print(decrypted.decode())from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
# Generate key pair
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
public_key = private_key.public_key()
# Encrypt
plaintext = b"Secret"
ciphertext = public_key.encrypt(
plaintext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Decrypt
decrypted = private_key.decrypt(ciphertext, padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
))from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
# Generate key pair
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()
# Sign
message = b"Message to sign"
signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
# Verify
public_key.verify(signature, message, ec.ECDSA(hashes.SHA256()))Cryptography/
├── README.md # This file
├── requirements.txt # Python dependencies
├── LICENSE # MIT License
├── .gitignore # Git ignore patterns
│
├── symmetric/ # Symmetric encryption algorithms
│ ├── __init__.py
│ ├── aes.py # AES implementations (ECB, CBC, CTR, GCM)
│ ├── chacha20.py # ChaCha20 and ChaCha20-Poly1305
│ └── des.py # DES (legacy/educational)
│
├── asymmetric/ # Asymmetric encryption algorithms
│ ├── __init__.py
│ ├── rsa.py # RSA encryption and signing
│ ├── ecc.py # Elliptic Curve Cryptography
│ └── diffie_hellman.py # Diffie-Hellman key exchange
│
├── hashing/ # Cryptographic hash functions
│ ├── __init__.py
│ ├── sha.py # SHA-256, SHA-512
│ ├── blake2.py # BLAKE2b and BLAKE2s
│ └── hmac.py # HMAC implementation
│
├── signatures/ # Digital signature algorithms
│ ├── __init__.py
│ ├── dsa.py # DSA signatures
│ ├── ecdsa.py # ECDSA signatures
│ └── rsa_sign.py # RSA-PSS signatures
│
├── key_derivation/ # Key derivation functions
│ ├── __init__.py
│ ├── pbkdf2.py # PBKDF2
│ ├── argon2.py # Argon2 (password hashing)
│ └── scrypt.py # Scrypt
│
├── examples/ # Practical implementation examples
│ ├── __init__.py
│ ├── secure_file_encryption.py # Encrypt/decrypt files
│ ├── key_exchange.py # Key exchange protocols
│ ├── digital_signatures.py # Sign and verify documents
│ └── password_management.py # Secure password handling
│
└── tests/ # Unit tests
├── __init__.py
├── test_symmetric.py
├── test_asymmetric.py
├── test_hashing.py
└── test_signatures.py
Symmetric algorithms use the same key for encryption and decryption. They are fast and efficient for large data.
Use cases:
- Encrypting files or databases
- Securing communication channels (in combination with key exchange)
Examples: AES, ChaCha20
Advantages: High speed, suitable for large data
Disadvantages: Key distribution challenge
Asymmetric algorithms use public and private key pairs. Anyone can encrypt with the public key, but only the private key holder can decrypt.
Use cases:
- Secure key exchange
- Digital signatures
- Public key infrastructure (PKI)
Examples: RSA, ECC, Diffie-Hellman
Advantages: Elegant key distribution, enables digital signatures
Disadvantages: Slower than symmetric encryption
One-way functions that produce fixed-size digests from arbitrary input. Collisions should be computationally infeasible.
Use cases:
- Data integrity verification
- Password storage (with salt and slow hashing)
- Content addressing
Examples: SHA-256, BLAKE2, Argon2
Properties:
- Deterministic (same input → same output)
- Avalanche effect (small input change → completely different output)
- Preimage resistance (hard to find input from hash)
- Collision resistance (hard to find two inputs with same hash)
Prove authenticity and non-repudiation using asymmetric cryptography. Sender signs with private key; receiver verifies with public key.
Use cases:
- Document authentication
- Code signing
- Certificate signing (PKI)
Examples: ECDSA, RSA-PSS, DSA
Derive cryptographic keys from passwords or other low-entropy sources securely using salt and computational cost parameters.
Use cases:
- Password-based encryption
- Secure password storage
- Key stretching
Examples: PBKDF2, Argon2, Scrypt
Key properties:
- Deterministic
- Slow (intentionally, to resist brute-force)
- Salt prevents rainbow tables
- Memory cost (for Argon2) resists GPU/ASIC attacks
from cryptography.fernet import Fernet
import os
def encrypt_file(filepath, key):
"""Encrypt a file using Fernet (authenticated encryption)."""
cipher = Fernet(key)
with open(filepath, 'rb') as f:
data = f.read()
encrypted = cipher.encrypt(data)
with open(filepath + '.encrypted', 'wb') as f:
f.write(encrypted)
def decrypt_file(filepath, key):
"""Decrypt a Fernet-encrypted file."""
cipher = Fernet(key)
with open(filepath, 'rb') as f:
encrypted_data = f.read()
decrypted = cipher.decrypt(encrypted_data)
return decrypted
# Usage
key = Fernet.generate_key()
encrypt_file('sensitive.txt', key)
plaintext = decrypt_file('sensitive.txt.encrypted', key)from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
import os
def derive_key_from_password(password: str, salt: bytes = None, iterations: int = 100000) -> tuple:
"""Derive a key from a password using PBKDF2."""
if salt is None:
salt = os.urandom(16)
kdf = PBKDF2(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=iterations,
backend=default_backend()
)
key = kdf.derive(password.encode())
return key, salt
# Usage
password = "user_password"
key, salt = derive_key_from_password(password)
# Store salt with ciphertext; derive same key later with same password + saltfrom cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
def perform_key_exchange():
"""Perform ECDH key exchange between two parties."""
# Alice generates her key pair
alice_private = ec.generate_private_key(ec.SECP256R1(), default_backend())
alice_public = alice_private.public_key()
# Bob generates his key pair
bob_private = ec.generate_private_key(ec.SECP256R1(), default_backend())
bob_public = bob_private.public_key()
# Alice computes shared secret
alice_shared = alice_private.exchange(ec.ECDH(), bob_public)
# Bob computes shared secret
bob_shared = bob_private.exchange(ec.ECDH(), alice_public)
# Shared secrets are identical
assert alice_shared == bob_shared
return alice_shared
shared_key = perform_key_exchange()- Use established libraries like
cryptography,PyCryptodome, orlibsodium - Generate cryptographic randomness using
os.urandom()or equivalent (notrandommodule) - Use authenticated encryption (AES-GCM, ChaCha20-Poly1305) instead of raw AES
- Include authentication for all encrypted data (HMAC, authenticated encryption modes)
- Use salt and slow hashing for password storage (Argon2, bcrypt, scrypt)
- Rotate keys periodically for long-term security
- Use unique nonces/IVs for each encryption operation
- Validate all inputs before cryptographic operations
- Use secure key storage (HSMs, key vaults, environment variables—never hardcoded)
- Keep cryptographic libraries updated to patch vulnerabilities
- Hardcode secrets in source code or configuration
- Use deprecated algorithms (DES, MD5, SHA-1, RC4)
- Reuse nonces/IVs with the same key
- Implement cryptography from scratch without extensive review
- Use predictable randomness for security-sensitive operations
- Encrypt without authentication (ECB mode, unauthenticated ciphers)
- Store passwords in plain text or with weak hashing
- Use symmetric encryption alone for key distribution (use asymmetric or key derivation)
- Trust unverified cryptographic implementations in production
- Ignore cryptographic padding and IV/nonce requirements
# Good: Load secrets from environment
import os
from cryptography.fernet import Fernet
SECRET_KEY = os.environ.get('SECRET_KEY')
if not SECRET_KEY:
raise ValueError("SECRET_KEY environment variable not set")
cipher = Fernet(SECRET_KEY.encode())# Bad: Never do this
SECRET_KEY = "my-secret-key" # Exposed in source code!
cipher = Fernet(SECRET_KEY.encode())We welcome contributions! Please follow these guidelines:
- Style: Follow PEP 8
- Documentation: Add docstrings to all functions/classes (Google or NumPy style)
- Testing: Write unit tests for new features
- Security: Review for common cryptographic pitfalls
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name
- Commit your changes:
git commit -m "Add feature: description" - Push to your fork:
git push origin feature/your-feature-name
- Open a Pull Request with a clear description
# Run all tests
python -m pytest tests/
# Run with coverage
python -m pytest --cov=. tests/- cryptography.io – Python cryptography library
- NIST Cryptographic Standards – Federal standards and guidelines
- RFC Editor – Internet standards
- Cryptography Basics - Stanford
- Understanding Cryptography
- Serious Cryptography – Book by Jean-Philippe Aumasson
- CyberChef – Cryptography playground
- HashCat – Hash cracking (learn security implications)
- Wireshark – Network analysis
This project is licensed under the MIT License – see the LICENSE file for details.
This repository is for educational and reference purposes only.
- Do not use unreviewed cryptographic implementations in production systems
- Always use peer-reviewed, battle-tested libraries
- Cryptography is complex; mistakes can have severe security implications
- When in doubt, consult security experts or use established tools
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Questions: Please open an issue with the
questionlabel
Last Updated: September 2026
Maintainer: Ali-hey-0