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
Metadata
Release files for shield-crypto 2.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| shield_crypto-2.3.0.tar.gz | 98.0 kB | Without provenance | Details for shield_crypto-2.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| shield_crypto-2.3.0-py3-none-any.whl | Python 3 | none | any | Details for shield_crypto-2.3.0-py3-none-any.whl |
Total release size: 219.5 kB
Release files / shield_crypto-2.3.0.tar.gz
| Download URL | shield_crypto-2.3.0.tar.gz |
|---|---|
| Size | 98.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87f1e69f7da16e138da9fc5f96b86c85ac24abf275672066adb1a33c6564515c
|
|
BLAKE2b-256 checksum How to use checksums |
187b31d7c746f8d0648f092b76f3d6b50f2962dc1ce668c46d2ba3143460d404
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / shield_crypto-2.3.0-py3-none-any.whl
| Download URL | shield_crypto-2.3.0-py3-none-any.whl |
|---|---|
| Size | 121.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a24d958de536d5e1e71be34546d9268a10df074e060c69dbd8243003ff2d1cc
|
|
BLAKE2b-256 checksum How to use checksums |
317c1789c2f8c56d315837e88d2aed06d0b0ae996bbf6fad4483acfbbd9421be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Security for shield-crypto 2.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.