# SeedPGP v1.4.3 **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:** ## Features - ๐Ÿ” **PGP Encryption**: Uses cv25519 (Curve25519) for modern elliptic curve cryptography - ๐Ÿ“ฑ **QR Code Ready**: Base45 encoding optimized for QR code generation - โœ… **Integrity Checking**: CRC16-CCITT-FALSE checksums prevent corruption - ๐Ÿ”‘ **BIP39 Support**: Full support for 12/18/24-word mnemonics with passphrase indicator - ๐Ÿงช **Battle-Tested**: Validated against official Trezor BIP39 test vectors - โšก **Fast**: Built with Bun runtime and Vite for optimal performance - ๐Ÿ”’ **Session-Key Encryption**: Ephemeral AES-GCM-256 encryption for in-memory protection - ๐Ÿ›ก๏ธ **CSP Enforcement**: Real Content Security Policy headers block all network requests - ๐Ÿ“ธ **QR Scanner**: Camera and file upload support for scanning encrypted QR codes - ๐Ÿ‘๏ธ **Security Monitoring**: Real-time storage monitoring and clipboard tracking ## Installation ```bash # Clone repository git clone https://github.com/kccleoc/seedpgp-web.git cd seedpgp-web # Install dependencies bun install # Run tests bun test # Start development server bun run dev ``` ## Usage ### Web Interface Visit or run locally: ```bash bun run dev # Open http://localhost:5173 ``` **Backup Flow:** 1. Enter your BIP39 mnemonic (12/18/24 words) 2. Import PGP public key or set encryption password 3. Click "Backup" to encrypt and generate QR code 4. Save/print QR code for offline storage **Restore Flow:** 1. Scan QR code or paste encrypted text 2. Import PGP private key or enter password 3. Click "Restore" to decrypt mnemonic 4. Mnemonic auto-clears after 10 seconds ### API Usage ```typescript import { encryptToSeedPgp, buildPlaintext } from "./lib/seedpgp"; const mnemonic = "legal winner thank year wave sausage worth useful legal winner thank yellow"; const plaintext = buildPlaintext(mnemonic, false); // false = no BIP39 passphrase used const result = await encryptToSeedPgp({ plaintext, publicKeyArmored: yourPgpPublicKey, }); console.log(result.framed); // SEEDPGP1:0:ABCD:BASE45DATA... console.log(result.recipientFingerprint); // Key fingerprint for verification ``` ### Decrypt a SeedPGP Frame ```typescript import { decryptSeedPgp } from "./lib/seedpgp"; const decrypted = await decryptSeedPgp({ frameText: "SEEDPGP1:0:ABCD:BASE45DATA...", privateKeyArmored: yourPrivateKey, privateKeyPassphrase: "your-key-password", }); console.log(decrypted.w); // Recovered mnemonic console.log(decrypted.pp); // BIP39 passphrase indicator (0 or 1) ``` ## Deployment **Production:** Cloudflare Pages (auto-deploys from `main` branch) **Live URL:** ### Cloudflare Pages Setup This project is deployed on Cloudflare Pages for enhanced security features: 1. **Repository:** `seedpgp-web` (private repo) 2. **Build command:** `bun run build` 3. **Output directory:** `dist/` 4. **Security headers:** Automatically enforced via `public/_headers` ### Benefits Over GitHub Pages - โœ… Real CSP header enforcement (blocks network requests at browser level) - โœ… Custom security headers (X-Frame-Options, X-Content-Type-Options) - โœ… Auto-deploy on push to main - โœ… Build preview for PRs - โœ… Better performance (global CDN) - โœ… Cost: $0/month ### Deployment Workflow ```bash # Commit feature git add src/ git commit -m "feat(v1.x): description" # Tag version (triggers auto-deploy to Cloudflare) git tag v1.x.x git push origin main --tags ``` **No manual deployment needed!** Cloudflare Pages auto-deploys when you push to `main`. ## Frame Format ``` SEEDPGP1:FRAME:CRC16:BASE45DATA SEEDPGP1 - Protocol identifier and version 0 - Frame number (0 = single frame) ABCD - 4-digit hex CRC16-CCITT-FALSE checksum BASE45 - Base45-encoded PGP message ``` ## API Reference ### `buildPlaintext(mnemonic, bip39PassphraseUsed, recipientFingerprints?)` Creates a SeedPGP plaintext object. **Parameters:** - `mnemonic` (string): BIP39 mnemonic phrase (12/18/24 words) - `bip39PassphraseUsed` (boolean): Whether a BIP39 passphrase was used - `recipientFingerprints` (string[]): Optional array of recipient key fingerprints **Returns:** `SeedPgpPlaintext` object ### `encryptToSeedPgp(params)` Encrypts a plaintext object to SeedPGP format. **Parameters:** ```typescript { plaintext: SeedPgpPlaintext; publicKeyArmored?: string; // PGP public key (PKESK) messagePassword?: string; // Symmetric password (SKESK) } ``` **Returns:** ```typescript { framed: string; // SEEDPGP1 frame pgpBytes: Uint8Array; // Raw PGP message recipientFingerprint?: string; // Key fingerprint } ``` ### `decryptSeedPgp(params)` Decrypts a SeedPGP frame. **Parameters:** ```typescript { frameText: string; // SEEDPGP1 frame privateKeyArmored?: string; // PGP private key privateKeyPassphrase?: string; // Key unlock password messagePassword?: string; // SKESK password } ``` **Returns:** `SeedPgpPlaintext` object ## Testing ```bash # Run all tests bun test # Run with verbose output bun test --verbose # Watch mode (auto-rerun on changes) bun test --watch ``` ### Test Coverage - โœ… 15 comprehensive tests - โœ… 8 official Trezor BIP39 test vectors - โœ… Edge cases (wrong key, wrong passphrase) - โœ… Frame format validation - โœ… CRC16 integrity checking ## Security Considerations ### โœ… Best Practices - Uses **AES-256** for symmetric encryption - **cv25519** provides ~128-bit security level - **CRC16** detects QR scan errors (not cryptographic) - Key fingerprint validation prevents wrong-key usage - **Session-key encryption**: Ephemeral AES-GCM-256 for in-memory protection - **CSP headers**: Browser-enforced network blocking via Cloudflare Pages ### โš ๏ธ Important Notes - **Never share your private key or encrypted QR codes publicly** - Store backup QR codes in secure physical locations (safe, safety deposit box) - Use a strong PGP key passphrase (20+ characters) - Test decryption immediately after generating backups - Consider password-only (SKESK) encryption as additional fallback ### ๐Ÿ”’ Production Deployment Warning The Cloudflare Pages deployment at **** is for: - โœ… Personal use with enhanced security - โœ… CSP enforcement blocks all network requests - โœ… Convenient access from any device - โš ๏ธ Always verify the URL before use For maximum security with real funds: - Run locally: `bun run dev` - Or self-host on your own domain with HTTPS - Use an airgapped device for critical operations ### Threat Model (Honest) **What we protect against:** - Accidental persistence to localStorage/sessionStorage - Plaintext secrets lingering in React state after use - Clipboard history exposure (with warnings) **What we DON'T protect against:** - Active XSS or malicious browser extensions - Memory dumps or browser crash reports - JavaScript garbage collection timing (non-deterministic) ## Project Structure ``` seedpgp-web/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ components/ โ”‚ โ”‚ โ”œโ”€โ”€ PgpKeyInput.tsx # PGP key import UI โ”‚ โ”‚ โ”œโ”€โ”€ QrDisplay.tsx # QR code generation โ”‚ โ”‚ โ”œโ”€โ”€ QrScanner.tsx # Camera + file scanner โ”‚ โ”‚ โ”œโ”€โ”€ ReadOnly.tsx # Read-only mode toggle โ”‚ โ”‚ โ”œโ”€โ”€ StorageIndicator.tsx # Storage monitoring โ”‚ โ”‚ โ”œโ”€โ”€ SecurityWarnings.tsx # Context alerts โ”‚ โ”‚ โ””โ”€โ”€ ClipboardTracker.tsx # Clipboard monitoring โ”‚ โ”œโ”€โ”€ lib/ โ”‚ โ”‚ โ”œโ”€โ”€ seedpgp.ts # Core encryption/decryption โ”‚ โ”‚ โ”œโ”€โ”€ seedpgp.test.ts # Test vectors โ”‚ โ”‚ โ”œโ”€โ”€ sessionCrypto.ts # Ephemeral session keys โ”‚ โ”‚ โ”œโ”€โ”€ base45.ts # Base45 codec โ”‚ โ”‚ โ”œโ”€โ”€ crc16.ts # CRC16-CCITT-FALSE โ”‚ โ”‚ โ”œโ”€โ”€ qr.ts # QR utilities โ”‚ โ”‚ โ””โ”€โ”€ types.ts # TypeScript definitions โ”‚ โ”œโ”€โ”€ App.tsx # Main application โ”‚ โ””โ”€โ”€ main.tsx # React entry point โ”œโ”€โ”€ public/ โ”‚ โ””โ”€โ”€ _headers # Cloudflare CSP headers โ”œโ”€โ”€ package.json โ”œโ”€โ”€ vite.config.ts # Vite configuration โ”œโ”€โ”€ GEMINI.md # AI agent project brief โ””โ”€โ”€ README.md # This file ``` ## Tech Stack - **Runtime**: [Bun](https://bun.sh) v1.3.6+ - **Language**: TypeScript (strict mode) - **Crypto**: [OpenPGP.js](https://openpgpjs.org) v6.3.0 - **Framework**: React + Vite - **UI**: Tailwind CSS - **Icons**: lucide-react - **QR**: html5-qrcode, qrcode - **Testing**: Bun test runner - **Deployment**: Cloudflare Pages ## Version History ### v1.4.3 (2026-01-30) - โœ… Fixed textarea contrast for readability - โœ… Fixed overlapping floating boxes - โœ… Polished UI with modern crypto wallet design - โœ… Updated background color to be lighter ### v1.4.2 (2026-01-30) - โœ… Migrated to Cloudflare Pages for real CSP enforcement - โœ… Added "Encrypted in memory" badge when mnemonic locked - โœ… Improved security header configuration - โœ… Updated deployment documentation ### 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 - โœ… Removed debug console logs from production ### v1.3.0 (2026-01-28) - โœ… Implemented ephemeral session-key encryption (AES-GCM-256) - โœ… Auto-clear mnemonic after QR generation (Backup flow) - โœ… Encrypted cache for sensitive state - โœ… Manual Lock/Clear functionality ### v1.2.0 (2026-01-27) - โœ… Added storage monitoring (StorageIndicator) - โœ… Added security warnings (context-aware) - โœ… Added clipboard tracking - โœ… Implemented read-only mode ### v1.1.0 (2026-01-26) - โœ… Initial public release - โœ… QR code generation and scanning - โœ… Full BIP39 mnemonic support - โœ… Trezor test vector validation - โœ… Production-ready implementation ## Roadmap - [ ] UI polish (modern crypto wallet design) - [ ] Multi-frame support for larger payloads - [ ] Hardware wallet integration - [ ] Mobile scanning app - [ ] Shamir Secret Sharing support - [ ] Reproducible builds with git hash verification ## License MIT License - see LICENSE file for details ## Author **kccleoc** - [GitHub](https://github.com/kccleoc) --- โš ๏ธ **Disclaimer**: This software is provided as-is. Always test thoroughly before trusting with real funds. The author is not responsible for lost funds due to software bugs or user error.