SeedPGP v1.1.0

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.

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 for optimal performance

Installation

# 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

Encrypt a Mnemonic

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

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)

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:

{
  plaintext: SeedPgpPlaintext;
  publicKeyArmored?: string;      // PGP public key (PKESK)
  messagePassword?: string;        // Symmetric password (SKESK)
}

Returns:

{
  framed: string;                  // SEEDPGP1 frame
  pgpBytes: Uint8Array;            // Raw PGP message
  recipientFingerprint?: string;   // Key fingerprint
}

decryptSeedPgp(params)

Decrypts a SeedPGP frame.

Parameters:

{
  frameText: string;                // SEEDPGP1 frame
  privateKeyArmored?: string;       // PGP private key
  privateKeyPassphrase?: string;    // Key unlock password
  messagePassword?: string;         // SKESK password
}

Returns: SeedPgpPlaintext object

Testing

# 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

⚠️ 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

Project Structure

seedpgp-web/
├── src/
│   ├── lib/
│   │   ├── seedpgp.ts        # Core encryption/decryption
│   │   ├── seedpgp.test.ts   # Test vectors
│   │   ├── base45.ts         # Base45 codec
│   │   ├── crc16.ts          # CRC16-CCITT-FALSE
│   │   └── types.ts          # TypeScript definitions
│   └── App.tsx               # React UI
├── package.json
└── README.md

Tech Stack

  • Runtime: Bun v1.3.6+
  • Language: TypeScript
  • Crypto: OpenPGP.js v6.3.0
  • Framework: React + Vite
  • Testing: Bun test runner

Roadmap

  • QR code generation UI
  • Multi-frame support for larger payloads
  • Hardware wallet integration
  • Mobile scanning app
  • Shamir Secret Sharing support

License

MIT License - see LICENSE file for details

Author

kccleoc - GitHub

Version History

v1.1.0 (2026-01-28)

  • Initial public release
  • Full BIP39 mnemonic support
  • Trezor test vector validation
  • Production-ready implementation

⚠️ 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.

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