9 Commits

Author SHA1 Message Date
LC mac
81fbd210ca chore: Bump version to 1.4.3 2026-01-30 18:39:30 +08:00
LC mac
5ea3b92ab1 docs: Update version to 1.4.2 in GEMINI.md 2026-01-30 17:33:08 +08:00
LC mac
eec194fbba docs: Revert deployment process and update version in GEMINI.md 2026-01-30 17:26:27 +08:00
LC mac
24c714fb2f update index to 1.4.2 2026-01-30 02:13:53 +08:00
LC mac
eeb5184b8a Cloudflare Pages migration with enforced CSP headers 2026-01-30 02:11:06 +08:00
LC mac
422fe04a12 fix: Copy _headers to dist during build 2026-01-30 01:59:24 +08:00
LC mac
ebeea79a33 chore: Force Cloudflare rebuild with correct base path 2026-01-30 01:49:01 +08:00
LC mac
faf58dc49d fix: Auto-detect base path for Cloudflare vs GitHub Pages 2026-01-30 01:44:53 +08:00
LC mac
46982794cc feat(v1.4): Add 'Encrypted in memory' badge 2026-01-30 01:25:09 +08:00
6 changed files with 198 additions and 177 deletions

View File

@@ -2,7 +2,7 @@
## Project Overview ## Project Overview
**SeedPGP v1.3.0**: Client-side BIP39 mnemonic encryption webapp **SeedPGP v1.4.3**: Client-side BIP39 mnemonic encryption webapp
**Stack**: Bun + Vite + React + TypeScript + OpenPGP.js + Tailwind CSS **Stack**: Bun + Vite + React + TypeScript + OpenPGP.js + Tailwind CSS
**Deploy**: GitHub Pages (public repo: `seedpgp-web-app`, private source: `seedpgp-web`) **Deploy**: GitHub Pages (public repo: `seedpgp-web-app`, private source: `seedpgp-web`)
**Live URL**: <https://kccleoc.github.io/seedpgp-web-app/> **Live URL**: <https://kccleoc.github.io/seedpgp-web-app/>
@@ -130,9 +130,23 @@ bun run preview # Preview production build
### Deployment Process ### Deployment Process
1. **Private repo** (`seedpgp-web`): Source code, development **Production:** Cloudflare Pages (auto-deploys from `main` branch)
2. **Public repo** (`seedpgp-web-app`): Built files for GitHub Pages **Live URL:** <https://seedpgp-web.pages.dev>
3. **Deploy script** (`scripts/deploy.sh`): Builds + copies to dist/ + pushes to public repo
### Cloudflare Pages Setup
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)
### Git Workflow ### Git Workflow
@@ -141,10 +155,19 @@ bun run preview # Preview production build
git add src/ git add src/
git commit -m "feat(v1.x): description" git commit -m "feat(v1.x): description"
# Tag version # Tag version (triggers auto-deploy to Cloudflare)
git tag v1.x.x git tag v1.x.x
git push origin main --tags git push origin main --tags
# **IMPORTANT: Update README.md before tagging**
# Update the following sections in README.md:
# - Current version number in header
# - Recent Changes section with new features
# - Any new usage instructions or screenshots
# Then commit the README update:
git add README.md
git commit -m "docs: update README for v1.x.x"
# Deploy to GitHub Pages # Deploy to GitHub Pages
./scripts/deploy.sh v1.x.x ./scripts/deploy.sh v1.x.x
``` ```
@@ -291,26 +314,9 @@ await window.runSessionCryptoTest()
--- ---
## Current Version: v1.4.0 ## Current Version: v1.4.3
### Recent Changes (2026-01-30) *Please update the "Recent Changes", "Known Limitations", and "Next Priorities" sections to reflect the current state of the project.*
- ✅ Extended session-key encryption to Restore flow
- ✅ Added 10-second auto-clear timer for restored mnemonic
- ✅ Added Hide button for manual clear
- ✅ Removed debug console logs from sessionCrypto.ts
### Known Limitations
- GitHub Pages cannot set custom CSP headers (need Cloudflare Pages for enforcement)
- Read-only Mode is UI-level only (not browser-enforced)
- Session-key encryption doesn't protect against active XSS/extensions
### Next Priorities (Suggested)
1. Extend session-key encryption to Restore flow
2. Migrate to Cloudflare Pages for real CSP header enforcement
3. Add "Encrypted in memory" badge when encryptedMnemonicCache exists
4. Document reproducible builds (git hash verification)
--- ---
@@ -370,7 +376,6 @@ Check:
Output: ✅ or ❌ for each item + suggest fixes for failures. Output: ✅ or ❌ for each item + suggest fixes for failures.
``` ```
--- ---
**Last Updated**: 2026-01-29 **Last Updated**: 2026-01-29

295
README.md
View File

@@ -1,8 +1,10 @@
# SeedPGP v1.1.0 # SeedPGP v1.4.3
**Secure BIP39 mnemonic backup using PGP encryption and QR codes** **Secure BIP39 mnemonic backup using PGP encryption and QR codes**
A TypeScript/Bun tool for encrypting cryptocurrency seed phrases with OpenPGP and encoding them as QR-friendly Base45 frames with CRC16 integrity checking. 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 ## Features
@@ -11,7 +13,11 @@ A TypeScript/Bun tool for encrypting cryptocurrency seed phrases with OpenPGP an
-**Integrity Checking**: CRC16-CCITT-FALSE checksums prevent corruption -**Integrity Checking**: CRC16-CCITT-FALSE checksums prevent corruption
- 🔑 **BIP39 Support**: Full support for 12/18/24-word mnemonics with passphrase indicator - 🔑 **BIP39 Support**: Full support for 12/18/24-word mnemonics with passphrase indicator
- 🧪 **Battle-Tested**: Validated against official Trezor BIP39 test vectors - 🧪 **Battle-Tested**: Validated against official Trezor BIP39 test vectors
-**Fast**: Built with Bun runtime for optimal performance -**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 ## Installation
@@ -32,7 +38,30 @@ bun run dev
## Usage ## Usage
### Encrypt a Mnemonic ### 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 ```typescript
import { encryptToSeedPgp, buildPlaintext } from "./lib/seedpgp"; import { encryptToSeedPgp, buildPlaintext } from "./lib/seedpgp";
@@ -64,106 +93,42 @@ console.log(decrypted.w); // Recovered mnemonic
console.log(decrypted.pp); // BIP39 passphrase indicator (0 or 1) console.log(decrypted.pp); // BIP39 passphrase indicator (0 or 1)
``` ```
## Deployment to GitHub Pages (FREE) ## Deployment
This project uses a two-repository setup to keep source code private while hosting the app for free. **Production:** Cloudflare Pages (auto-deploys from `main` branch)
**Live URL:** <https://seedpgp-web.pages.dev>
### One-Time Setup ### Cloudflare Pages Setup
#### 1. Create Public Deployment Repo This project is deployed on Cloudflare Pages for enhanced security features:
Go to https://github.com/new and create: 1. **Repository:** `seedpgp-web` (private repo)
- **Name**: `seedpgp-web-app` (or any name you prefer) 2. **Build command:** `bun run build`
- **Visibility**: **Public** 3. **Output directory:** `dist/`
- **Don't** initialize with README, .gitignore, or license 4. **Security headers:** Automatically enforced via `public/_headers`
#### 2. Configure Vite Base Path ### Benefits Over GitHub Pages
Edit `vite.config.ts`: - ✅ 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
```typescript ### Deployment Workflow
export default defineConfig({
plugins: [react()],
base: '/seedpgp-web-app/', // Match your public repo name
})
```
#### 3. Build and Deploy
```bash ```bash
# Build the production bundle # Commit feature
bun run build git add src/
git commit -m "feat(v1.x): description"
# Initialize git in dist folder # Tag version (triggers auto-deploy to Cloudflare)
cd dist git tag v1.x.x
git init git push origin main --tags
git add .
git commit -m "Deploy seedpgp v1.1.0"
# Push to your public repo
git remote add origin https://github.com/kccleoc/seedpgp-web-app.git
git branch -M main
git push -u origin main
# Return to project root
cd ..
``` ```
#### 4. Enable GitHub Pages **No manual deployment needed!** Cloudflare Pages auto-deploys when you push to `main`.
1. Go to `https://github.com/kccleoc/seedpgp-web-app/settings/pages`
2. **Source**: Deploy from a branch
3. **Branch**: Select `main``/` (root)
4. Click **Save**
Wait 1-2 minutes, then visit: **https://kccleoc.github.io/seedpgp-web-app/**
---
### Deploying Updates (v1.2.0, v1.3.0, etc.)
Create `scripts/deploy.sh` in your project root:
```bash
#!/bin/bash
set -e
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: ./scripts/deploy.sh v1.2.0"
exit 1
fi
echo "🔨 Building $VERSION..."
bun run build
echo "📦 Deploying to GitHub Pages..."
cd dist
git add .
git commit -m "Deploy $VERSION" || echo "No changes to commit"
git push
cd ..
echo "✅ Deployed to https://kccleoc.github.io/seedpgp-web-app/"
echo "🏷️ Don't forget to tag: git tag $VERSION && git push --tags"
```
Make executable and use:
```bash
chmod +x scripts/deploy.sh
./scripts/deploy.sh v1.2.0
```
---
### Repository Structure
- **seedpgp-web** (Private) - Your source code, active development
- **seedpgp-web-app** (Public) - Built files only, served via GitHub Pages
**Cost: $0/month**
## Frame Format ## Frame Format
@@ -183,6 +148,7 @@ BASE45 - Base45-encoded PGP message
Creates a SeedPGP plaintext object. Creates a SeedPGP plaintext object.
**Parameters:** **Parameters:**
- `mnemonic` (string): BIP39 mnemonic phrase (12/18/24 words) - `mnemonic` (string): BIP39 mnemonic phrase (12/18/24 words)
- `bip39PassphraseUsed` (boolean): Whether a BIP39 passphrase was used - `bip39PassphraseUsed` (boolean): Whether a BIP39 passphrase was used
- `recipientFingerprints` (string[]): Optional array of recipient key fingerprints - `recipientFingerprints` (string[]): Optional array of recipient key fingerprints
@@ -194,6 +160,7 @@ Creates a SeedPGP plaintext object.
Encrypts a plaintext object to SeedPGP format. Encrypts a plaintext object to SeedPGP format.
**Parameters:** **Parameters:**
```typescript ```typescript
{ {
plaintext: SeedPgpPlaintext; plaintext: SeedPgpPlaintext;
@@ -203,6 +170,7 @@ Encrypts a plaintext object to SeedPGP format.
``` ```
**Returns:** **Returns:**
```typescript ```typescript
{ {
framed: string; // SEEDPGP1 frame framed: string; // SEEDPGP1 frame
@@ -216,6 +184,7 @@ Encrypts a plaintext object to SeedPGP format.
Decrypts a SeedPGP frame. Decrypts a SeedPGP frame.
**Parameters:** **Parameters:**
```typescript ```typescript
{ {
frameText: string; // SEEDPGP1 frame frameText: string; // SEEDPGP1 frame
@@ -256,6 +225,8 @@ bun test --watch
- **cv25519** provides ~128-bit security level - **cv25519** provides ~128-bit security level
- **CRC16** detects QR scan errors (not cryptographic) - **CRC16** detects QR scan errors (not cryptographic)
- Key fingerprint validation prevents wrong-key usage - 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 ### ⚠️ Important Notes
@@ -267,50 +238,129 @@ bun test --watch
### 🔒 Production Deployment Warning ### 🔒 Production Deployment Warning
The GitHub Pages deployment at **https://kccleoc.github.io/seedpgp-web-app/** is for: The Cloudflare Pages deployment at **<https://seedpgp-web.pages.dev>** is for:
- ✅ Testing and demonstration
-Convenient access for personal use -Personal use with enhanced security
- ✅ CSP enforcement blocks all network requests
- ✅ Convenient access from any device
- ⚠️ Always verify the URL before use - ⚠️ Always verify the URL before use
For maximum security with real funds: For maximum security with real funds:
- Run locally: `bun run dev` - Run locally: `bun run dev`
- Or self-host on your own domain with HTTPS - 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 ## Project Structure
``` ```
seedpgp-web/ seedpgp-web/
├── src/ ├── 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/ │ ├── lib/
│ │ ├── seedpgp.ts # Core encryption/decryption │ │ ├── seedpgp.ts # Core encryption/decryption
│ │ ├── seedpgp.test.ts # Test vectors │ │ ├── seedpgp.test.ts # Test vectors
│ │ ├── sessionCrypto.ts # Ephemeral session keys
│ │ ├── base45.ts # Base45 codec │ │ ├── base45.ts # Base45 codec
│ │ ├── crc16.ts # CRC16-CCITT-FALSE │ │ ├── crc16.ts # CRC16-CCITT-FALSE
│ │ ├── qr.ts # QR utilities
│ │ └── types.ts # TypeScript definitions │ │ └── types.ts # TypeScript definitions
── App.tsx # React UI ── App.tsx # Main application
├── scripts/ │ └── main.tsx # React entry point
│ └── deploy.sh # Deployment automation ├── public/
│ └── _headers # Cloudflare CSP headers
├── package.json ├── package.json
├── DEVELOPMENT.md # Development guide ├── vite.config.ts # Vite configuration
├── GEMINI.md # AI agent project brief
└── README.md # This file └── README.md # This file
``` ```
## Tech Stack ## Tech Stack
- **Runtime**: [Bun](https://bun.sh) v1.3.6+ - **Runtime**: [Bun](https://bun.sh) v1.3.6+
- **Language**: TypeScript - **Language**: TypeScript (strict mode)
- **Crypto**: [OpenPGP.js](https://openpgpjs.org) v6.3.0 - **Crypto**: [OpenPGP.js](https://openpgpjs.org) v6.3.0
- **Framework**: React + Vite - **Framework**: React + Vite
- **UI**: Tailwind CSS
- **Icons**: lucide-react
- **QR**: html5-qrcode, qrcode
- **Testing**: Bun test runner - **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 ## Roadmap
- [ ] QR code generation UI - [ ] UI polish (modern crypto wallet design)
- [ ] QR code scanner with camera support
- [ ] Multi-frame support for larger payloads - [ ] Multi-frame support for larger payloads
- [ ] Hardware wallet integration - [ ] Hardware wallet integration
- [ ] Mobile scanning app - [ ] Mobile scanning app
- [ ] Shamir Secret Sharing support - [ ] Shamir Secret Sharing support
- [ ] Reproducible builds with git hash verification
## License ## License
@@ -320,47 +370,6 @@ MIT License - see LICENSE file for details
**kccleoc** - [GitHub](https://github.com/kccleoc) **kccleoc** - [GitHub](https://github.com/kccleoc)
## Version History
### v1.1.0 (2026-01-28)
- Initial public release
- Full BIP39 mnemonic support
- Trezor test vector validation
- Production-ready implementation
- GitHub Pages deployment guide
--- ---
⚠️ **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. ⚠️ **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.
Now create the deployment script:
```bash
mkdir -p scripts
cat > scripts/deploy.sh << 'EOF'
#!/bin/bash
set -e
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: ./scripts/deploy.sh v1.2.0"
exit 1
fi
echo "🔨 Building $VERSION..."
bun run build
echo "📦 Deploying to GitHub Pages..."
cd dist
git add .
git commit -m "Deploy $VERSION" || echo "No changes to commit"
git push
cd ..
echo "✅ Deployed to https://kccleoc.github.io/seedpgp-web-app/"
echo "🏷️ Don't forget to tag: git tag $VERSION && git push --tags"
EOF
chmod +x scripts/deploy.sh
```

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SeedPGP v1.4</title> <title>SeedPGP v1.4.2</title>
</head> </head>
<body> <body>

View File

@@ -1,7 +1,7 @@
{ {
"name": "seedpgp-web", "name": "seedpgp-web",
"private": true, "private": true,
"version": "1.4.0", "version": "1.4.3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

6
public/_headers Normal file
View File

@@ -0,0 +1,6 @@
/*
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'none'; form-action 'none'; base-uri 'self';
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin

View File

@@ -12,7 +12,8 @@ const gitHash = execSync('git rev-parse --short HEAD').toString().trim()
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
base: '/seedpgp-web-app/', base: process.env.CF_PAGES ? '/' : '/seedpgp-web-app/',
publicDir: 'public', // ← Explicitly set (should be default)
build: { build: {
outDir: 'dist', outDir: 'dist',
emptyOutDir: false, emptyOutDir: false,