add recovery kit function and beta test the Test Wizard function

This commit is contained in:
LC mac
2026-02-21 23:48:59 +08:00
parent 573cdce585
commit 87bf40f27b
9 changed files with 19631 additions and 17185 deletions

View File

@@ -1,6 +1,6 @@
# SeedPGP Web - TailsOS Offline Build # SeedPGP Web - TailsOS Offline Build
Built: Thu 19 Feb 2026 22:31:58 HKT Built: Sat Feb 21 23:47:29 HKT 2026
Usage Instructions: Usage Instructions:
1. Copy this entire folder to a USB drive 1. Copy this entire folder to a USB drive
@@ -17,7 +17,7 @@ Security Features:
- Session-only crypto keys (destroyed on tab close) - Session-only crypto keys (destroyed on tab close)
SHA-256 Checksums: SHA-256 Checksums:
32621ec84de2d13307181ed49050b9ba89429f2c43e340585b9efc189e4c0376 ./assets/index-D4JSYqq2.css
3c716a34a15cf1fb65f5b0e2af025ebc003c9e4e9efbf7c1b1b4c494466d0cbe ./assets/index-rrnn41w7.js
5cbbcb8adc7acc3b78a3fd31c76d573302705ff5fd714d03f5a2602591197cb5 ./assets/secp256k1-Cao5Swmf.wasm 5cbbcb8adc7acc3b78a3fd31c76d573302705ff5fd714d03f5a2602591197cb5 ./assets/secp256k1-Cao5Swmf.wasm
aab3ea208db02b2cb40902850c203f23159f515288b26ca5a131e1188b4362af ./assets/index-DW74Yc8k.css e233270f7e649c773433b6bf85f68012aa95ed6936aa40e5ec11ee8cb9bb164c ./index.html
c5d6ba57285386d3c4a4e082b831ca24e6e925d7e25a4c38533a10e06c37b238 ./assets/index-Bwz_2nW3.js
c7cd63f8c0a39b0aca861668029aa569597e3b4f9bcd2e40aa274598522e0e8e ./index.html

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,10 +8,12 @@
<title>SeedPGP Web</title> <title>SeedPGP Web</title>
<!-- Baseline CSP for generic builds. <!-- Baseline CSP for generic builds.
TailsOS builds override this via Makefile (build-tails target). --> TailsOS builds override this via Makefile (build-tails target).
Commented out for development to avoid CSP issues with WebAssembly.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' blob: data:; font-src 'self'; object-src 'none'; media-src 'self' blob:; base-uri 'self'; form-action 'none';" data-env="tails"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' blob: data:; font-src 'self'; object-src 'none'; media-src 'self' blob:; base-uri 'self'; form-action 'none';" data-env="tails">
<script type="module" crossorigin src="./assets/index-Bwz_2nW3.js"></script> -->
<link rel="stylesheet" crossorigin href="./assets/index-DW74Yc8k.css"> <script type="module" crossorigin src="./assets/index-rrnn41w7.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-D4JSYqq2.css">
</head> </head>
<body> <body>

View File

@@ -1,422 +0,0 @@
## SeedPGP Recovery Playbook - Offline Recovery Guide
**Generated:** Feb 3, 2026 | **SeedPGP v1.4.7** | **Frame Format:** `SEEDPGP1:0:CRC16:BASE45_PAYLOAD`
***
## 📋 Recovery Requirements
```
✅ SEEDPGP1 QR code or printed text
✅ PGP Private Key (.asc file) OR Message Password (if symmetric encryption used)
✅ Offline computer with terminal access
✅ gpg command line tool (GNU Privacy Guard)
```
**⚠️ Important:** This playbook assumes you have the original encryption parameters:
- PGP private key (if PGP encryption was used)
- Private key passphrase (if the key is encrypted)
- Message password (if symmetric encryption was used)
- BIP39 passphrase (if 25th word was used during backup)
***
## 🔓 Step 1: Understand Frame Format
**SeedPGP Frame Structure:**
```
SEEDPGP1:0:CRC16:BASE45_PAYLOAD
```
- **SEEDPGP1:** Protocol identifier
- **0:** Frame version (single frame)
- **CRC16:** 4-character hexadecimal CRC16-CCITT checksum
- **BASE45_PAYLOAD:** Base45-encoded PGP binary data
**Example Frame:**
```
SEEDPGP1:0:58B5:2KO K0S-U. M:E1T*A%50%886N2SDITXSQVE VV$BA7.FZ+I01N%ISK$KBGESBRNOHYIK%A8N1FUOE.Z1T:8JBHDNNBV2AVJRGC1-OY67AU777I07UB88TQN0B5033IJOGG7$2ID/QNIR.:UGUO/M0BH0O94468TXM 0RGSIYT FNSQGNJKDCHP3JV/V-77:%KVZG+6VA7P826W0N0TBI5AMSQX60A%2E$OMWF1TV/J0SJJ 0M-VF0TH60W4TL1/519HS7BO%OT-QGZ5.AS.18AWSGF9O5E%MCYLM4STPI5+.3A5K7ZULFQM.JO:J3/C.IOB1819L8*ME027S9DJ0+18WCVTC30928T72W5D4P0UHC4O11IPRQ I5T39RSI9BTVT6LK6A9PWUF7B2CBEI43M%TT47%I4KBT-0H44L.RP$U02F8-7A*LH2$G44Q.880WF0BJ5SB5OR*39W/N3T9 -DQ4C
```
### Extract Base45 Payload
```bash
# Extract everything after the 3rd colon
FRAME="SEEDPGP1:0:58B5:2KO K0S-U. M:E1T*A%50%886N2SDITXSQVE VV$BA7.FZ+I01N%ISK$KBGESBRNOHYIK%A8N1FUOE.Z1T:8JBHDNNBV2AVJRGC1-OY67AU777I07UB88TQN0B5033IJOGG7$2ID/QNIR.:UGUO/M0BH0O94468TXM 0RGSIYT FNSQGNJKDCHP3JV/V-77:%KVZG+6VA7P826W0N0TBI5AMSQX60A%2E$OMWF1TV/J0SJJ 0M-VF0TH60W4TL1/519HS7BO%OT-QGZ5.AS.18AWSGF9O5E%MCYLM4STPI5+.3A5K7ZULFQM.JO:J3/C.IOB1819L8*ME027S9DJ0+18WCVTC30928T72W5D4P0UHC4O11IPRQ I5T39RSI9BTVT6LK6A9PWUF7B2CBEI43M%TT47%I4KBT-0H44L.RP$U02F8-7A*LH2$G44Q.880WF0BJ5SB5OR*39W/N3T9 -DQ4C"
PAYLOAD=$(echo "$FRAME" | cut -d: -f4-)
echo "$PAYLOAD" > payload.b45
```
***
## 🔓 Step 2: Decode Base45 → PGP Binary
**Option A: Using base45 CLI tool:**
```bash
# Install base45 if needed
npm install -g base45
# Decode the payload
base45decode < payload.b45 > encrypted.pgp
```
**Option B: Using CyberChef (offline browser tool):**
1. Download CyberChef HTML from <https://gchq.github.io/CyberChef/>
2. Open it in an offline browser
3. Input → Paste your Base45 payload
4. Operation → `From Base45`
5. Save output as `encrypted.pgp`
**Option C: Manual verification (check CRC):**
```bash
# Verify CRC16 checksum matches
# The CRC16-CCITT-FALSE checksum should match the value in the frame (58B5 in example)
# If using the web app, this is automatically verified during decryption
```
***
## 🔓 Step 3: Decrypt PGP Binary
### Option A: PGP Private Key Decryption (PKESK)
If the backup was encrypted with a PGP public key:
```bash
# Import your private key (if not already imported)
gpg --import private-key.asc
# List keys to verify fingerprint
gpg --list-secret-keys --keyid-format LONG
# Decrypt using your private key
gpg --batch --yes --decrypt encrypted.pgp
```
**Expected JSON Output:**
```json
{"v":1,"t":"bip39","w":"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about","l":"en","pp":0}
```
**If private key has a passphrase:**
```bash
gpg --batch --yes --passphrase "YOUR-PGP-KEY-PASSPHRASE" --decrypt encrypted.pgp
```
### Option B: Message Password Decryption (SKESK)
If the backup was encrypted with a symmetric password:
```bash
gpg --batch --yes --passphrase "YOUR-MESSAGE-PASSWORD" --decrypt encrypted.pgp
```
**Expected JSON Output:**
```json
{"v":1,"t":"bip39","w":"your seed phrase words here","l":"en","pp":1}
```
***
## 🔓 Step 4: Parse Decrypted Data
The decrypted output is a JSON object with the following structure:
```json
{
"v": 1, // Version (always 1)
"t": "bip39", // Type (always "bip39")
"w": "word1 word2 ...", // BIP39 mnemonic words (lowercase, single spaces)
"l": "en", // Language (always "en" for English)
"pp": 0 // BIP39 passphrase flag: 0 = no passphrase, 1 = passphrase used
}
```
**Extract the mnemonic:**
```bash
# After decryption, extract the 'w' field
DECRYPTED='{"v":1,"t":"bip39","w":"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about","l":"en","pp":0}'
MNEMONIC=$(echo "$DECRYPTED" | grep -o '"w":"[^"]*"' | cut -d'"' -f4)
echo "Mnemonic: $MNEMONIC"
```
***
## 💰 Step 5: Wallet Recovery
### BIP39 Passphrase Status
Check the `pp` field in the decrypted JSON:
- `"pp": 0` → No BIP39 passphrase was used during backup
- `"pp": 1`**BIP39 passphrase was used** (25th word/extra passphrase)
### Recovery Instructions
**Without BIP39 Passphrase (`pp": 0`):**
```
Seed Words: [extracted from 'w' field]
BIP39 Passphrase: None required
```
**With BIP39 Passphrase (`pp": 1`):**
```
Seed Words: [extracted from 'w' field]
BIP39 Passphrase: [Your original 25th word/extra passphrase]
```
**Wallet Recovery Steps:**
1. **Hardware Wallets (Ledger/Trezor):**
- Start recovery process
- Enter 12/24 word mnemonic
- **If `pp": 1`:** Enable passphrase option and enter your BIP39 passphrase
2. **Software Wallets (Electrum, MetaMask, etc.):**
- Create/restore wallet
- Enter mnemonic phrase
- **If `pp": 1`:** Look for "Advanced options" or "Passphrase" field
3. **Bitcoin Core (using `hdseed`):**
```bash
# Use the mnemonic with appropriate BIP39 passphrase
# Consult your wallet's specific recovery documentation
```
***
## 🛠️ GPG Setup (One-time)
**Mac (Homebrew):**
```bash
brew install gnupg
```
**Ubuntu/Debian:**
```bash
sudo apt update && sudo apt install gnupg
```
**Fedora/RHEL/CentOS:**
```bash
sudo dnf install gnupg
```
**Windows:**
- Download Gpg4win from <https://www.gpg4win.org/>
- Install and use Kleopatra or command-line gpg
**Verify installation:**
```bash
gpg --version
```
***
## 🔍 Troubleshooting
| Error | Likely Cause | Solution |
|-------|-------------|----------|
| `gpg: decryption failed: No secret key` | Wrong PGP private key or key not imported | Import correct private key: `gpg --import private-key.asc` |
| `gpg: BAD decrypt` | Wrong passphrase (key passphrase or message password) | Verify you're using the correct passphrase |
| `base45decode: command not found` | base45 CLI tool not installed | Use CyberChef or install: `npm install -g base45` |
| `gpg: no valid OpenPGP data found` | Invalid Base45 decoding or corrupted payload | Verify Base45 decoding step, check for scanning errors |
| `gpg: CRC error` | Frame corrupted during scanning/printing | Rescan QR code or use backup copy |
| `gpg: packet(3) too short` | Truncated PGP binary | Ensure complete frame was captured |
| JSON parsing error after decryption | Output not valid JSON | Check if decryption succeeded, may need different passphrase |
**Common Issues:**
1. **Wrong encryption method:** Trying PGP decryption when symmetric password was used, or vice versa
2. **BIP39 passphrase mismatch:** Forgetting the 25th word used during backup
3. **Frame format errors:** Missing `SEEDPGP1:` prefix or incorrect colon separation
***
## 📦 Recovery Checklist
```
[ ] Airgapped computer prepared (offline, clean OS)
[ ] GPG installed and verified
[ ] Base45 decoder available (CLI tool or CyberChef)
[ ] SEEDPGP1 frame extracted and verified
[ ] Base45 payload decoded to PGP binary
[ ] CRC16 checksum verified (optional but recommended)
[ ] Correct decryption method identified (PGP key vs password)
[ ] Private key imported (if PGP encryption)
[ ] Decryption successful with valid JSON output
[ ] Mnemonic extracted from 'w' field
[ ] BIP39 passphrase status checked ('pp' field)
[ ] Appropriate BIP39 passphrase ready (if 'pp': 1)
[ ] Wallet recovery tool selected (hardware/software wallet)
[ ] Test recovery on testnet/small amount first
[ ] Browser/terminal history cleared after recovery
[ ] Original backup securely stored or destroyed after successful recovery
[ ] Funds moved to new addresses after recovery
```
***
## ⚠️ Security Best Practices
**Critical Security Measures:**
1. **Always use airgapped computer** for recovery operations
2. **Never type mnemonics or passwords on internet-connected devices**
3. **Clear clipboard and terminal history** after recovery
4. **Test with small amounts** before recovering significant funds
5. **Move funds to new addresses** after successful recovery
6. **Destroy recovery materials** or store them separately from private keys
**Storage Recommendations:**
- Print QR code on archival paper or metal
- Store playbook separately from private keys/passphrases
- Use multiple geographically distributed backups
- Consider Shamir's Secret Sharing for critical components
***
## 🔄 Alternative Recovery Methods
**Using the SeedPGP Web App (Online):**
1. Open <https://seedpgp.com> (or local instance)
2. Switch to "Restore" tab
3. Scan QR code or paste SEEDPGP1 frame
4. Provide private key or message password
5. App handles Base45 decoding, CRC verification, and decryption automatically
**Using Custom Script (Advanced):**
```python
# Example Python recovery script (conceptual)
import base45
import gnupg
import json
frame = "SEEDPGP1:0:58B5:2KO K0S-U. M:..."
parts = frame.split(":", 3)
crc_expected = parts[2]
b45_payload = parts[3]
# Decode Base45
pgp_binary = base45.b45decode(b45_payload)
# Decrypt with GPG
gpg = gnupg.GPG()
decrypted = gpg.decrypt(pgp_binary, passphrase="your-passphrase")
# Parse JSON
data = json.loads(str(decrypted))
print(f"Mnemonic: {data['w']}")
print(f"BIP39 Passphrase used: {'YES' if data['pp'] == 1 else 'NO'}")
```
***
## 📝 Technical Details
**Encryption Algorithms:**
- **PGP Encryption:** AES-256 (OpenPGP standard)
- **Symmetric Encryption:** AES-256 with random session key
- **CRC Algorithm:** CRC16-CCITT-FALSE (polynomial 0x1021)
- **Encoding:** Base45 (RFC 9285)
**JSON Schema:**
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["v", "t", "w", "l", "pp"],
"properties": {
"v": {
"type": "integer",
"const": 1,
"description": "Protocol version"
},
"t": {
"type": "string",
"const": "bip39",
"description": "Data type (BIP39 mnemonic)"
},
"w": {
"type": "string",
"pattern": "^[a-z]+( [a-z]+){11,23}$",
"description": "BIP39 mnemonic words (lowercase, space-separated)"
},
"l": {
"type": "string",
"const": "en",
"description": "Language (English)"
},
"pp": {
"type": "integer",
"enum": [0, 1],
"description": "BIP39 passphrase flag: 0 = none, 1 = used"
},
"fpr": {
"type": "array",
"items": {"type": "string"},
"description": "Optional: Recipient key fingerprints"
}
}
}
```
**Frame Validation Rules:**
1. Must start with `SEEDPGP1:`
2. Frame version must be `0` (single frame)
3. CRC16 must be 4 hex characters `[0-9A-F]{4}`
4. Base45 payload must use valid Base45 alphabet
5. Decoded PGP binary must pass CRC16 verification
***
## 🆘 Emergency Contact & Support
**No Technical Support Available:**
- SeedPGP is a self-sovereign tool with no central authority
- You are solely responsible for your recovery
- Test backups regularly to ensure they work
**Community Resources:**
- GitHub Issues: <https://github.com/kccleoc/seedpgp-web/issues>
- Bitcoin StackExchange: Use `seedpgp` tag
- Local Bitcoin meetups for in-person help
**Remember:** The security of your funds depends on your ability to successfully execute this recovery process. Practice with test backups before relying on it for significant amounts.
***
**Print this playbook on archival paper or metal. Store separately from encrypted backups and private keys.** 🔒
**Last Updated:** February 3, 2026
**SeedPGP Version:** 1.4.7
**Frame Example CRC:** 58B5 ✓
**Test Recovery:** [ ] Completed [ ] Not Tested
***

View File

@@ -131,8 +131,8 @@ export const QrDisplay: React.FC<QrDisplayProps> = ({ value, encryptionMode = 'p
</> </>
)} )}
<div>Recovery Guide:</div> <div className="whitespace-nowrap">Recovery Guide:</div>
<div className="text-[#00f0ff]">{metadata.recovery_url}</div> <div className="text-[#00f0ff] break-all text-right min-w-0">{metadata.recovery_url}</div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -62,5 +62,6 @@ export default defineConfig({
'__BUILD_HASH__': JSON.stringify(gitHash), '__BUILD_HASH__': JSON.stringify(gitHash),
'__BUILD_TIMESTAMP__': JSON.stringify(new Date().toISOString()), '__BUILD_TIMESTAMP__': JSON.stringify(new Date().toISOString()),
'global': 'globalThis', 'global': 'globalThis',
} },
}) assetsInclude: ['**/*.md', '**/*.txt'] // Enables raw imports for .txt files
})