Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

PaperVault Core — Crypto + Shamir + page generation library

PaperVault 📄🔐 is a free open source tool for creating offline paper-based data vaults for your foundational secrets, such as passwords, 2FA recovery codes, digital asset keys, hard drive encryption keys, and other critical data.

This is the core library — the AES-256-GCM crypto, Shamir Secret Sharing, and printable HTML page generation that powers @papervault/cli and @papervault/mcp. The browser app lives at papervault.xyz.

If you just want to back up secrets to paper, install @papervault/cli instead. This package is the building block — useful if you want to embed kit generation in your own tool.

🔐 Overview

@papervault/core is a pure-JavaScript library (no native deps) that:

  • Encrypts plaintext with a fresh AES-256-GCM key (via Web Crypto API)
  • Splits the key into N shares using Shamir's Secret Sharing over GF(2^8), recoverable with any M of N
  • Generates standalone printable HTML pages: one vault page with the ciphertext QR and N key pages with one share QR each
  • Produces the v2 vault format — byte-identical to vaults produced at papervault.xyz, so kits unlock at papervault.xyz/unlock

🚀 Quick Start

npm install @papervault/core
import { createKit } from '@papervault/core';

const kit = await createKit({
  secrets: [
    { kind: 'password', service: 'GitHub', username: 'alice', password: 's3cret' },
    { kind: 'wallet',   name: 'BTC cold', seed: 'abandon abandon ... about' },
  ],
  vaultName: 'My DR Kit',
  threshold: 2,
  shares: 3,
});

// kit.pages = [
//   { kind: 'vault', html: '...', filename: 'vault', ... },
//   { kind: 'key',   html: '...', filename: 'key-1-atom-river', seq: 'Key 1', alias: 'atom-river', ... },
//   ...
// ]
// kit.keyShares, kit.cipherKeyHex, kit.cipherTextHex, kit.cipherIvHex available for round-trip / verify.

Requires Node.js ≥ 24 (needs global crypto and crypto.subtle).

🔑 Public API

Export Purpose
createKit(opts) High-level: compress → encrypt → split → generate HTML. Returns the full Kit.
compress(secrets, freeText?) Convert structured entries into the web-app's ultra-compact JSON. Byte-identical to what papervault.xyz produces.
countUserContent(secrets, freeText?) Character-count for the 300-char limit (matches the web app's getUserContentLength).
encrypt(plaintext) Fresh-key AES-256-GCM. Returns {cipherText, cipherKey, cipherIV} (all hex).
decrypt(cipherTextHex, keyHex, ivHex) Inverse.
splitKey(secretKeyHex, n, t) Shamir split. 32-byte hex key in, array of hex shares out.
combineShares(shares) Inverse — give it ≥ threshold shares, get the original key back.
generateKeyAliases(n) Two-word BIP-39 aliases (e.g. atom-river).
generateVaultId() 6-char public hex ID.
generateVaultPage(opts) / buildVaultBody(opts) Standalone vault HTML, or just its inner body fragment.
generateKeyPage(opts) / buildKeyBody(opts) Same for key pages.
generatePrintAllPage(opts) Single doc concatenating every kit page with CSS page breaks — one print dialog for the whole packet.
generateSelectorPage(opts) Orchestration page used by the CLI's ephemeral print server.
LIMITS {MAX_KEYS: 20, MAX_STORAGE: 300, MAX_QR_PAYLOAD_BYTES: 102400}
VAULT_VERSION '2'
VAULT_IDENT 'papervault.xyz' — sentinel in vault QR payloads.

📄 Vault format

v2 vaults use:

  • AES-256-GCM for encryption (Web Crypto API)
  • Shamir Secret Sharing over GF(2^8) for key splitting (shamir-secret-sharing)
  • Random 12-byte nonce per encryption, generated by crypto.getRandomValues()
  • Level-M QR codes for ~15% damage tolerance

The QR payload shapes match the web app exactly:

// Vault QR (single-QR form; splits to metadata + data QRs if > 420 bytes)
{ id: 1, vault: 'papervault.xyz', version: '2', name, shares, threshold,
  cipherIV, keys: [...], data: cipherText }

// Key QR
{ ident: keyAlias, key: keyShare }

V1 vault decryption (legacy CryptoJS / AES-CTR / secrets.js) lives in the web app, not here — this package is v2-only by design.

🛡️ Security Notes

  • All randomness via crypto.getRandomValues(). Never Math.random().
  • crypto.subtle for AES-GCM — no JS reimplementation of the cipher.
  • No persistent state. The library never writes anywhere.
  • The returned Kit object holds the raw AES key + Shamir shares so the caller can audit / round-trip. Treat it like a secret and drop the reference when done.

For the full PaperVault threat model, see the main app's README and SECURITY.md.

📦 Limits

  • Maximum Keys: 20 (Shamir library constraint)
  • Storage Limit: 300 characters of user content per vault (QR optimization — matches the web app)

createKit enforces both and throws clear errors.

🔗 Related Packages

🤝 Contributing

Contributions are welcome! See the main repo at github.com/boazeb/papervault.

📄 License

MIT — see LICENSE.

🙏 Acknowledgments