Distribute secrets. Eliminate single points of failure.
An offline-first implementation of Shamir's Secret Sharing, split across a standalone NPM crypto module and a single-file PWA.
PieceKeeper allows you to take a sensitive secret—like a master password, cryptocurrency seed phrase, or private key—and mathematically split it into multiple independent shares. You define a threshold (e.g., "any 3 out of 5 shares"), and the secret can only be reconstructed when that minimum number of shares are brought together. Any fewer reveals nothing.
Shares can be distributed as printable QR codes, written to NFC tags, or saved as plain text. An optional two-factor password adds a second layer of protection so that physical possession of shares alone is not sufficient to decode the secret.
This repository is structured as an NPM workspace containing two packages:
PieceKeeper/
├── packages/
│ ├── core/ → @midnightlogic/piecekeeper-crypto (standalone NPM module)
│ └── pwa/ → PieceKeeper PWA (Vite single-file application)
├── package.json (workspace root)
└── LICENSE
| Package | Description |
|---|---|
packages/core |
Isomorphic Shamir's Secret Sharing + AES-256-GCM encryption. Zero DOM dependencies. Ships ESM, CJS, and TypeScript declarations. |
packages/pwa |
The full PieceKeeper Progressive Web App — camera scanning, NFC, QR code generation, 21 languages, and offline-first architecture. |
A pre-compiled deployment is available via GitHub Pages:
Once loaded over HTTPS, install PieceKeeper as a Progressive Web App (PWA) from the Settings tab. This decouples it from the browser and enables offline access from your device's home screen.
-
Emergency Break Glass Accounts: Split a privileged admin or root credential across multiple team leads (e.g., 3-of-5). The account can only be activated when the required threshold of authorised personnel come together — enforcing dual-control without a single point of compromise.
-
Estate Planning & Inheritance: Split a crypto wallet seed phrase among family members and a lawyer. Funds can only be accessed when the required threshold of parties come together — no single individual can act alone.
-
Geographic Distribution: Split a master backup password and store shares in separate physical locations: a home safe, a bank deposit box, and a secure office. If one location is compromised, the secret remains protected.
-
Distributed Team Access: Split a critical server password or API key among 5 executives, requiring at least 3 to agree before it can be unlocked. No single person holds unilateral access.
- Flexible Thresholds (K of N): Split a secret into up to 64 shares with any threshold you define (e.g., 3-of-5, 4-of-8).
- Two-Factor Password Protection: Optionally encrypt shares with a master password. Reconstruction requires both the physical shares and the password.
- QR Code Generation & Scanning: Generate printable QR codes for each share. Scan them back using the built-in camera scanner—no keyboard input needed.
- NFC Tag Support: Write shares directly to NFC tags and read them back with a simple tap. Ideal for hardware-grade distribution. (Note: NFC features are currently only supported on Chrome for Android).
- Print-Ready PDF Layout: One-click printing with share metadata, QR codes, and clearly formatted instructions.
- 21 Languages: Fully internationalized interface covering English, Spanish, French, German, Italian, Dutch, Polish, Portuguese, Russian, Turkish, Arabic, Hindi, Bengali, Thai, Vietnamese, Indonesian, Japanese, Korean, Hebrew, and Chinese (Simplified + Traditional).
- Progressive Web App: Installable on iOS and Android as a standalone application with full offline capability.
- Dark Mode, Audio Feedback, Haptic Feedback, RTL Support: Configurable theme, optional UI sounds, haptic feedback, and right-to-left layout for Arabic and Hebrew.
- Desktop Width Toggle: Switchable between compact app-like layout and wide desktop layout, with preference persistence.
- Built-in Diagnostic Suite: Run internal cryptographic self-tests to verify your device's compatibility before generating shares.
- Single-File, Zero Dependencies: The build compiles all application logic, cryptographic libraries, stylesheets, translations, and icons into one portable HTML file. Store it on a USB drive, print it to paper, or vault it anywhere.
- Offline by Design: PieceKeeper makes zero network requests. Your data never leaves your device. The application functions identically whether you are connected to the internet or completely air-gapped.
- No Secret Persistence: Secrets and shares are never written to disk,
IndexedDB, or cookies. Only user preferences (theme, language, layout) are stored inlocalStorage. When you close the tab, all cryptographic material is gone.
The cryptographic core is published as a standalone NPM package for use in your own applications:
npm install @midnightlogic/piecekeeper-cryptoimport { splitSecret } from '@midnightlogic/piecekeeper-crypto';
const shares = await splitSecret('my-secret-password', 5, 3, { comment: 'backup-key' });
// → 5 shares, any 3 can reconstructimport { reconstructSecret } from '@midnightlogic/piecekeeper-crypto';
const result = await reconstructSecret(shares.slice(0, 3), '');
console.log(result.secret); // "my-secret-password"import { inspectShare } from '@midnightlogic/piecekeeper-crypto';
const meta = inspectShare(shares[0].share);
console.log(meta.isEncrypted); // false
console.log(meta.familyId); // "a1b2c3d4"Full API documentation →
packages/core/README.md
- Shamir's Secret Sharing (SSS): Generates polynomial shares over a 5-tier prime Galois Field (128-bit to 2048-bit), providing information-theoretic security below the threshold.
- Dynamic Prime Selection (default): Automatically selects the smallest prime that fits the secret, minimising share size and QR code density.
- Stealth Mode (advanced): Forces all shares to the maximum 2048-bit prime with zero-padded payloads, producing uniform-length shares that reveal nothing about the secret's actual size.
- Key Derivation Functions: Six configurable KDF profiles ship with the application:
- Argon2id — Memory-hard, OWASP-recommended. Profiles at 19MB (mobile) and 64MB (default).
- scrypt — Memory-hard legacy alternative at OWASP baseline (N=131072).
- PBKDF2 — CPU-bound, FIPS-140 compliant. Configurable from 100k to 2M iterations with SHA-256/SHA-512.
- Encryption: AES-256-GCM authenticated encryption for all password-protected shares.
- Tamper-Evident Shares: Each share is a self-describing Base64 envelope containing schema version, set identifier, timestamp, and N/K parameters.
- Content Security Policy: Production builds inject SHA-256 script hashes into CSP headers, blocking arbitrary code execution without relying on
unsafe-eval. Frame-ancestors, form-action, and base-uri are locked down. - XSS Protection: All user-supplied data (comments, share strings, metadata) is escaped via
escapeHtml()before any HTML rendering.
Click to expand: Share Binary Format, Prime Fields, Polynomial Arithmetic, and Integrity Checksums
All Shamir arithmetic is performed modulo a prime p in GF(p). PieceKeeper uses a 5-tier prime resolution table, where each prime is the smallest verified prime strictly greater than 2^(B×8):
| Tier | Boundary (B) | Prime | Bit-width | Typical use |
|---|---|---|---|---|
| 0 | 16 bytes | 2¹²⁸ + 51 | 128-bit | Short passwords (≤16 bytes) |
| 1 | 32 bytes | 2²⁵⁶ + 297 | 256-bit | Standard passwords (≤32 bytes) |
| 2 | 64 bytes | 2⁵¹² + 75 | 512-bit | Seed phrases (≤64 bytes) |
| 3 | 128 bytes | 2¹⁰²⁴ + 643 | 1024-bit | Extended secrets (≤128 bytes) |
| 4 | 256 bytes | 2²⁰⁴⁸ + 981 | 2048-bit | Maximum / Stealth mode |
Dynamic Prime Selection (default): The engine selects the smallest tier whose boundary B fits the raw payload length (1 + 1 + secretLen + 4 bytes), minimising share output size and QR code density.
Stealth Mode (advanced): Forces Tier 4 (2048-bit) for all shares regardless of secret length. The payload is zero-padded to the full 256-byte boundary, producing uniform-length shares that reveal nothing about the secret's actual size.
Before polynomial evaluation, the raw secret is wrapped in an integrity envelope:
[0x01 marker] [1-byte secretLen] [secretBytes...] [4-byte SHA-256 checksum] [0x00 padding...]
- Marker (1 byte): Fixed
0x01sentinel for post-reconstruction validation. - Length prefix (1 byte): Encodes the exact byte length of the user's secret, enabling precise extraction after reconstruction.
- Secret bytes: UTF-8 encoded user secret (max 250 bytes).
- Checksum (4 bytes): Truncated SHA-256 of the original secret bytes. Used during reconstruction to detect corruption or tampered shares.
- Padding (Stealth only): Zero bytes to fill to the prime boundary.
This byte array is then converted to a BigInt S — the secret term (a₀) of the polynomial.
For a threshold K, the engine generates a random polynomial of degree K-1:
f(x) = a₀ + a₁x + a₂x² + ... + aₖ₋₁xᵏ⁻¹ (mod p)
Where a₀ = S (the secret) and a₁...aₖ₋₁ are cryptographically random coefficients in [1, p-1] generated via crypto.getRandomValues(). Each share i is the point (i, f(i) mod p).
Given K points (x₁, y₁) ... (xₖ, yₖ), the secret S = f(0) is recovered via:
S = Σᵢ yᵢ · Lᵢ(0) (mod p)
Lᵢ(0) = Πⱼ≠ᵢ (0 - xⱼ) / (xᵢ - xⱼ) (mod p)
Division is computed via modular multiplicative inverse using the extended Euclidean algorithm.
Each share is a self-describing binary packet encoded as Base64URL:
┌──────────────────── AAD (Authenticated Additional Data) ─────────────────────┐
│ [2B version] [1B flags] [1B kdfSchema] [4B timestamp] [4B familyId] [2B commentLen] [commentBytes...] │
└──────────────────────────────────────────────────────────────────────────────┘
┌──── Payload (encrypted if 2FA enabled, plaintext otherwise) ────┐
│ [1B N] [1B K] [1B X-coordinate] [variable: Y-coordinate bytes] │
└─────────────────────────────────────────────────────────────────┘
Flags byte bitmask:
- Bit 0:
isEncrypted(Two-Factor password applied) - Bit 1:
isStealth(Uniform padding active) - Bits 2–4:
primeIndex(0–4, selects which prime tier was used)
The AAD metadata is always plaintext (not encrypted) so shares can be inspected without a password. The inner payload (N, K, X, Y) is encrypted via AES-256-GCM when two-factor is enabled.
When a password is provided:
- Salt generation: 16 bytes from
crypto.getRandomValues(). - Key derivation: The password is stretched via the user-selected KDF (Argon2id / scrypt / PBKDF2) in a dedicated Web Worker thread.
- Encryption: The inner payload + AAD are sealed with AES-256-GCM. The 12-byte IV is prepended to the ciphertext.
- Decryption: During reconstruction, the same KDF is re-derived from the password + salt, and the GCM tag authenticates both the ciphertext and AAD before releasing the inner coordinates.
After every cryptographic operation, all intermediate byte arrays (passwordBytes, secretBytes, checksumBytes, polynomial coefficients) are zeroed via .fill(0). This minimises the window during which sensitive material exists in JavaScript heap memory.
- Navigate to the Generate tab.
- Enter your secret text and set a comment (optional).
- Choose the total number of shares (N) and the reconstruction threshold (K).
- Optionally enable two-factor password protection.
- Tap Generate Shares. The cryptographic engine runs in a background thread to keep the UI responsive.
- Distribute the resulting shares: copy as text, print as QR codes, download as CSV, or write to NFC tags.
- Navigate to the Reconstruct tab.
- Input shares using any combination of: manual paste, QR code scanning, NFC tag tapping, or file upload.
- Once the minimum threshold (K) is met, provide the two-factor password if one was used.
- Tap Reconstruct to reveal the original secret.
- Desktop: Chrome, Firefox, Safari, and Edge. Installable as a desktop PWA.
- Mobile: iOS Safari and Android Chrome. Camera and NFC features require HTTPS or localhost.
- Offline: Once installed as a PWA, the application works without any network connection.
PieceKeeper uses NPM workspaces. All commands are run from the repository root.
git clone https://github.com/MidnightLogic/PieceKeeper.git
cd PieceKeeper
npm installBuilds the core module, then starts the PWA dev server with hot reload:
npm run devBuilds both packages (Core → PWA) and compiles everything into packages/pwa/dist/index.html:
npm run buildnpm run testTo test hardware features (camera, NFC) that require a secure context without deploying to a server:
npm run build
npm run preview:tunnelThis starts a local server and creates a temporary HTTPS tunnel, allowing you to test on mobile devices over your local network.
See SECURITY.md for our vulnerability disclosure policy.
Apache 2.0 License. See LICENSE for details.
