Xrecall: Physical MFA Recovery
Authentication credentials lost forever when phones die. Xrecall backs up TOTP seeds, OAuth tokens, passkeys, recovery codes, API keys, and certificates as K-of-N XorIDA QR shares. Print them, store them physically, scan to recover. Information-theoretically secure. No cloud, no server, no electronic backup.
Executive Summary
MFA and authentication credentials die with your phone. Xrecall backs them up as printed QR codes using information-theoretically secure XorIDA threshold sharing.
Two functions cover all use cases: backupCredentials() splits an array of credentials (TOTP seeds, OAuth refresh tokens, passkeys, recovery codes, API keys, certificates) into K-of-N QR-printable shares using XorIDA. Each share is wrapped in an Xformat binary envelope and Base45-encoded for QR alphanumeric mode. restoreCredentials() scans any K QR codes, validates integrity via HMAC-SHA256, reconstructs the plaintext via XorIDA, and deserializes the credentials.
With N=3 and K=2, you print three QR codes and store them in separate physical locations (safe, bank vault, trusted family). Any two reconstruct all credentials. One lost share is tolerated. An attacker who compromises any single share learns mathematically nothing about the plaintext — not computationally hard to break, but impossible regardless of computational power.
No cloud backup. No server. No phone restore. No App Store. Just paper and a QR scanner.
Developer Experience
Xrecall provides structured error codes with actionable hints to help developers build reliable credential recovery flows.
Error Categories
Xrecall organizes 20+ error codes across 7 categories:
| Category | Example Codes | When |
|---|---|---|
| Configuration | INVALID_CONFIG, N_TOO_SMALL, K_EXCEEDS_N | Split configuration validation (n, k, ownerDid) |
| Credential | INVALID_CREDENTIAL, EMPTY_SECRET, UNKNOWN_TYPE | Credential field validation (type, issuer, account, secret) |
| Encryption | ENCRYPT_FAILED, PASSPHRASE_DERIVATION | Optional AES-256-GCM passphrase layer |
| Split | SPLIT_FAILED, HMAC_FAILED | XorIDA split or HMAC generation |
| Reconstruct | HMAC_MISMATCH, INSUFFICIENT_SHARES | Share validation, HMAC verification, reconstruction |
| Deserialize | RECONSTRUCT_FAILED:DESERIALIZE | JSON parsing or PKCS#7 unpad failure |
| Decrypt | DECRYPT, BAD_PASSPHRASE | Optional AES-256-GCM decryption |
Result<T, RecoveryError>. Errors are values, not thrown exceptions. This enables exhaustive error handling and makes error cases explicit in the type system.
const restore = await restoreCredentials(qrShares); if (!restore.ok) { switch (restore.error) { case 'RECONSTRUCT_FAILED:HMAC_MISMATCH': console.error('Share data corrupted or tampered.'); break; case 'RECONSTRUCT_FAILED:INSUFFICIENT_SHARES': console.error('Not enough shares. Need at least k.'); break; case 'RECONSTRUCT_FAILED:BAD_PASSPHRASE': console.error('Incorrect passphrase.'); break; default: console.error(`Reconstruction failed: ${restore.error}`); } return; } console.log(restore.value.credentials);
The Problem
Authentication credentials are locked in your phone. When the phone dies, your credentials die with it.
Lost phones mean lost access. TOTP seeds, OAuth refresh tokens, passkeys, recovery codes — all gone. Most MFA apps don't offer backup. The ones that do rely on cloud sync, which creates a centralized target and requires trust in the provider.
Cloud backup is a single point of failure. Apple Keychain, Google Authenticator sync, 1Password — one compromise exposes all credentials. One rogue employee, one server breach, one legal subpoena, and your entire authentication stack is exposed.
Recovery codes are brittle. Paper printouts fade. Digital copies on your laptop aren't airgapped. Storing them in a password manager defeats the purpose of multi-factor authentication.
Physical security tokens lock you in. YubiKeys cost money per device. If you lose it, you're locked out. Backup keys double the attack surface.
| Method | Backup | Cloud Required | Fault Tolerance | Information-Theoretic |
|---|---|---|---|---|
| Google Authenticator Sync | Cloud | Yes | No | No |
| Apple Keychain | iCloud | Yes | No | No |
| 1Password | Cloud | Yes | No | No |
| Paper recovery codes | Physical | No | No | No |
| YubiKey | Physical | No | Manual dups | No |
| Xrecall | Physical QR | No | K-of-N | XorIDA |
The Old Way
The New Way
Real-World Use Cases
Six scenarios where Xrecall replaces cloud MFA backup with physical QR shares.
Executive loses phone during travel. IT scans 2 of 3 QR codes from company safe and restores all corporate MFA seeds within minutes. No account lockout, no support ticket escalation.
2-of-3 TOTP seedsPrint 3 QR codes for personal accounts (Gmail, banking, crypto). Store one at home, one in bank vault, one with family. Phone theft or loss is recoverable without cloud dependency.
3-of-5 all credential typesLaw firm backs up OAuth refresh tokens for document management systems. Shares distributed across partner offices. Cloud backup would waive privilege under some jurisdictions.
2-of-3 OAuth tokensTrading desk API keys backed up as QR shares. Keys for Bloomberg Terminal, exchanges, internal systems. Physical distribution across secured locations ensures regulatory compliance.
API keys + certificatesDevOps team backs up passkeys and recovery codes for production infrastructure. Shares distributed to on-call engineers. Hardware token loss doesn't block incident response.
Passkeys + recovery codesHospital staff TOTP seeds for EHR access. Shares stored in facility security office. HIPAA compliance requires no cloud transmission of authentication credentials.
2-of-2 TOTP seedsSolution Architecture
Four building blocks compose to deliver physical credential recovery with information-theoretic security.
Integration Guide
Install, backup, print, and restore in under 20 lines of code.
Installation
npm install @private.me/authrecovery
Backup Credentials
import { backupCredentials } from '@private.me/authrecovery'; // 1. Define credentials to back up const credentials = [ { type: 'TOTP_SEED', issuer: 'GitHub', account: 'dev@github.com', secret: new Uint8Array([72, 101, 108, 108, 111]), }, { type: 'API_KEY', issuer: 'Stripe', account: 'acct_123', secret: new TextEncoder().encode('sk_live_xyzabc'), label: 'Production key', expiresAt: '2027-12-31T23:59:59Z', }, ]; // 2. Split into 3 shares, any 2 reconstruct (2-of-3) const config = { n: 3, k: 2, ownerDid: 'did:key:z6MkOwner123', }; const backup = await backupCredentials(credentials, config); if (!backup.ok) throw new Error(backup.error); // 3. Print each share as a QR code for (const share of backup.value.shares) { console.log(`Share ${share.shareId}/${share.n}: ${share.qrData}`); // Use a QR library like 'qrcode' to generate printable QR images }
Restore Credentials
import { restoreCredentials } from '@private.me/authrecovery'; // 1. Scan any K QR codes const scannedQrStrings = [ backup.value.shares[0].qrData, backup.value.shares[2].qrData, ]; // 2. Restore credentials const restore = await restoreCredentials(scannedQrStrings); if (!restore.ok) throw new Error(restore.error); console.log(restore.value.credentials); // [{ type: 'TOTP_SEED', issuer: 'GitHub', ... }, { type: 'API_KEY', ... }]
Optional Passphrase Layer
// Add AES-256-GCM encryption before splitting const config = { n: 3, k: 2, ownerDid: 'did:key:z6MkOwner123', passphrase: 'correct-horse-battery-staple', // PBKDF2 derived }; const backup = await backupCredentials(credentials, config);
Security Model
Six cryptographic guarantees deliver credential recovery with information-theoretic security.
Information-Theoretic Security
XorIDA threshold sharing over GF(2) provides unconditional security. Fewer than K shares reveal zero information about the plaintext, regardless of computational power. This is not "hard to break" — it is mathematically impossible to extract any credential data from K-1 shares, even with infinite compute.
HMAC Before Reconstruction
HMAC-SHA256 integrity verification runs before any credential data is deserialized. The HMAC key and signature are prepended to the padded data before splitting, so they travel with the shares. During restore, the HMAC is verified immediately after reconstruction. Corrupted or tampered shares are rejected before JSON parsing or unpadding. This prevents maliciously crafted shares from triggering parser vulnerabilities.
No Plaintext Persistence
Credentials exist in memory only during backup and restore operations. The library never writes plaintext to disk, network, or any persistent storage. Shares are Base45-encoded for QR code printing, which happens outside the library (via user-provided QR generation tools). The only outputs are in-memory JavaScript values.
Xformat Envelope Routing
Each share is wrapped in a standard Xformat binary envelope with the XRECALL product type. The envelope header contains the IDA5 magic bytes, version, product type, K/N parameters, and a UUID. During restore, all shares must have the same UUID, product type, n, and k. Shares from different backups cannot be mixed. Mismatched product types are rejected before reconstruction.
Consistency Validation
The restore function validates that all provided shares are consistent:
- → Same UUID (shares from the same backup operation)
- → Same n and k (same threshold configuration)
- → Same product type (XRECALL only)
- → No duplicate share indices
Providing the same share twice is detected and rejected before reconstruction. Mixing shares from different backups returns RECONSTRUCT_FAILED with a mismatch hint.
Optional Passphrase Layer
If a passphrase is provided in the RecoveryConfig, the serialized credentials are encrypted with AES-256-GCM before splitting. The passphrase is derived using PBKDF2 with 600,000 iterations and a random salt. The salt and nonce are prepended to the ciphertext before splitting. During restore, the passphrase is required for decryption. Incorrect passphrase returns RECONSTRUCT_FAILED:BAD_PASSPHRASE.
Performance Benchmarks
Sub-millisecond splits for typical credential sets. QR code generation is the bottleneck, not XorIDA.
| Credential Count | K-of-N | Backup Time | Restore Time |
|---|---|---|---|
| 5 | 2-of-3 | <1ms | <1ms |
| 10 | 2-of-3 | ~1ms | ~1ms |
| 20 | 3-of-5 | ~2ms | ~2ms |
| 50 | 2-of-2 | ~4ms | ~4ms |
| 100 | 2-of-2 | ~8ms | ~8ms |
Benchmarked on Node.js 22.x on Apple M2. QR code generation (via external libraries like qrcode) adds ~10-50ms per share depending on error correction level and size. Base45 encoding is sub-millisecond for all tested payloads.
Honest Limitations
Xrecall solves credential recovery. It does not solve physical security, QR code durability, or user error.
Physical Security
If all K shares are stored in the same location, an attacker with physical access can reconstruct the credentials. XorIDA provides information-theoretic security against partial compromise (fewer than K shares), but cannot protect against full compromise (K or more shares from the same backup).
QR Code Durability
Printed QR codes fade over time. Ink degrades, paper yellows, thermal printers fade in months. Use archival-quality paper and ink (acid-free, pigment-based) for long-term storage. Laminating QR codes extends durability but introduces risk of adhesive degradation. Test scannability annually.
No Automatic Rotation
Xrecall backs up credentials as they exist at the moment of backup. If a TOTP seed is rotated or an API key is revoked after backup, the QR shares become stale. Users must manually create new backups after credential rotation. There is no automatic synchronization or update mechanism.
User Error
Xrecall cannot prevent users from storing all shares in one location, printing on thermal paper, or losing K shares simultaneously. The library provides cryptographic guarantees, not operational discipline. Physical distribution and share management are the user's responsibility.
QR Scanner Availability
Restore requires a QR code scanner (smartphone camera, dedicated scanner, webcam with QR library). If no scanner is available during an emergency, the shares cannot be read. Consider storing at least one share in a format that can be manually transcribed (Base45 alphanumeric text is human-readable, though error-prone).
No Cloud Sync
Xrecall has no server, no cloud sync, no remote backup. This is a design feature for privacy, but it means recovery is only possible with physical access to K shares. Lost shares are permanently lost. If K shares are destroyed (fire, flood, theft), the credentials are permanently unrecoverable.
Developer Reference
Complete error taxonomy and API surface documentation.
Error Taxonomy
Complete list of error codes, causes, and recovery actions.
| Error Code | Category | Cause |
|---|---|---|
| INVALID_CONFIG | Configuration | General configuration validation error |
| INVALID_CONFIG:N_TOO_SMALL | Configuration | n < 2 (minimum 2 shares required) |
| INVALID_CONFIG:K_TOO_SMALL | Configuration | k < 2 (minimum 2-of-n threshold) |
| INVALID_CONFIG:K_EXCEEDS_N | Configuration | k > n (threshold exceeds total shares) |
| INVALID_CONFIG:MISSING_OWNER | Configuration | Empty or whitespace-only ownerDid |
| INVALID_CREDENTIAL | Credential | General credential validation error |
| INVALID_CREDENTIAL:EMPTY_SECRET | Credential | Credential secret is zero-length Uint8Array |
| INVALID_CREDENTIAL:MISSING_ISSUER | Credential | Empty or whitespace-only issuer field |
| INVALID_CREDENTIAL:MISSING_ACCOUNT | Credential | Empty or whitespace-only account field |
| INVALID_CREDENTIAL:UNKNOWN_TYPE | Credential | Credential type not in valid set (TOTP_SEED, OAUTH_REFRESH, PASSKEY, RECOVERY_CODE, API_KEY, CERTIFICATE) |
| NO_CREDENTIALS | Credential | Empty credential array passed to backupCredentials |
| ENCRYPT_FAILED | Encryption | AES-256-GCM encryption failure (passphrase mode) |
| ENCRYPT_FAILED:PASSPHRASE_DERIVATION | Encryption | PBKDF2 key derivation from passphrase failed |
| SPLIT_FAILED | Split | XorIDA split or Xformat envelope serialization failure |
| HMAC_FAILED | Split | HMAC-SHA256 generation failure |
| RECONSTRUCT_FAILED | Reconstruct | General reconstruction failure (envelope parsing, product mismatch, UUID mismatch, duplicate shares) |
| RECONSTRUCT_FAILED:HMAC_MISMATCH | Reconstruct | HMAC integrity check failed — share data corrupted or tampered |
| RECONSTRUCT_FAILED:INSUFFICIENT_SHARES | Reconstruct | Fewer than k shares provided to restoreCredentials |
| RECONSTRUCT_FAILED:DESERIALIZE | Reconstruct | Credential JSON deserialization or PKCS#7 unpad failure |
| RECONSTRUCT_FAILED:DECRYPT | Decrypt | AES-256-GCM decryption failure (passphrase mode) |
| RECONSTRUCT_FAILED:BAD_PASSPHRASE | Decrypt | Incorrect passphrase for encrypted backup |
Full API Surface
Complete TypeScript signatures for all exported functions and types.
Splits an array of credentials into K-of-N QR-printable shares using XorIDA. Returns n shares wrapped in Xformat envelopes and Base45-encoded for QR code printing. Pipeline: credentials → JSON serialize → PKCS#7 pad → HMAC sign → XorIDA split → Xformat envelope → Base45 encode.
Reconstructs credentials from scanned QR share strings. Pipeline: Base45 decode → Xformat deserialize → validate consistency → XorIDA reconstruct → HMAC verify → PKCS#7 unpad → JSON parse. Returns the original credentials on success.
Types
interface Credential { readonly type: 'TOTP_SEED' | 'OAUTH_REFRESH' | 'PASSKEY' | 'RECOVERY_CODE' | 'API_KEY' | 'CERTIFICATE'; readonly issuer: string; readonly account: string; readonly secret: Uint8Array; readonly label?: string; readonly expiresAt?: string; // ISO 8601 } interface RecoveryConfig { readonly n: number; // Total shares (2-255) readonly k: number; // Threshold (2-n) readonly ownerDid: string; // DID of credential owner readonly passphrase?: string; // Optional AES-256-GCM layer } interface RecoveryShare { readonly shareId: number; // 1-based share index readonly qrData: string; // Base45-encoded envelope readonly ownerDid: string; readonly credentialCount: number; readonly n: number; readonly k: number; } interface RecoveryResult { readonly backupId: string; // UUID readonly shares: ReadonlyArray<RecoveryShare>; // n shares readonly credentialCount: number; readonly backedUpAt: string; // ISO 8601 readonly ownerDid: string; } interface RecoveryReconstructResult { readonly backupId: string; // UUID from backup readonly credentials: ReadonlyArray<Credential>; // Restored credentials readonly restoredAt: string; // ISO 8601 }
Deployment Options
SaaS Recommended
Fully managed infrastructure. Call our REST API, we handle scaling, updates, and operations.
- Zero infrastructure setup
- Automatic updates
- 99.9% uptime SLA
- Enterprise SLA available
SDK Integration
Embed directly in your application. Runs in your codebase with full programmatic control.
npm install @private.me/authrecovery- TypeScript/JavaScript SDK
- Full source access
- Enterprise support available
On-Premise Upon Request
Enterprise CLI for compliance, air-gap, or data residency requirements.
- Complete data sovereignty
- Air-gap capable deployment
- Custom SLA + dedicated support
- Professional services included
Enterprise On-Premise Deployment
While authRecovery is primarily delivered as SaaS or SDK, we build dedicated on-premise infrastructure for customers with:
- Regulatory mandates — HIPAA, SOX, FedRAMP, CMMC requiring self-hosted processing
- Air-gapped environments — SCIF, classified networks, offline operations
- Data residency requirements — EU GDPR, China data laws, government mandates
- Custom integration needs — Embed in proprietary platforms, specialized workflows
Includes: Enterprise CLI, Docker/Kubernetes orchestration, RBAC, audit logging, and dedicated support.