LC mac 185efe454f feat: mobile-first redesign and layout improvements
## Major Changes

### Mobile-First Responsive Design
- Converted entire app to mobile-first single-column layout
- Constrained max-width to 448px (mobile phone width)
- Black margins on desktop, centered content
- Removed all multi-column grids (md:grid-cols-3)

### Header Reorganization (3-Row Layout)
- Row 1: App logo + title + version
- Row 2: Security badges + action buttons (Empty, Reset)
- Row 3: Navigation tabs (Create, Backup, Restore, Blender)
- Replaced text buttons with emoji icons (📋 clipboard, 🙈 privacy mask)
- Consistent button sizing across all tabs

### Font Size Reductions
- Reduced all button text sizes for mobile density
- Main buttons: py-4 → py-3, added text-sm
- Labels: text-xs → text-[10px]
- Placeholders: consistent text-[10px] across all inputs
- Input fields: text-sm → text-xs, p-4 → p-3

### Create Tab Improvements
- Changed "GENERATE NEW SEED" from button-style to banner
- Left-aligned banner with gradient background
- Equal-width button grid (12/24 Words, Backup/Seed Blender)
- Used grid-cols-2 for consistent sizing

### Backup Tab Improvements
- Simplified drag-drop area with 📎 emoji
- Reduced padding and text sizes
- Cleaner, shorter copy
- PGP label font size: text-xs → text-[12px]

### SeedBlender Component
- Reorganized mnemonic input cards: textarea on row 1, buttons on row 2
- QR button (left) and X button (right) alignment
- Consistent placeholder text sizing (text-[10px])
- Shortened dice roll placeholder text

### HTTPS Development Server
- Added @vitejs/plugin-basic-ssl for HTTPS in dev mode
- Configured server to listen on 0.0.0.0:5173
- Fixed Web Crypto API issues on mobile (requires secure context)
- Enables testing on iPhone via local network

## Technical Details
- All changes maintain cyberpunk theme and color scheme
- Improved mobile usability and visual consistency
- No functionality changes, pure UI/UX improvements
2026-02-09 21:58:18 +08:00

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


Quick Start

🔒 Backup Your Seed (in 30 seconds)

  1. Run locally (recommended for maximum security):

    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):

    # 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)

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)

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)

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 v1.3.6+ (recommended) or Node.js 18+
  • Git

Quick Install

# 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

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.

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.

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)

# 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)


🧪 Testing & Verification

Test Suite

# 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...


🗺️ 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 file for details.

👤 Author

kccleoc - GitHub
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

Remember: Your seed phrase is the key to your cryptocurrency. Guard it with your life.

Description
No description provided
Readme 4 MiB
Languages
JavaScript 84.1%
TypeScript 15.2%
Makefile 0.5%