Technical Overview

Cryptographic Architecture

How seQRets protects your secrets — algorithms, parameters, and design decisions. No marketing, just facts.

Processing Pipeline

Secret InputPassword + Optional KeyfileArgon2id KDFXChaCha20-Poly1305 EncryptShamir Split (K-of-N)QR Encode (Qards)SHA-256 Verify

The secret enters memory, is encrypted under a key derived from your password (and optional keyfile), split into threshold shares, rendered as QR codes, integrity-verified with SHA-256, and then destroyed. In the desktop app, Rust zeroes the memory with compiler-fence zeroization.

Cryptographic Primitives

AlgorithmPurposeKey / Output SizeLibrary (Web)Library (Desktop)
XChaCha20-Poly1305Authenticated encryption256-bit key, 192-bit nonce@noble/cipherschacha20poly1305 (RustCrypto)
Argon2idKey derivation (KDF)256-bit output@noble/hashesargon2 (RustCrypto)
Shamir's Secret SharingThreshold splittingVariable (matches input)shamir-secret-sharing (Cure53 + Zellic audits)shamir-secret-sharing (Cure53 + Zellic audits)
BIP-32 (XFP only)Hardware-wallet verification32-bit master fingerprint@scure/bip32@scure/bip32
SHA-256Share integrity verification256-bit hash@noble/hashes@noble/hashes

Argon2id Parameters

ParameterValuePurpose
Memory cost64 MB (65536 KiB)Resists GPU/ASIC brute-force attacks by requiring large memory
Iterations (time cost)4Increases computation time per password attempt
Parallelism1Single-threaded derivation — deterministic output
Output length32 bytes (256 bits)Matches XChaCha20-Poly1305 key size
Salt16 bytes (random)Unique per encryption — prevents rainbow tables
Inputpassword_bytes || keyfile_bytesPassword and optional keyfile are concatenated before derivation

Optional Keyfile

seQRets supports an optional keyfile as a second authentication factor. When provided, the keyfile bytes are concatenated with the password bytes before being fed into Argon2id — the keyfile is not hashed separately or used as a pepper.

key = Argon2id(password_bytes || keyfile_bytes, salt, m=65536, t=4, p=1, dkLen=32)

Both the web app (TypeScript) and the desktop app (Rust) use identical concatenation logic. When no keyfile is provided, only the password is used. A wrong or missing keyfile causes the XChaCha20-Poly1305 authentication tag to fail, preventing decryption.

PropertyValue
Generated size32 bytes (256 bits) — CSPRNG
Accepted formats.bin, .key
Maximum upload size2 MB
Integration pointConcatenated with password before Argon2id
AvailabilityWeb app and Desktop app

What keyfiles defend against

  • Keyloggers — a binary file is never typed, so keystroke capture is useless
  • Shoulder surfing — nothing to observe visually during authentication
  • Weak password brute-force — a generated 256-bit keyfile makes brute-force computationally infeasible regardless of password strength
  • Physical coercion — if the keyfile is stored in a separate physical location, the user genuinely cannot decrypt on demand

Important: keyfile loss is irrecoverable

If you encrypt with a keyfile and lose it, the secret cannot be decrypted. There is no recovery mechanism. Back up your keyfile separately from your shares and password.

Shamir's Secret Sharing

Security Type

Information-theoretic

Security does not depend on computational hardness. Fewer than K shares provide zero bits of information about the secret, regardless of computing power.

Quantum Resistance

Yes

Shamir's SSS relies on polynomial interpolation over finite fields, not factoring or discrete logarithms. Quantum computers provide no advantage. While fewer than K shares are in an adversary's hands, the encrypted blob is never reconstructed, so the entire scheme is quantum-safe regardless of which cipher encrypts the underlying secret.

Threshold Semantics

K-of-N configurable

You choose K (threshold) and N (total shares). Any K shares reconstruct; K-1 or fewer reveal nothing. Common choices: 2-of-3, 3-of-5.

Field

GF(256)

Operations are performed in the Galois Field GF(2^8), enabling byte-level splitting with no data expansion.

QR Code Encoding

PropertyValue
Data formatBase64-encoded ciphertext share
Error correctionLevel M (15% recovery)
Maximum capacity~2,953 bytes (Version 40, Level L) — practical limit depends on error correction level
OutputPrintable QR code (Qard) — designed for physical distribution
MetadataShare index, threshold, total shares — included in the QR payload, not stored externally
Integrity hashOptional SHA-256 hash appended as a 4th pipe-delimited segment for tamper detection

Share Format & Integrity Verification

Each share is a pipe-delimited string with an optional SHA-256 integrity hash. The hash covers the first three segments and is verified automatically at generation and restoration.

seQRets|<salt>|<data>|sha256:<64-char hex>

The 4th segment is optional for backward compatibility — legacy 3-part shares are still fully supported. The SHA-256 hash is one-way and reveals nothing about the share contents.

PropertyValue
Hash functionSHA-256 via @noble/hashes/sha256
Hash inputSHA-256("seQRets|salt|data") — covers the 3-part share string
Hash output64 hex characters (~71 chars with sha256: prefix)
Verification (generation)All shares are round-trip verified before being presented
Verification (restore)Desktop: auto-verified on scan/import with visible shield icon. Web: verified silently in the background (no UI) — the hash is still checked round-trip, there's just no indicator shown to the user.
Printed fingerprintDesktop: truncated hash (xxxxxxxx...xxxxxxxx) displayed on exported Qard cards
Backward compatibilityLegacy 3-part shares without hashes are fully supported
SecurityOne-way — the hash cannot be reversed to recover share data

Manual verification

Users can independently verify any share in a terminal:

echo -n "seQRets|salt|data" | shasum -a 256

Hardware-Wallet Verification (BIP-32 XFP)

When restoring a BIP-39 mnemonic, the reveal dialog's SeedQR tab displays the BIP-32 master fingerprint (XFP) beneath the QR — an 8-character hex string derived from the master public key. Many hardware wallets (Jade, Coldcard, Trezor, and others) initialize without ever displaying the mnemonic, but almost all of them show the XFP on the home screen after import. Matching the two proves the correct seed was loaded.

XFP = HDKey.fromMasterSeed(mnemonicToSeedSync(phrase, "")).fingerprint (hex, uppercase)

The seed buffer is zeroized immediately after fingerprint computation. The XFP is derived from the master public key and reveals nothing about the seed — it is safe to display outside the QR's blur halo so users can verify without exposing the QR to bystanders.

PropertyValue
Format8 uppercase hex characters (e.g. 73C5DA0A)
Library@scure/bip32 (audited, same author as @scure/bip39)
Applies toBIP-39 mnemonic secrets only (single or multi-mnemonic/multisig)
PrivacyDerived from the master public key — reveals nothing about the seed
MemorySeed buffer is zeroized after computation
Passphrase caveatIf a BIP-39 passphrase is applied at wallet-import time, the on-device XFP will differ — the value shown here assumes no passphrase

Zero-Knowledge Claims

What "zero-knowledge" means for seQRets

  • No server — all processing happens locally on your device
  • No account — no registration, no login, no user database
  • No cloud storage — nothing is uploaded, synced, or backed up
  • No analytics — no tracking pixels, error reporters, or usage telemetry
  • No network dependency — the app works fully offline after initial load (web) or always (desktop)

Explicit Caveats

  • Bob AI assistant (optional) — if you provide a Google Gemini API key and ask Bob a question, your question is sent to Google's Gemini API. No secret data is included.
  • BTC price display (optional) — the app fetches the current Bitcoin price from a public API for display purposes. No user data is transmitted. Disabled when offline.

Source Code

The full source code is available for audit and independent verification.

For the full web vs. desktop security comparison, see the Security page.