# SeedPGP v1.4.5 **Secure BIP39 mnemonic backup using PGP encryption and QR codes** A client-side web app for encrypting cryptocurrency seed phrases with OpenPGP and encoding them as QR-friendly Base45 frames with CRC16 integrity checking. **Live App:** --- ## ✨ Quick Start ### πŸ”’ Backup Your Seed (in 30 seconds) 1. **Run locally** (recommended for maximum security): ```bash git clone https://github.com/kccleoc/seedpgp-web.git cd seedpgp-web bun install bun run dev # Open http://localhost:5173 ``` 2. **Enter your 12/24-word BIP39 mnemonic** 3. **Choose encryption method**: - **Option A**: Upload your PGP public key (`.asc` file or paste) - **Option B**: Set a strong password (AES-256 encryption) 4. **Click "Generate QR Backup"** β†’ Save/print the QR code ### πŸ”“ Restore Your Seed 1. **Scan the QR code** (camera or upload image) 2. **Provide decryption key**: - PGP private key + passphrase (if using PGP) - Password (if using password encryption) 3. **Mnemonic appears for 10 seconds** β†’ auto-clears for security --- ## πŸ›‘οΈ Explicit Threat Model Documentation ### 🎯 What SeedPGP Protects Against (Security Guarantees) SeedPGP is designed to protect against specific threats when used correctly: | Threat | Protection | Implementation Details | |--------|------------|------------------------| | **Accidental browser storage** | Real-time monitoring & alerts for localStorage/sessionStorage | StorageDetails component shows all browser storage activity | | **Clipboard exposure** | Clipboard tracking with warnings and history clearing | ClipboardDetails tracks all copy operations, shows what/when | | **Network leaks** | Strict CSP headers blocking ALL external requests | Cloudflare Pages enforces CSP: `default-src 'self'; connect-src 'none'` | | **Wrong-key usage** | Key fingerprint validation prevents wrong-key decryption | OpenPGP.js validates recipient fingerprints before decryption | | **QR corruption** | CRC16-CCITT-FALSE checksum detects scanning/printing errors | Frame format includes 4-digit hex CRC for integrity verification | | **Memory persistence** | Session-key encryption with auto-clear timers | AES-GCM-256 session keys, 10-second auto-clear for restored mnemonics | | **Shoulder surfing** | Read-only mode blurs sensitive data, disables inputs | Toggle blurs content, disables form inputs, prevents clipboard operations | ### ⚠️ **Critical Limitations & What SeedPGP CANNOT Protect Against** **IMPORTANT: Understand these limitations before trusting SeedPGP with significant funds:** | Threat | Reason | Recommended Mitigation | |--------|--------|-----------------------| | **Browser extensions** | Malicious extensions can read DOM, memory, keystrokes | Use dedicated browser with all extensions disabled; consider browser isolation | | **Memory analysis** | JavaScript cannot force immediate memory wiping; strings may persist in RAM | Use airgapped device, reboot after use, consider hardware wallets | | **XSS attacks** | If hosting server is compromised, malicious JS could be injected | Host locally from verified source, use Subresource Integrity (SRI) checks | | **Hardware keyloggers** | Physical device compromise at hardware/firmware level | Use trusted hardware, consider hardware wallets for large amounts | | **Supply chain attacks** | Compromised dependencies (OpenPGP.js, React, etc.) | Audit dependencies regularly, verify checksums, consider reproducible builds | | **Quantum computers** | Future threat to current elliptic curve cryptography | Store encrypted backups physically, rotate periodically, monitor crypto developments | | **Browser bugs/exploits** | Zero-day vulnerabilities in browser rendering engine | Keep browsers updated, use security-focused browsers (Brave, Tor) | | **Screen recording** | Malware or built-in OS screen recording | Use privacy screens, be aware of surroundings during sensitive operations | | **Timing attacks** | Potential side-channel attacks on JavaScript execution | Use constant-time algorithms where possible, though limited in browser context | ### πŸ”¬ Technical Security Architecture **Encryption Stack:** - **PGP Encryption:** OpenPGP.js with AES-256 (OpenPGP standard) - **Session Keys:** Web Crypto API AES-GCM-256 with `extractable: false` - **Key Derivation:** PBKDF2 for password-based keys (when used) - **Integrity:** CRC16-CCITT-FALSE checksums on all frames - **Encoding:** Base45 (RFC 9285) for QR-friendly representation **Memory Management Limitations:** - JavaScript strings are immutable and may persist in memory after "clearing" - Garbage collection timing is non-deterministic and implementation-dependent - Browser crash dumps may contain sensitive data in memory - The best practice is to minimize exposure time and use airgapped devices ### πŸ† Best Practices for Maximum Security 1. **Airgapped Workflow** (Recommended for large amounts): ``` [Online Device] β†’ Generate PGP keypair β†’ Export public key [Airgapped Device] β†’ Run SeedPGP locally β†’ Encrypt with public key [Airgapped Device] β†’ Print QR code β†’ Store physically [Online Device] β†’ Never touches private key or plaintext seed ``` 2. **Local Execution** (Next best): ```bash # Clone and run offline git clone https://github.com/kccleoc/seedpgp-web.git cd seedpgp-web bun install # Disable network, then run bun run dev -- --host 127.0.0.1 ``` 3. **Cloudflare Pages** (Convenient but trust required): - βœ… Real CSP enforcement (blocks network at browser level) - βœ… Security headers (X-Frame-Options, X-Content-Type-Options) - ⚠️ Trusts Cloudflare infrastructure - ⚠️ Requires HTTPS connection --- ## πŸ“š Simple Usage Examples ### Example 1: Password-only Encryption (Simplest) ```typescript import { encryptToSeed, decryptFromSeed } from "./lib/seedpgp"; // Backup with password const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; const result = await encryptToSeed({ plaintext: mnemonic, messagePassword: "MyStrongPassword123!", }); console.log(result.framed); // "SEEDPGP1:0:ABCD:BASE45DATA..." // Restore with password const restored = await decryptFromSeed({ frameText: result.framed, messagePassword: "MyStrongPassword123!", }); console.log(restored.w); // Original mnemonic ``` ### Example 2: PGP Key Encryption (More Secure) ```typescript import { encryptToSeed, decryptFromSeed } from "./lib/seedpgp"; const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- ... your public key here ... -----END PGP PUBLIC KEY BLOCK-----`; const privateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- ... your private key here ... -----END PGP PRIVATE KEY BLOCK-----`; // Backup with PGP key const result = await encryptToSeed({ plaintext: mnemonic, publicKeyArmored: publicKey, }); // Restore with PGP key const restored = await decryptFromSeed({ frameText: result.framed, privateKeyArmored: privateKey, privateKeyPassphrase: "your-key-password", }); ``` ### Example 3: Krux-Compatible Encryption (Hardware Wallet Users) ```typescript import { encryptToSeed, decryptFromSeed } from "./lib/seedpgp"; // Krux mode uses passphrase-only encryption const result = await encryptToSeed({ plaintext: mnemonic, messagePassword: "MyStrongPassphrase", mode: 'krux', kruxLabel: 'Main Wallet Backup', kruxIterations: 200000, }); // Hex format compatible with Krux firmware console.log(result.framed); // Hex string starting with KEF: ``` --- ## πŸ”§ Installation & Development ### Prerequisites - [Bun](https://bun.sh) v1.3.6+ (recommended) or Node.js 18+ - Git ### Quick Install ```bash # Clone and install git clone https://github.com/kccleoc/seedpgp-web.git cd seedpgp-web bun install # Run tests bun test # Start development server bun run dev # Open http://localhost:5173 ``` ### Production Build ```bash bun run build # Build to dist/ bun run preview # Preview production build ``` --- ## πŸ” Advanced Security Features ### Session-Key Encryption - **AES-GCM-256** ephemeral keys for in-memory protection - Auto-destroys on tab close/navigation - Manual lock/clear button for immediate wiping ### Storage Monitoring - Real-time tracking of localStorage/sessionStorage - Alerts for sensitive data detection - Visual indicators of storage usage ### Clipboard Protection - Tracks all copy operations - Shows what was copied and when - One-click history clearing ### Read-Only Mode - Blurs all sensitive data - Disables all inputs - Prevents clipboard operations - Perfect for demonstrations or shared screens --- ## πŸ“– API Reference ### Core Functions #### `encryptToSeed(params)` Encrypts a mnemonic to SeedPGP format. ```typescript interface EncryptionParams { plaintext: string | SeedPgpPlaintext; // Mnemonic or plaintext object publicKeyArmored?: string; // PGP public key (optional) messagePassword?: string; // Password (optional) mode?: 'pgp' | 'krux'; // Encryption mode kruxLabel?: string; // Label for Krux mode kruxIterations?: number; // PBKDF2 iterations for Krux } const result = await encryptToSeed({ plaintext: "your mnemonic here", messagePassword: "optional-password", }); // Returns: { framed: string, pgpBytes?: Uint8Array, recipientFingerprint?: string } ``` #### `decryptFromSeed(params)` Decrypts a SeedPGP frame. ```typescript interface DecryptionParams { frameText: string; // SEEDPGP1 frame or KEF hex privateKeyArmored?: string; // PGP private key (optional) privateKeyPassphrase?: string; // Key password (optional) messagePassword?: string; // Message password (optional) mode?: 'pgp' | 'krux'; // Encryption mode } const plaintext = await decryptFromSeed({ frameText: "SEEDPGP1:0:ABCD:...", messagePassword: "your-password", }); // Returns: SeedPgpPlaintext { v: 1, t: "bip39", w: string, l: "en", pp: number } ``` ### Frame Format ``` SEEDPGP1:FRAME:CRC16:BASE45DATA β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ Protocol & Frame CRC16 Base45-encoded Version Number Check PGP Message Examples: β€’ SEEDPGP1:0:ABCD:J9ESODB... # Single frame β€’ KEF:0123456789ABCDEF... # Krux Encryption Format (hex) ``` --- ## πŸš€ Deployment Options ### Option 1: Localhost (Most Secure) ```bash # Run on airgapped machine bun run dev -- --host 127.0.0.1 # Browser only connects to localhost, no external traffic ``` ### Option 2: Self-Hosted (Balanced) - Build: `bun run build` - Serve `dist/` via NGINX/Apache with HTTPS - Set CSP headers (see `public/_headers`) ### Option 3: Cloudflare Pages (Convenient) - Auto-deploys from GitHub - Built-in CDN and security headers - [seedpgp-web.pages.dev](https://seedpgp-web.pages.dev) --- ## πŸ§ͺ Testing & Verification ### Test Suite ```bash # Run all tests bun test # Run specific test categories bun test --test-name-pattern="Trezor" # BIP39 test vectors bun test --test-name-pattern="CRC" # Integrity checks bun test --test-name-pattern="Krux" # Krux compatibility # Watch mode (development) bun test --watch ``` ### Test Coverage - βœ… **15 comprehensive tests** including edge cases - βœ… **8 official Trezor BIP39 test vectors** - βœ… **CRC16 integrity validation** (corruption detection) - βœ… **Wrong key/password** rejection testing - βœ… **Frame format parsing** (malformed input handling) --- ## πŸ“ Project Structure ``` seedpgp-web/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ components/ # React UI components β”‚ β”‚ β”œβ”€β”€ PgpKeyInput.tsx # PGP key import (drag & drop) β”‚ β”‚ β”œβ”€β”€ QrDisplay.tsx # QR code generation β”‚ β”‚ β”œβ”€β”€ QRScanner.tsx # Camera + file scanning β”‚ β”‚ β”œβ”€β”€ SecurityWarnings.tsx # Threat model display β”‚ β”‚ β”œβ”€β”€ StorageDetails.tsx # Storage monitoring β”‚ β”‚ └── ClipboardDetails.tsx # Clipboard tracking β”‚ β”œβ”€β”€ lib/ β”‚ β”‚ β”œβ”€β”€ seedpgp.ts # Core encryption/decryption β”‚ β”‚ β”œβ”€β”€ sessionCrypto.ts # AES-GCM session key management β”‚ β”‚ β”œβ”€β”€ krux.ts # Krux KEF compatibility β”‚ β”‚ β”œβ”€β”€ bip39.ts # BIP39 validation β”‚ β”‚ β”œβ”€β”€ base45.ts # Base45 encoding/decoding β”‚ β”‚ └── crc16.ts # CRC16-CCITT-FALSE checksums β”‚ β”œβ”€β”€ App.tsx # Main application β”‚ └── main.tsx # React entry point β”œβ”€β”€ public/ β”‚ └── _headers # Cloudflare security headers β”œβ”€β”€ package.json β”œβ”€β”€ vite.config.ts β”œβ”€β”€ RECOVERY_PLAYBOOK.md # Offline recovery guide └── README.md # This file ``` --- ## πŸ”„ Version History ### v1.4.5 (2026-02-07) - βœ… **Fixed QR Scanner bugs** related to camera initialization and race conditions. - βœ… **Improved error handling** in the scanner to prevent crashes and provide better feedback. - βœ… **Stabilized component props** to prevent unnecessary re-renders and fix `AbortError`. ### v1.4.4 (2026-02-03) - βœ… **Enhanced security documentation** with explicit threat model - βœ… **Improved README** with simple examples and best practices - βœ… **Better air-gapped usage guidance** for maximum security - βœ… **Version bump** with security audit improvements ### v1.4.3 (2026-01-30) - βœ… Fixed textarea contrast for readability - βœ… Fixed overlapping floating boxes - βœ… Polished UI with modern crypto wallet design ### v1.4.2 (2026-01-30) - βœ… Migrated to Cloudflare Pages for real CSP enforcement - βœ… Added "Encrypted in memory" badge - βœ… Improved security header configuration ### v1.4.0 (2026-01-29) - βœ… Extended session-key encryption to Restore flow - βœ… Added 10-second auto-clear timer for restored mnemonic - βœ… Added manual Hide button for immediate clearing [View full version history...](https://github.com/kccleoc/seedpgp-web/releases) --- ## πŸ—ΊοΈ Roadmap ### Short-term (v1.5.x) - [ ] Enhanced BIP39 validation (full wordlist + checksum) - [ ] Multi-frame support for larger payloads - [ ] Hardware wallet integration (Trezor/Keystone) ### Medium-term - [ ] Shamir Secret Sharing support - [ ] Mobile companion app (React Native) - [ ] Printable paper backup templates - [ ] Encrypted cloud backup with PBKDF2 ### Long-term - [ ] BIP85 child mnemonic derivation - [ ] Quantum-resistant algorithm options - [ ] Cross-platform desktop app (Tauri) --- ## βš–οΈ License MIT License - see [LICENSE](LICENSE) file for details. ## πŸ‘€ Author **kccleoc** - [GitHub](https://github.com/kccleoc) **Security Audit**: v1.4.4 audited for vulnerabilities, no exploits found --- ## ⚠️ Important Disclaimer **CRYPTOGRAPHY IS HARD. USE AT YOUR OWN RISK.** This software is provided as-is, without warranty of any kind. Always: 1. **Test with small amounts** before trusting with significant funds 2. **Verify decryption works** immediately after creating backups 3. **Keep multiple backup copies** in different physical locations 4. **Consider professional advice** for large cryptocurrency holdings The author is not responsible for lost funds due to software bugs, user error, or security breaches. --- ## πŸ†˜ Getting Help - **Issues**: [GitHub Issues](https://github.com/kccleoc/seedpgp-web/issues) - **Security Concerns**: Private disclosure via GitHub security advisory - **Recovery Help**: See [RECOVERY_PLAYBOOK.md](RECOVERY_PLAYBOOK.md) for offline recovery instructions **Remember**: Your seed phrase is the key to your cryptocurrency. Guard it with your life.