Files
seedpgp-web/README.md
2026-01-30 18:39:30 +08:00

376 lines
11 KiB
Markdown

# 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:** <https://seedpgp-web.pages.dev>
## 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 <https://seedpgp-web.pages.dev> 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:** <https://seedpgp-web.pages.dev>
### 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 **<https://seedpgp-web.pages.dev>** 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.