Authenticated symmetric encryption library
Project description
Shield - Authenticated Symmetric Encryption
Symmetric authenticated encryption with 256-bit keys (~128-bit post-quantum security).
Why Shield?
Shield builds on well-established symmetric primitives (SHA-256, HMAC-SHA256, PBKDF2). A 256-bit key gives 256-bit classical and ~128-bit post-quantum brute-force resistance, assuming these primitives are secure:
- PBKDF2-SHA256 for key derivation (600,000 iterations)
- SHA256-based stream cipher (AES-256-CTR equivalent)
- HMAC-SHA256 for authentication
Installation
pip install shield-crypto
Quick Start
Basic Encryption
from shield import Shield
# Password-based encryption
s = Shield("my_password", "github.com")
encrypted = s.encrypt(b"secret data")
decrypted = s.decrypt(encrypted) # b"secret data"
Pre-shared Key
from shield import quick_encrypt, quick_decrypt
import os
key = os.urandom(32)
encrypted = quick_encrypt(key, b"data")
decrypted = quick_decrypt(key, encrypted)
Post-Quantum Hybrid Key Exchange
For two parties who have never shared a secret, derive a session key over an open network that stays safe even against a future quantum computer ("harvest now, decrypt later"). It runs a classical (X25519) and a quantum-safe (ML-KEM-768) exchange and mixes both — an attacker must break both to win.
Requires the optional extra: pip install shield-crypto[pq].
from shield import Shield
from shield.pqhybrid import HybridPrivateKey, HybridPublicKey, initiate
# Recipient (Bob): generate a keypair, publish the public key anywhere.
bob = HybridPrivateKey.generate()
bob_public = bob.public_key().to_bytes()
# Sender (Alice): derive a shared key + a handshake to send.
handshake, key = initiate(HybridPublicKey.from_bytes(bob_public))
ciphertext = Shield.with_key(key).encrypt(b"hello bob") # send handshake + ciphertext
# Recipient (Bob): recover the same key and decrypt.
bob_key = bob.accept(handshake)
Shield.with_key(bob_key).decrypt(ciphertext) # b"hello bob"
See examples/pq_hybrid_demo.py for a narrated run.
Large File Encryption
from shield import StreamCipher
cipher = StreamCipher.from_password("password", b"salt")
cipher.encrypt_file("large.bin", "large.bin.enc")
cipher.decrypt_file("large.bin.enc", "large.bin.dec")
Forward Secrecy (Ratchet)
from shield import RatchetSession
import os
root_key = os.urandom(32) # Exchanged via secure channel
alice = RatchetSession(root_key, is_initiator=True)
bob = RatchetSession(root_key, is_initiator=False)
# Each message uses a new key
encrypted = alice.encrypt(b"Hello!")
decrypted = bob.decrypt(encrypted) # b"Hello!"
TOTP (2FA)
from shield import TOTP
# Setup
secret = TOTP.generate_secret()
totp = TOTP(secret)
# Get QR code URI for authenticator apps
uri = totp.provisioning_uri("user@example.com", "MyApp")
# Generate/verify codes
code = totp.generate()
is_valid = totp.verify(code) # True
Web Framework Integrations
FastAPI
from fastapi import FastAPI, Depends
from shield.integrations import ShieldMiddleware, ShieldTokenAuth
app = FastAPI()
# Encrypt all JSON responses automatically
app.add_middleware(ShieldMiddleware, password="secret", service="api.example.com")
# Token-based authentication
auth = ShieldTokenAuth(password="secret", service="api.example.com")
@app.post("/login")
async def login(username: str, password: str):
# Verify credentials...
token = auth.create_token(user_id=username, roles=["user"])
return {"token": token}
@app.get("/protected")
async def protected(user: dict = Depends(auth)):
return {"user_id": user["sub"], "roles": user["roles"]}
Flask
from flask import Flask
from shield.integrations import ShieldFlask, shield_required
app = Flask(__name__)
shield = ShieldFlask(app, password="secret", service="api.example.com")
@app.route("/protected")
@shield_required(password="secret", service="api.example.com")
def protected():
from flask import g
return {"user_id": g.shield_user["sub"]}
Rate Limiting
from shield.integrations import RateLimiter, APIProtector
# Simple rate limiter
limiter = RateLimiter(password="secret", service="api", max_requests=100, window=60)
if limiter.is_allowed(user_id):
process_request()
else:
return "Rate limit exceeded", 429
# Full API protection
protector = APIProtector(password="secret", service="api")
protector.add_rate_limit(max_requests=100, window=60)
protector.add_ip_blacklist(["1.2.3.0/24"])
result = protector.check_request(client_ip=request.remote_addr, user_id=user_id)
if not result.allowed:
return {"error": result.reason}, 403
Encrypted Cookies
from shield.integrations import EncryptedCookie
cookie = EncryptedCookie(password="secret", service="api.example.com")
# Encode session data
session_value = cookie.encode({"user_id": "123", "role": "admin"})
# Set cookie header
header = cookie.make_header("session", {"user_id": "123"})
# "session=...; Secure; HttpOnly; SameSite=Strict"
# Decode from request
data = cookie.decode(request.cookies.get("session"))
CLI Usage
# Encrypt a file
shield encrypt secret.txt -o secret.enc
# Decrypt a file
shield decrypt secret.enc -o secret.txt
# Generate random key
shield keygen
# Setup TOTP
shield totp-setup --account user@example.com
# Generate TOTP code
shield totp-code JBSWY3DPEHPK3PXP
API Reference
Shield
Main encryption class with password-derived keys.
Shield(password: str, service: str, salt: bytes = None, iterations: int = 600_000)
Shield.with_key(key: bytes) # Create from raw 32-byte key
.encrypt(plaintext: bytes) -> bytes
.decrypt(ciphertext: bytes) -> Optional[bytes]
StreamCipher
Streaming encryption for large files.
StreamCipher(key: bytes, chunk_size: int = 65536)
StreamCipher.from_password(password: str, salt: bytes)
.encrypt_file(in_path: str, out_path: str)
.decrypt_file(in_path: str, out_path: str)
.encrypt(data: bytes) -> bytes
.decrypt(data: bytes) -> bytes
RatchetSession
Forward secrecy with key ratcheting.
RatchetSession(root_key: bytes, is_initiator: bool)
.encrypt(plaintext: bytes) -> bytes
.decrypt(ciphertext: bytes) -> Optional[bytes]
TOTP
Time-based One-Time Passwords (RFC 6238).
TOTP(secret: bytes, digits: int = 6, interval: int = 30, algorithm: str = "sha1")
TOTP.generate_secret() -> bytes
TOTP.secret_to_base32(secret: bytes) -> str
TOTP.secret_from_base32(b32: str) -> bytes
.generate(timestamp: int = None) -> str
.verify(code: str, timestamp: int = None, window: int = 1) -> bool
.provisioning_uri(account: str, issuer: str = "Shield") -> str
Security Model
Shield builds on well-established symmetric primitives. Like all practical ciphers, their security is conjectural (it relies on standard assumptions), not unconditional:
- Symmetric encryption (AES-256 equivalent)
- Hash functions (SHA-256)
- HMAC authentication
- Key derivation (PBKDF2)
Brute-forcing a full 256-bit key requires 2^256 operations; this relies on the standard assumption that SHA-256/HMAC have no exploitable structure (an assumption, not a mathematical proof).
License
MIT License - Use freely.
See Also
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shield_crypto-2.3.0.tar.gz.
File metadata
- Download URL: shield_crypto-2.3.0.tar.gz
- Upload date:
- Size: 98.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87f1e69f7da16e138da9fc5f96b86c85ac24abf275672066adb1a33c6564515c
|
|
| MD5 |
2710177cda7b2d98b8c8a9984e4277e9
|
|
| BLAKE2b-256 |
187b31d7c746f8d0648f092b76f3d6b50f2962dc1ce668c46d2ba3143460d404
|
File details
Details for the file shield_crypto-2.3.0-py3-none-any.whl.
File metadata
- Download URL: shield_crypto-2.3.0-py3-none-any.whl
- Upload date:
- Size: 121.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a24d958de536d5e1e71be34546d9268a10df074e060c69dbd8243003ff2d1cc
|
|
| MD5 |
9f70981c668ddc82295f062aea327d7c
|
|
| BLAKE2b-256 |
317c1789c2f8c56d315837e88d2aed06d0b0ae996bbf6fad4483acfbbd9421be
|