SPDF (Secure Portable Document Format) is designed to provide enterprise-grade document security through cryptographic protection and access control.
| Purpose | Algorithm | Standard |
|---|---|---|
| Content Encryption | AES-256-GCM | NIST SP 800-38D |
| Digital Signatures | Ed25519 | RFC 8032 |
| Key Wrapping | AES-256-KW | RFC 3394 |
| Hashing | SHA-256 | FIPS 180-4 |
| Device Fingerprint | HMAC-SHA256 | RFC 2104 |
Master Key (K_master)
├── Server-side only, 256-bit
├── Stored in environment variable (production)
└── Used to wrap document keys
Document Key (k_doc)
├── Per-document, 256-bit
├── Generated using CSPRNG
└── Wrapped with K_master before storage
Signing Key (Ed25519)
├── Per-organization
├── Private key: Server-side only
└── Public key: Embedded in SPDF header
- PDF Content - The actual document data
- Document Keys - Per-document encryption keys
- License Keys - Access tokens for users
- User Data - Email addresses, device information
| Actor | Capability | Motivation |
|---|---|---|
| Unauthorized User | Network access | Access restricted documents |
| Malicious Insider | Server access | Exfiltrate documents |
| External Attacker | Remote exploitation | Data theft |
| Attack | Mitigation |
|---|---|
| File Tampering | Ed25519 signature verification |
| Key Extraction | Server-side key storage, AES-KW wrapping |
| Brute Force | Rate limiting, account lockout |
| Device Cloning | Hardware-based device fingerprinting |
| Replay Attacks | Unique nonces per encryption |
| Man-in-the-Middle | Signature verification (TLS recommended) |
Caution
The following threats are NOT protected against:
- Memory Attacks - Decrypted content exists in RAM during viewing
- Screen Capture - Cannot prevent screenshots or screen recording
- Physical Access - Camera/photography of displayed content
- Client Tampering - Modified viewer application (no code signing enforcement)
- Rubber Hose Attack - Physical coercion to reveal credentials
- Admin Access: Username/password with session tokens
- User Access: License key + device fingerprint validation
- License keys are bound to specific:
- User (email)
- Document (doc_id)
- Device (device_hash)
- Time period (expires_at)
License Validation: 20 requests/minute per IP
Failed Attempts: 5 failures = 15 minute lockout
Device Registration: 30 requests/minute per IP
All security-relevant events are logged:
- License validation attempts
- Device registrations
- Failed authentication
- File access
-
SPDF_MASTER_KEYset from secure secrets manager - Master key is unique per environment
- Master key is backed up securely
- HTTPS enabled with valid TLS certificate
- CORS configured for specific origins
- Firewall rules restrict server access
- Debug mode disabled
- Default credentials changed
- Rate limiting enabled
- Logging configured
- Regular key rotation scheduled
- Access logs monitored
- Vulnerability scanning enabled
- Incident response plan documented
- Scheduled: Every 90 days
- On-demand: After suspected compromise
- Personnel changes: Key staff departure
from crypto.keys import KeyManager
km = KeyManager("org_id")
# 1. Generate new master key
new_key = KeyManager.generate_master_key()
# 2. Get all wrapped keys from database
wrapped_keys = get_all_wrapped_keys_from_db()
# 3. Rotate (re-wrap with new key)
new_wrapped = km.rotate_master_key(new_key, wrapped_keys)
# 4. Update database with new wrapped keys
update_database(new_wrapped)
# 5. Update environment variable
# export SPDF_MASTER_KEY=<new_key_hex>If you discover a security vulnerability:
- Do NOT open a public GitHub issue
- Email: security@yourdomain.com
- Include:
- Description of vulnerability
- Steps to reproduce
- Potential impact
- Allow 90 days for fix before disclosure
SPDF encryption meets requirements for:
- HIPAA - PHI protection (with proper deployment)
- PCI-DSS - Document encryption standards
- GDPR - Personal data protection (encryption at rest)
Note
Compliance depends on proper deployment configuration. Consult your compliance officer.
Last security review: December 2025
| Severity | Count | Status |
|---|---|---|
| Critical | 0 | - |
| High | 0 | - |
| Medium | 2 | Resolved |
| Low | 5 | Resolved |
| Info | 8 | Noted |