mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-06 17:37:51 +08:00
Create offline_recovery_playbook.md
This commit is contained in:
658
doc/offline_recovery_playbook.md
Normal file
658
doc/offline_recovery_playbook.md
Normal file
@@ -0,0 +1,658 @@
|
||||
# 🆘 SeedPGP Offline Recovery Playbook
|
||||
|
||||
**EMERGENCY SEED RECOVERY WITHOUT THE SEEDPGP WEB APP**
|
||||
|
||||
---
|
||||
|
||||
## 📋 What This Document Is For
|
||||
|
||||
You created an encrypted backup of your cryptocurrency seed phrase using SeedPGP. This document explains **how to decrypt that backup if the SeedPGP web app is no longer available** (website down, GitHub deleted, domain expired, etc.).
|
||||
|
||||
**Print this document and store it with your encrypted QR backup.**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Quick Reference: What You Need
|
||||
|
||||
Depending on how you encrypted your backup, you need:
|
||||
|
||||
| Encryption Method | What You Need to Decrypt |
|
||||
|-------------------|--------------------------|
|
||||
| **Password-only** | Password + this playbook + any computer with GPG |
|
||||
| **PGP Public Key** | Private key + private key passphrase + this playbook + any computer with GPG |
|
||||
| **Krux KEF format** | Passphrase + Python 3 + this playbook |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Step 1: Identify Your Backup Format
|
||||
|
||||
Look at your encrypted backup. The format determines which recovery method to use:
|
||||
|
||||
### **Format A: SeedPGP Standard (PGP)**
|
||||
|
||||
Your QR code or text starts with:
|
||||
```
|
||||
SEEDPGP1:0:A1B2:CDEFG...
|
||||
```
|
||||
|
||||
**OR** your backup is a PGP armored message:
|
||||
```
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQEMA...
|
||||
-----END PGP MESSAGE-----
|
||||
```
|
||||
|
||||
➜ **Use Method 1: GPG Command-Line Recovery** (see below)
|
||||
|
||||
---
|
||||
|
||||
### **Format B: Krux KEF**
|
||||
|
||||
Your QR code or text starts with:
|
||||
```
|
||||
KEF:1234+ABCD...
|
||||
```
|
||||
|
||||
**OR** it's a Base43-encoded string using only these characters:
|
||||
```
|
||||
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$%*+-./:
|
||||
```
|
||||
|
||||
➜ **Use Method 2: Python Krux Decryption** (see below)
|
||||
|
||||
---
|
||||
|
||||
### **Format C: Plain SeedQR** (NOT ENCRYPTED)
|
||||
|
||||
Your QR is all digits (48 or 96 digits for 12/24 words):
|
||||
```
|
||||
0216007100420461...
|
||||
```
|
||||
|
||||
**OR** all hex (32 or 64 hex characters):
|
||||
```
|
||||
1a2b3c4d5e6f...
|
||||
```
|
||||
|
||||
➜ **Use Method 3: SeedQR Decoder** (see below)
|
||||
|
||||
---
|
||||
|
||||
## 📖 Method 1: GPG Command-Line Recovery (PGP Format)
|
||||
|
||||
### **What You Need:**
|
||||
- ✅ Your encrypted backup (QR scan result or PGP armored text)
|
||||
- ✅ Your password (if password-encrypted)
|
||||
- ✅ Your PGP private key + passphrase (if key-encrypted)
|
||||
- ✅ A computer with GPG installed (Linux/Mac: pre-installed, Windows: download Gpg4win)
|
||||
|
||||
---
|
||||
|
||||
### **Step 1A: Extract the PGP Message**
|
||||
|
||||
If your backup is a `SEEDPGP1:...` QR string, you need to decode it first:
|
||||
|
||||
```bash
|
||||
# Your QR scan result looks like:
|
||||
# SEEDPGP1:0:A1B2:BASE45_ENCODED_DATA_HERE
|
||||
|
||||
# Extract just the Base45 part (everything after the third colon)
|
||||
echo "SEEDPGP1:0:A1B2:BASE45DATA" | cut -d: -f4- > base45.txt
|
||||
|
||||
# Decode Base45 to binary PGP (requires Python script - see Appendix A)
|
||||
python3 decode_base45.py base45.txt > encrypted.pgp
|
||||
```
|
||||
|
||||
If your backup is already a PGP armored message (`-----BEGIN PGP MESSAGE-----`), save it to a file:
|
||||
|
||||
```bash
|
||||
cat > encrypted.asc << 'EOF'
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQEMA...
|
||||
-----END PGP MESSAGE-----
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Step 1B: Decrypt with GPG**
|
||||
|
||||
**If encrypted with PASSWORD only:**
|
||||
|
||||
```bash
|
||||
# Decrypt using password
|
||||
gpg --decrypt encrypted.pgp
|
||||
|
||||
# OR if you have the armored version:
|
||||
gpg --decrypt encrypted.asc
|
||||
|
||||
# GPG will prompt: "Enter passphrase:"
|
||||
# Type your password exactly as you created it
|
||||
# Output will be JSON like: {"v":1,"t":"bip39","w":"word1 word2 word3...","l":"en","pp":0}
|
||||
```
|
||||
|
||||
**If encrypted with PGP PUBLIC KEY:**
|
||||
|
||||
```bash
|
||||
# First, import your private key (if not already in your GPG keyring)
|
||||
gpg --import my-private-key.asc
|
||||
|
||||
# Decrypt
|
||||
gpg --decrypt encrypted.pgp
|
||||
|
||||
# GPG will prompt for your PRIVATE KEY PASSPHRASE (not the backup password)
|
||||
# Output will be JSON like: {"v":1,"t":"bip39","w":"word1 word2 word3...","l":"en","pp":0}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Step 1C: Extract Your Seed Phrase**
|
||||
|
||||
The decrypted output is JSON format:
|
||||
```json
|
||||
{"v":1,"t":"bip39","w":"abandon ability able about above...","l":"en","pp":0}
|
||||
```
|
||||
|
||||
**Your seed phrase is the value of the `"w"` field.**
|
||||
|
||||
Extract it:
|
||||
```bash
|
||||
# If output is in a file:
|
||||
cat decrypted.json | grep -o '"w":"[^"]*"' | cut -d'"' -f4
|
||||
|
||||
# Or use Python:
|
||||
python3 -c 'import json; print(json.load(open("decrypted.json"))["w"])'
|
||||
```
|
||||
|
||||
**Write down your seed phrase immediately on paper.**
|
||||
|
||||
---
|
||||
|
||||
### **JSON Field Meanings:**
|
||||
|
||||
| Field | Meaning | Example Value |
|
||||
|-------|---------|---------------|
|
||||
| `v` | Format version | `1` |
|
||||
| `t` | Mnemonic type | `"bip39"` |
|
||||
| `w` | **Your seed phrase (words)** | `"abandon ability able..."` |
|
||||
| `l` | Language | `"en"` (English) |
|
||||
| `pp` | BIP39 passphrase used? | `0` (no) or `1` (yes) |
|
||||
| `fpr` | Recipient PGP fingerprints | `["ABC123..."]` (optional) |
|
||||
|
||||
**If `pp` is `1`:** You used a BIP39 passphrase in addition to your seed words. You need BOTH to restore your wallet.
|
||||
|
||||
---
|
||||
|
||||
## 🐍 Method 2: Python Krux Decryption (KEF Format)
|
||||
|
||||
### **What You Need:**
|
||||
- ✅ Your Krux KEF backup (QR scan result starting with `KEF:` or Base43 string)
|
||||
- ✅ Your passphrase
|
||||
- ✅ A computer with Python 3
|
||||
|
||||
---
|
||||
|
||||
### **Step 2A: Prepare the Decryption Script**
|
||||
|
||||
Save this Python script as `decrypt_krux.py`:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Krux KEF (Krux Encryption Format) Offline Decryption Tool
|
||||
For emergency recovery when SeedPGP is unavailable
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
from getpass import getpass
|
||||
|
||||
# Base43 alphabet (Krux standard)
|
||||
B43_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$%*+-./:"
|
||||
|
||||
def base43_decode(s):
|
||||
"""Decode Base43 string to bytes"""
|
||||
n = 0
|
||||
for c in s:
|
||||
n = n * 43 + B43_CHARS.index(c)
|
||||
|
||||
byte_len = (n.bit_length() + 7) // 8
|
||||
return n.to_bytes(byte_len, 'big')
|
||||
|
||||
def unwrap_kef(kef_bytes):
|
||||
"""Extract label, version, iterations, and payload from KEF envelope"""
|
||||
if len(kef_bytes) < 5:
|
||||
raise ValueError("Invalid KEF: too short")
|
||||
|
||||
label_len = kef_bytes[0]
|
||||
if label_len > 252 or len(kef_bytes) < 1 + label_len + 4:
|
||||
raise ValueError("Invalid KEF: malformed header")
|
||||
|
||||
label = kef_bytes[1:1+label_len].decode('utf-8')
|
||||
version = kef_bytes[1+label_len]
|
||||
|
||||
iter_bytes = kef_bytes[2+label_len:5+label_len]
|
||||
iterations = int.from_bytes(iter_bytes, 'big')
|
||||
if iterations <= 10000:
|
||||
iterations *= 10000
|
||||
|
||||
payload = kef_bytes[5+label_len:]
|
||||
return label, version, iterations, payload
|
||||
|
||||
def pbkdf2_hmac_sha256(password, salt, iterations, dklen=32):
|
||||
"""PBKDF2-HMAC-SHA256 key derivation"""
|
||||
return hashlib.pbkdf2_hmac('sha256', password.encode(), salt, iterations, dklen)
|
||||
|
||||
def aes_gcm_decrypt(key, iv, ciphertext, tag):
|
||||
"""AES-GCM decryption using cryptography library"""
|
||||
try:
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
aesgcm = AESGCM(key)
|
||||
return aesgcm.decrypt(iv, ciphertext + tag, None)
|
||||
except ImportError:
|
||||
print("ERROR: 'cryptography' library not found.")
|
||||
print("Install with: pip3 install cryptography")
|
||||
raise
|
||||
|
||||
def entropy_to_mnemonic(entropy_bytes):
|
||||
"""Convert entropy to BIP39 mnemonic (requires bip39 library)"""
|
||||
try:
|
||||
from mnemonic import Mnemonic
|
||||
mnemo = Mnemonic("english")
|
||||
return mnemo.to_mnemonic(entropy_bytes)
|
||||
except ImportError:
|
||||
print("ERROR: 'mnemonic' library not found.")
|
||||
print("Install with: pip3 install mnemonic")
|
||||
raise
|
||||
|
||||
def decrypt_krux(kef_data, passphrase):
|
||||
"""Main decryption function"""
|
||||
# Step 1: Decode from Base43 or hex
|
||||
if kef_data.startswith('KEF:'):
|
||||
kef_data = kef_data[4:].strip()
|
||||
|
||||
try:
|
||||
kef_bytes = base43_decode(kef_data)
|
||||
except:
|
||||
try:
|
||||
kef_bytes = bytes.fromhex(kef_data)
|
||||
except:
|
||||
raise ValueError("Invalid KEF format: not Base43 or hex")
|
||||
|
||||
# Step 2: Unwrap KEF envelope
|
||||
label, version, iterations, payload = unwrap_kef(kef_bytes)
|
||||
|
||||
if version not in [20, 21]:
|
||||
raise ValueError(f"Unsupported KEF version: {version}")
|
||||
|
||||
print(f"KEF Label: {label}")
|
||||
print(f"Version: {version} (AES-GCM{' +compress' if version == 21 else ''})")
|
||||
print(f"Iterations: {iterations}")
|
||||
|
||||
# Step 3: Derive key from passphrase
|
||||
salt = label.encode('utf-8')
|
||||
key = pbkdf2_hmac_sha256(passphrase, salt, iterations, 32)
|
||||
|
||||
# Step 4: Extract IV, ciphertext, and tag
|
||||
iv = payload[:12]
|
||||
ciphertext = payload[12:-4]
|
||||
tag = payload[-4:]
|
||||
|
||||
# Step 5: Decrypt
|
||||
decrypted = aes_gcm_decrypt(key, iv, ciphertext, tag)
|
||||
|
||||
# Step 6: Decompress if needed
|
||||
if version == 21:
|
||||
import zlib
|
||||
decrypted = zlib.decompress(decrypted)
|
||||
|
||||
# Step 7: Convert to mnemonic
|
||||
mnemonic = entropy_to_mnemonic(decrypted)
|
||||
return mnemonic
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("KRUX KEF EMERGENCY DECRYPTION TOOL")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
kef_input = input("Paste your KEF backup (Base43 or hex): ").strip()
|
||||
passphrase = getpass("Enter passphrase: ")
|
||||
|
||||
try:
|
||||
mnemonic = decrypt_krux(kef_input, passphrase)
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("SUCCESS! Your seed phrase:")
|
||||
print("=" * 60)
|
||||
print(mnemonic)
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("⚠️ Write this down on paper immediately!")
|
||||
print("⚠️ Never save to disk or take a screenshot!")
|
||||
|
||||
except Exception as e:
|
||||
print()
|
||||
print(f"ERROR: {e}")
|
||||
print()
|
||||
print("Common issues:")
|
||||
print("1. Wrong passphrase")
|
||||
print("2. Missing Python libraries (run: pip3 install cryptography mnemonic)")
|
||||
print("3. Corrupted KEF data")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Step 2B: Install Dependencies**
|
||||
|
||||
```bash
|
||||
# Install required Python libraries
|
||||
pip3 install cryptography mnemonic
|
||||
|
||||
# Or on Ubuntu/Debian:
|
||||
sudo apt install python3-pip
|
||||
pip3 install cryptography mnemonic
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Step 2C: Run the Decryption**
|
||||
|
||||
```bash
|
||||
# Make script executable
|
||||
chmod +x decrypt_krux.py
|
||||
|
||||
# Run it
|
||||
python3 decrypt_krux.py
|
||||
|
||||
# It will prompt:
|
||||
# "Paste your KEF backup (Base43 or hex):"
|
||||
# → Paste your full QR scan result or KEF string
|
||||
|
||||
# "Enter passphrase:"
|
||||
# → Type your passphrase (won't show on screen)
|
||||
|
||||
# Output:
|
||||
# SUCCESS! Your seed phrase:
|
||||
# abandon ability able about above absent absorb...
|
||||
```
|
||||
|
||||
**Write down your seed phrase immediately.**
|
||||
|
||||
---
|
||||
|
||||
## 📱 Method 3: SeedQR Decoder (Unencrypted Format)
|
||||
|
||||
### **What You Need:**
|
||||
- ✅ Your SeedQR backup (all digits or hex)
|
||||
- ✅ BIP39 wordlist (see Appendix B)
|
||||
- ✅ Optional: Python script (see below)
|
||||
|
||||
---
|
||||
|
||||
### **Step 3A: Identify SeedQR Type**
|
||||
|
||||
**Standard SeedQR (all digits):**
|
||||
- 48 digits = 12-word seed
|
||||
- 96 digits = 24-word seed
|
||||
- Each 4 digits = one BIP39 word index (0000-2047)
|
||||
|
||||
**Compact SeedQR (hex):**
|
||||
- 32 hex chars = 12-word seed
|
||||
- 64 hex chars = 24-word seed
|
||||
- Raw entropy encoded as hexadecimal
|
||||
|
||||
---
|
||||
|
||||
### **Step 3B: Manual Decoding (Standard SeedQR)**
|
||||
|
||||
**Example:** `0216007100420461...` (48 digits for 12 words)
|
||||
|
||||
1. Split into 4-digit chunks: `0216`, `0071`, `0042`, `0461`, ...
|
||||
2. Each chunk is a word index (0-2047)
|
||||
3. Look up each index in the BIP39 wordlist (Appendix B)
|
||||
|
||||
```
|
||||
0216 → word #216 = "brick"
|
||||
0071 → word #71 = "appear"
|
||||
0042 → word #42 = "advise"
|
||||
0461 → word #461 = "dove"
|
||||
...
|
||||
```
|
||||
|
||||
**Your seed phrase is the words in order.**
|
||||
|
||||
---
|
||||
|
||||
### **Step 3C: Python Script (Standard SeedQR)**
|
||||
|
||||
Save as `decode_seedqr.py`:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""SeedQR to BIP39 Mnemonic Decoder"""
|
||||
|
||||
# BIP39 English wordlist (2048 words)
|
||||
# Download from: https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
|
||||
# Or see Appendix B of this document
|
||||
|
||||
def load_wordlist(filepath='bip39_wordlist.txt'):
|
||||
with open(filepath) as f:
|
||||
return [line.strip() for line in f]
|
||||
|
||||
def decode_standard_seedqr(qr_digits):
|
||||
"""Decode standard SeedQR (4-digit word indices)"""
|
||||
if len(qr_digits) not in [48, 96]:
|
||||
raise ValueError(f"Invalid length: {len(qr_digits)} (expected 48 or 96)")
|
||||
|
||||
wordlist = load_wordlist()
|
||||
mnemonic = []
|
||||
|
||||
for i in range(0, len(qr_digits), 4):
|
||||
index = int(qr_digits[i:i+4])
|
||||
if index >= 2048:
|
||||
raise ValueError(f"Invalid word index: {index} (max 2047)")
|
||||
mnemonic.append(wordlist[index])
|
||||
|
||||
return ' '.join(mnemonic)
|
||||
|
||||
def decode_compact_seedqr(qr_hex):
|
||||
"""Decode compact SeedQR (hex-encoded entropy)"""
|
||||
if len(qr_hex) not in [32, 64]:
|
||||
raise ValueError(f"Invalid hex length: {len(qr_hex)} (expected 32 or 64)")
|
||||
|
||||
try:
|
||||
from mnemonic import Mnemonic
|
||||
mnemo = Mnemonic("english")
|
||||
entropy = bytes.fromhex(qr_hex)
|
||||
return mnemo.to_mnemonic(entropy)
|
||||
except ImportError:
|
||||
print("ERROR: 'mnemonic' library required for compact SeedQR")
|
||||
print("Install: pip3 install mnemonic")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
qr_data = input("Paste your SeedQR data: ").strip()
|
||||
|
||||
if qr_data.isdigit():
|
||||
mnemonic = decode_standard_seedqr(qr_data)
|
||||
elif all(c in '0123456789abcdefABCDEF' for c in qr_data):
|
||||
mnemonic = decode_compact_seedqr(qr_data)
|
||||
else:
|
||||
print("ERROR: Not a valid SeedQR (must be all digits or all hex)")
|
||||
exit(1)
|
||||
|
||||
print()
|
||||
print("Your seed phrase:")
|
||||
print(mnemonic)
|
||||
print()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Appendix A: Base45 Decoder
|
||||
|
||||
If you need to decode SeedPGP's Base45 format manually, save this as `decode_base45.py`:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Base45 Decoder for SeedPGP Recovery"""
|
||||
|
||||
B45_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
||||
|
||||
def base45_decode(s):
|
||||
"""Decode Base45 string to bytes"""
|
||||
result = []
|
||||
i = 0
|
||||
|
||||
while i < len(s):
|
||||
if i + 2 < len(s):
|
||||
# Process 3 characters → 2 bytes
|
||||
c = B45_CHARS.index(s[i])
|
||||
d = B45_CHARS.index(s[i+1])
|
||||
e = B45_CHARS.index(s[i+2])
|
||||
x = c + d * 45 + e * 45 * 45
|
||||
|
||||
result.append(x // 256)
|
||||
result.append(x % 256)
|
||||
i += 3
|
||||
else:
|
||||
# Process 2 characters → 1 byte
|
||||
c = B45_CHARS.index(s[i])
|
||||
d = B45_CHARS.index(s[i+1])
|
||||
x = c + d * 45
|
||||
|
||||
if x > 255:
|
||||
raise ValueError("Invalid Base45 encoding")
|
||||
result.append(x)
|
||||
i += 2
|
||||
|
||||
return bytes(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
# Read from file
|
||||
with open(sys.argv[1]) as f:
|
||||
b45_input = f.read().strip()
|
||||
else:
|
||||
# Read from stdin
|
||||
b45_input = input("Paste Base45 data: ").strip()
|
||||
|
||||
try:
|
||||
decoded = base45_decode(b45_input)
|
||||
sys.stdout.buffer.write(decoded)
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
exit(1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Appendix B: BIP39 English Wordlist
|
||||
|
||||
**Download the official wordlist:**
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/bitcoin/bips/master/bip-0039/english.txt
|
||||
```
|
||||
|
||||
**Or manually:** The BIP39 English wordlist contains exactly 2048 words, indexed 0-2047:
|
||||
|
||||
```
|
||||
0000: abandon
|
||||
0001: ability
|
||||
0002: able
|
||||
0003: about
|
||||
...
|
||||
2045: zero
|
||||
2046: zone
|
||||
2047: zoo
|
||||
```
|
||||
|
||||
**Full wordlist:** https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Security Recommendations
|
||||
|
||||
### **When Recovering Your Seed:**
|
||||
|
||||
1. ✅ **Use an air-gapped computer** (TailsOS or Ubuntu Live USB)
|
||||
2. ✅ **Disconnect from internet** before decrypting
|
||||
3. ✅ **Write seed on paper immediately** after decryption
|
||||
4. ✅ **Never screenshot or save to disk**
|
||||
5. ✅ **Verify your seed** by importing to a test wallet (small amount first)
|
||||
6. ✅ **Destroy digital traces** after recovery (shutdown amnesic OS)
|
||||
|
||||
### **Storage Best Practices:**
|
||||
|
||||
- 📄 **Print this document** and store with your encrypted backup
|
||||
- 🔐 Store backup + recovery instructions in different locations
|
||||
- 💾 Keep a copy of Python scripts on offline USB
|
||||
- 📦 Include a copy of the BIP39 wordlist (offline reference)
|
||||
- 🗂️ Label everything clearly: "SEEDPGP BACKUP + RECOVERY GUIDE - DO NOT LOSE"
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Emergency Contact
|
||||
|
||||
**If you're stuck:**
|
||||
|
||||
1. Search GitHub for "seedpgp-web" (project may still exist)
|
||||
2. Check Internet Archive: https://web.archive.org/
|
||||
3. Ask in Bitcoin/crypto forums (describe format, don't share actual data!)
|
||||
4. Hire a professional cryptocurrency recovery service (last resort)
|
||||
|
||||
**Never share:**
|
||||
- ❌ Your encrypted backup data with strangers
|
||||
- ❌ Your password or passphrase
|
||||
- ❌ Your PGP private key
|
||||
- ❌ Your decrypted seed phrase
|
||||
|
||||
---
|
||||
|
||||
## ✅ Recovery Checklist
|
||||
|
||||
Before attempting recovery, verify you have:
|
||||
|
||||
- [ ] This printed playbook
|
||||
- [ ] Your encrypted backup (QR code or text file)
|
||||
- [ ] Your password/passphrase written down
|
||||
- [ ] Your PGP private key (if used) + passphrase
|
||||
- [ ] An air-gapped computer (TailsOS/Ubuntu Live recommended)
|
||||
- [ ] GPG installed (for PGP decryption)
|
||||
- [ ] Python 3 + libraries (for Krux/SeedQR decryption)
|
||||
- [ ] BIP39 wordlist (for manual SeedQR decoding)
|
||||
- [ ] Paper and pen (to write recovered seed)
|
||||
|
||||
**If missing any item above, DO NOT PROCEED. Secure it first.**
|
||||
|
||||
---
|
||||
|
||||
## 📅 Recommended Practice Schedule
|
||||
|
||||
**Every 6 months:**
|
||||
1. Test that you can still decrypt your backup
|
||||
2. Verify the recovery tools still work
|
||||
3. Update this playbook if formats change
|
||||
4. Check that your passwords/keys are still accessible
|
||||
|
||||
**Test with a dummy backup first!** Create a test seed, encrypt it, then practice recovery.
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** February 2026
|
||||
**Compatible with:** SeedPGP v1.4.7+
|
||||
|
||||
---
|
||||
|
||||
**🔒 KEEP THIS DOCUMENT SAFE AND ACCESSIBLE 🔒**
|
||||
|
||||
Your encrypted backup is worthless without the ability to decrypt it.
|
||||
Print this. Store it with your backup. Test it regularly.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user