272-bit entropy · Hybrid quantum-safe crypto · 42 languages · 256 icons · 16-bit checksum
Write your seed in any language. Recover it in any other. Or skip words entirely — select the icons.
🇬🇧 dog |
🇪🇸 perro |
🇫🇷 chien |
🇩🇪 Hund |
🇯🇵 犬 |
🇰🇷 개 |
🇷🇺 собака |
🇸🇦 كلب |
🐕 |
One concept. Infinite expressions. One universal recovery.
Screenshots from Signer's treasury app
![]() |
![]() |
![]() |
The 36-word seed is quantum-safe by design. Its 272-bit entropy survives Grover's algorithm with 136-bit post-quantum security — well above the 128-bit floor. The 24-word compact format (176-bit) is designed for classical use; for quantum-safe key derivation, use 36 words.
Beyond the quantum-resistant seed, this system includes a complete three-tier cryptography stack — classical, post-quantum, and hybrid — all pure Python with zero external crypto dependencies. Every algorithm is derived deterministically from the same master seed using HKDF domain separation.
| Algorithm | Standard | Security | Public Key | Use |
|---|---|---|---|---|
| Ed25519 | RFC 8032 | ~128-bit | 32 B | Digital signatures |
| X25519 | RFC 7748 | ~128-bit | 32 B | Diffie-Hellman key exchange |
| Algorithm | Standard | Security | Public Key | Signature / CT | Assumption |
|---|---|---|---|---|---|
| ML-DSA-65 (Dilithium) | FIPS 204 | Level 3 (192-bit PQ) | 1,952 B | 3,309 B sig | Lattice (MLWE) |
| SLH-DSA-SHAKE-128s (SPHINCS+) | FIPS 205 | Level 1 (128-bit PQ) | 32 B | 7,856 B sig | Hash-only (SHAKE-256) |
| ML-KEM-768 (Kyber) | FIPS 203 | Level 3 (192-bit PQ) | 1,184 B | 1,088 B ct | Lattice (MLWE) |
Hybrid schemes combine a classical and post-quantum algorithm in AND-composition — security holds as long as either component remains unbroken. This provides defense in depth during the cryptographic transition period.
| Algorithm | Components | Public Key | Signature / CT | Design |
|---|---|---|---|---|
| Hybrid-DSA-65 | Ed25519 + ML-DSA-65 | 1,984 B | 3,373 B sig | Both must verify |
| Hybrid-KEM-768 | X25519 + ML-KEM-768 | 1,216 B | 1,120 B ct | Secrets combined via HKDF |
Hybrid-DSA-65 — Both Ed25519 and ML-DSA-65 independently sign and verify every message. The Ed25519 component signs a domain-prefixed message (hybrid-dsa-v1 + ctx + message) to prevent signature stripping attacks — an adversary cannot extract the Ed25519 signature and present it as a valid standalone signature.
Hybrid-KEM-768 — X25519 ephemeral DH and ML-KEM-768 encapsulation each produce a shared secret. Both are combined via ciphertext-bound HKDF: the salt includes SHA-256(x25519_ct || ml_kem_ct), preventing ciphertext substitution attacks. The domain string hybrid-kem-v1 provides protocol separation.
from seed import generate_words, get_seed, generate_quantum_keypair
words = generate_words(36)
seed = get_seed(words, "passphrase")
# Post-quantum signatures
sk, pk = generate_quantum_keypair(seed, "ml-dsa-65") # NIST Level 3 lattice
sk, pk = generate_quantum_keypair(seed, "slh-dsa-shake-128s") # NIST Level 1 hash-based
# Post-quantum key encapsulation
ek, dk = generate_quantum_keypair(seed, "ml-kem-768") # NIST Level 3 lattice KEM
# Hybrid signatures — Ed25519 + ML-DSA-65
sk, pk = generate_quantum_keypair(seed, "hybrid-dsa-65")
# Hybrid key encapsulation — X25519 + ML-KEM-768
ek, dk = generate_quantum_keypair(seed, "hybrid-kem-768")
# Default is ML-DSA-65
sk, pk = generate_quantum_keypair(seed)The crypto/ module exposes all algorithms for direct use:
from crypto import (
# Hybrid signatures
hybrid_dsa_keygen, hybrid_dsa_sign, hybrid_dsa_verify,
# Hybrid key encapsulation
hybrid_kem_keygen, hybrid_kem_encaps, hybrid_kem_decaps,
)
# Hybrid-DSA-65: sign and verify
sk, pk = hybrid_dsa_keygen(seed_64_bytes)
sig = hybrid_dsa_sign(b"message", sk, ctx=b"my-protocol")
assert hybrid_dsa_verify(b"message", sig, pk, ctx=b"my-protocol")
# Hybrid-KEM-768: encapsulate and decapsulate
ek, dk = hybrid_kem_keygen(seed_96_bytes)
ct, shared_secret_sender = hybrid_kem_encaps(ek)
shared_secret_receiver = hybrid_kem_decaps(dk, ct)
assert shared_secret_sender == shared_secret_receiver # 32-byte shared secretClassical ECC keys (secp256k1, Ed25519) will be broken by Shor's algorithm on quantum computers. The hybrid and post-quantum algorithms ensure your seed remains secure and usable in a post-quantum world — while the classical components provide a fallback if post-quantum assumptions are ever weakened.
Traditional seed phrases (BIP-39) use a single word per position from a fixed list in one language. A seed written on paper is immediately recognizable and usable by anyone who finds it.
The Universal Quantum Seed takes a fundamentally different approach:
| Traditional (BIP-39) | Universal Quantum Seed | |
|---|---|---|
| Words per position | 1 | Multiple (synonyms, slang, abbreviations) |
| Languages | 10 | 42 |
| Visual recovery | ❌ | ✅ Select icons directly |
| Checksum | 4–8 bit | ✅ 16-bit |
| Paper backup recognizable as crypto? | 🛡️ No — looks like random notes | |
| Mixed-language backup | ❌ | ✅ Write in any combination |
| Accent/diacritic flexible | ❌ | ✅ corazón = corazon |
| Emoji input | ❌ | ✅ Paste 🐕 ☀️ 🔑 directly |
| Key stretching | PBKDF2 | PBKDF2 + Argon2id (chained, defense in depth) |
| Passphrase support | ✅ | ✅ Second factor — same seed + different passphrase = unrelated keys |
| Multiple accounts per seed | ❌ One seed = one wallet | ✅ Unlimited hidden profiles — one seed, many accounts |
36 words = 34 random + 2 checksum = 272 bits of entropy (2²⁷² combinations)
24 words = 22 random + 2 checksum = 176 bits of entropy (2¹⁷⁶ combinations)
| Generate — Cryptographically secure random positions selected from 256 icons using defense-in-depth entropy collection | |
| Display — Each position shows its visual icon alongside accepted words in the user's language | |
| Backup — Write down 36 words in whatever language and form you prefer | |
| Derive — Seed + optional passphrase are hardened through PBKDF2 + Argon2id into a 512-bit master key | |
| Recover — Type your words in any supported language, or select the 36 icons visually |
The system supports two entropy configurations:
| Configuration | Words | Random + Checksum | Entropy | Post-Quantum | Use Case |
|---|---|---|---|---|---|
| Standard (Quantum-Safe) | 36 | 34 + 2 | 272-bit | 136-bit (Grover) | Quantum-safe — required for post-quantum key derivation |
| Compact (Classical) | 24 | 22 + 2 | 176-bit | 88-bit (Grover) | Classical security — sufficient for traditional crypto |
272-bit exceeds the strongest entropy level used in cryptocurrency. Brute-forcing a 272-bit seed would require more energy than the sun produces in its lifetime. Both configurations use the same 256-position icon set with full positional encoding, and include a 16-bit checksum (2 dedicated words) for error detection.
For quantum-safe applications, always use the 36-word format. The 36-word seed provides 272-bit entropy (136-bit post-quantum), which exceeds NIST Level 3 (ML-DSA-65) and Level 5 requirements. The 24-word compact format (176-bit / 88-bit post-quantum) is suitable for classical cryptographic use only.
| System | Effective Security | Post-Quantum | Brute-Force Resistance |
|---|---|---|---|
| Bitcoin (BIP-39, 24 words) | 256-bit | 128-bit (Grover) | Higher security industry standard |
| Universal Quantum Seed (36 words) | 272-bit | 136-bit (Grover) | Quantum-safe tier |
| Universal Quantum Seed + passphrase | 272+ bits | 136+ bits | Second factor expands the keyspace further |
The 36-word seed retains 136-bit security even against a quantum computer running Grover's algorithm — well above the 128-bit post-quantum threshold. The 24-word format provides strong classical security (176-bit) but is not recommended for post-quantum key derivation.
Every generated seed mixes entropy from multiple sources through SHA-512 (a cryptographic randomness extractor). Even if some sources are weak, the output remains cryptographically strong as long as any single source provides real entropy. The OS CSPRNG alone is sufficient; additional sources provide defense in depth.
| # | Source | What It Captures | Est. Entropy |
|---|---|---|---|
| 1 | secrets.token_bytes |
OS CSPRNG (CryptGenRandom / /dev/urandom) |
512 bits |
| 2 | os.urandom |
Separate OS CSPRNG call (defense-in-depth) | 512 bits |
| 3 | time.perf_counter_ns |
Timer state mixed as additional input | low |
| 4 | os.getpid |
Process-level uniqueness | low |
| 5 | CPU jitter | Instruction timing variance (cache/TLB/branch predictor) | variable |
| 6 | Thread scheduling | OS scheduler nondeterminism (4 batches × 8 threads) | variable |
| 7 | Hardware RNG | BCryptGenRandom (Windows) / /dev/random (Linux) + ASLR |
512 bits |
| 8 | Mouse entropy | User-supplied cursor movement (sub-pixel timing + position) | ~512 bits |
All sources are combined into a single SHA-512 pool, then a final secrets.token_bytes call is folded in to guarantee the output is at minimum as strong as the OS CSPRNG alone.
Every call to generate_words() validates its entropy before using it for seed generation. Four statistical tests (based on NIST SP 800-22) are run on every sample:
| Test | What It Catches |
|---|---|
| Monobit | Bit bias — rejects if 0s and 1s aren't ~50/50 |
| Chi-squared | Byte frequency bias — rejects if byte values aren't uniformly distributed |
| Runs | Stuck patterns — rejects if bit transitions are predictable |
| Autocorrelation | Bit correlations — rejects if bit positions are dependent (Bonferroni-corrected) |
If any test fails, the entropy is discarded and regenerated — up to 10 attempts. Only entropy that passes all four tests is ever used. If all 10 attempts fail (indicating a broken or compromised RNG), seed generation raises a RuntimeError and refuses to produce a seed.
This means every seed generated by this system is backed by statistically validated entropy — not just trusted blindly from the OS.
A single CSPRNG (like secrets) is already sufficient for most applications. We go further because:
- Defense in depth — if one source has a flaw, the others compensate
- Hardware diversity — CPU jitter and thread scheduling capture physical nondeterminism independent from the OS random pool
- User involvement — mouse entropy gives users tangible participation in their own security
- Provable minimum — the SHA-512 mixing ensures the output has at least as much entropy as the best single source
After generation, the seed is transformed into a 512-bit master key through a 6-layer hardening pipeline. Each layer addresses a specific attack vector:
Seed (34 × 8-bit icons + 2 checksum) + optional Passphrase
│
┌────▼─────────────────────┐
│ 0. Checksum Verification │ Verifies the 2 checksum words
│ & Stripping │ Then strips them — only data words enter KDF
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 1. Positional Binding │ Each data icon tagged with its slot index
│ (pos, icon) pairs │ Prevents reordering attacks
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 2. Passphrase Mixing │ Optional second factor
│ │ Appended to payload before extraction
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 3. HKDF-Extract │ HMAC-SHA512 with domain separator
│ RFC 5869 │ Collapses (seed + passphrase) → PRK
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 4. Chained KDF │ PBKDF2-SHA512 (600k rounds)
│ PBKDF2 → Argon2id │ then Argon2id (64 MiB × 3 iter × 4 lanes)
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 5. HKDF-Expand │ Domain-separated final derivation
│ RFC 5869 │ Produces 64 bytes of key material
└────┬─────────────────────┘
│
┌────▼─────────────────────┐
│ 512-bit Master Key │ First 32 bytes: encryption key
│ (64 bytes) │ Last 32 bytes: authentication key
└──────────────────────────┘
Each icon is tagged with its slot position before hashing:
payload = [(pos=0, icon=15), (pos=1, icon=63), (pos=2, icon=136), ...]
Why: Each icon is cryptographically bound to its exact slot. Position tagging makes the security property explicit rather than relying on implicit byte ordering — the position-value relationship is part of the hashed data itself. This is a cryptographic best practice (structured commitment) that ensures [dog, sun, key] and [sun, dog, key] always produce completely different keys, regardless of how the payload is serialized.
The optional passphrase is UTF-8 encoded and appended to the position-tagged payload before extraction. This ensures the passphrase influences every downstream step:
payload = [(pos=0, icon=15), (pos=1, icon=63), ...] + passphrase_bytes
Why: The passphrase acts as a second factor (something you know in addition to the seed you have). By mixing it into the input keying material before HKDF-Extract, it becomes part of the PRK — and therefore affects the chained KDF, HKDF-Expand, and every derived key. Same seed + different passphrase = completely unrelated output. Brute-forcing the passphrase costs ~2 seconds per attempt (full PBKDF2 + Argon2id chain).
The combined payload (seed + passphrase) is collapsed into a fixed-size pseudorandom key (PRK) using HMAC-SHA512 with a domain separator (universal-seed-v1):
PRK = HMAC-SHA512(key="universal-seed-v1", msg=payload)
Why: HKDF-Extract is a proven randomness extractor. It takes the variable-length payload (which may have structure — repeating icons, short seeds, passphrase) and produces a uniformly distributed 512-bit key. The domain separator ensures that keys derived by this system can never collide with keys from any other system, even if the input data is identical.
The PRK is stretched through two KDFs in series — PBKDF2-SHA512 first, then Argon2id on top. Both always run; an attacker must break both to recover the key.
| Parameter | Stage 1: PBKDF2-SHA512 | Stage 2: Argon2id |
|---|---|---|
| Rounds / Iterations | 600,000 | 3 |
| Memory | N/A | 64 MiB per guess |
| Parallelism | N/A | 4 lanes |
| Output | 64 bytes → fed into Stage 2 | 64 bytes (final) |
| GPU resistance | Low | High (memory-hard) |
| ASIC resistance | Low | High (memory-hard) |
stage1 = PBKDF2-SHA512(PRK, salt="universal-seed-v1-stretch-pbkdf2", rounds=600000)
stretched = Argon2id(secret=stage1, salt="universal-seed-v1-stretch-argon2id")
Why: Defense in depth. PBKDF2-SHA512 provides a proven, NIST-approved baseline that resists brute force through sheer iteration count. Argon2id adds memory-hardness on top, making GPU/ASIC parallelization impractical — each attempt requires 64 MiB of RAM. If a vulnerability were ever found in one algorithm, the other still protects the key.
Argon2id is the winner of the Password Hashing Competition (2015) and the current OWASP recommendation for high-value targets.
pip install argon2-cffi # optional — ~100x faster, pure Python fallback includedThe stretched key is expanded into the final 64-byte master key using HKDF-Expand with a domain-specific info string:
master_key = HKDF-Expand(PRK=stretched, info="universal-seed-v1-master", length=64)
Why: HKDF-Expand provides domain separation for the final output. If this system ever needs to derive multiple keys (e.g., encryption key + authentication key), each can use a different info string. The first 32 bytes serve as a 256-bit encryption key, and the last 32 bytes serve as a 256-bit authentication key.
The passphrase acts as a second factor that makes the derived key dependent on something the user knows, in addition to the seed they have.
| Scenario | Result |
|---|---|
| Same seed, no passphrase | Always produces the same key |
| Same seed + passphrase A | Key X |
| Same seed + passphrase B | Completely unrelated Key Y |
| Different seed + passphrase A | Completely unrelated Key Z |
Key properties:
- The passphrase only affects the derived key and fingerprint, not the displayed words/icons
- An empty passphrase is valid and produces a deterministic key
- The passphrase goes through the full PBKDF2 + Argon2id pipeline — brute-forcing is expensive
- Entropy from the passphrase adds to the seed entropy (272 + passphrase bits)
The get_entropy_bits() function estimates total security strength:
from seed import get_entropy_bits
get_entropy_bits(36) # → 272.0 (seed only)
get_entropy_bits(36, "hunter2") # → 305.3 (+ passphrase)
get_entropy_bits(36, "Tr0ub4dor&3") # → 337.2 (mixed case + digits + symbols)
get_entropy_bits(24) # → 176.0 (compact seed)
get_entropy_bits(24, "パスワード") # → 225.2 (+ Unicode passphrase)Passphrase entropy is estimated from the character set used:
| Character Set | Bits per Character |
|---|---|
| Digits only (0-9) | ~3.32 |
| Lowercase only (a-z) | ~4.70 |
| Mixed case (a-z, A-Z) | ~5.70 |
| Mixed + digits | ~5.95 |
| Full printable (+ symbols) | ~6.55 |
| Unicode (non-ASCII) | ~7.00 |
Hidden Profiles — Multiple Accounts, One Seed
Hidden profiles let you derive unlimited independent keys from a single master key using profile passwords. Each profile password produces a completely unrelated key — and without the password, no one can detect that the profile exists.
Seed → Master Key (expensive KDF — runs once)
├── default (no password) = master key
├── "personal" → independent 64-byte key
├── "business" → independent 64-byte key
└── "savings" → independent 64-byte key
from seed import generate_words, get_seed, get_profile
words = generate_words(36)
seed = get_seed(words)
personal = get_profile(seed, "personal") # independent key
business = get_profile(seed, "business") # completely unrelated
savings = get_profile(seed, "savings") # each password = new account
default = get_profile(seed, "") # empty = master key itself| Property | Detail |
|---|---|
| Algorithm | HMAC-SHA512(master_key, domain + password) |
| Speed | Instant — single HMAC, no KDF (master key is already hardened) |
| Deterministic | Same password always produces the same key |
| Independent | Profiles cannot be derived from each other |
| Hidden | No way to enumerate how many profiles exist |
| Plausible deniability | Under duress, reveal only the default profile |
Why this matters: With BIP-39, one seed = one wallet. To manage multiple accounts you need multiple seeds. With the Universal Quantum Seed, one seed + profile passwords = unlimited independent wallets, all hidden behind a single backup.
Everything lives in a single file — seed.py. Import it and you get seed generation, key derivation, word lookup, and entropy estimation.
pip install argon2-cffi # optional — ~100x faster, pure Python fallback includedNo external dependencies required. seed.py uses only the Python standard library and the bundled crypto/argon2.py module. Installing argon2-cffi is optional but recommended for performance (~100x faster Argon2id).
from seed import generate_words, get_seed, get_fingerprint, get_entropy_bits, get_languages, verify_checksum
# Generate 36 words (272-bit entropy, 34 random + 2 checksum)
words = generate_words(36)
# → [(15, "dog"), (63, "sun"), (136, "key"), ..., (cs1, "word"), (cs2, "word")]
# Generate in a specific language
words = generate_words(36, language="french")
# → [(15, "chien"), (63, "soleil"), (136, "clé"), ...]
# List available languages
get_languages()
# → [("english", "English"), ("arabic", "العربية"), ("french", "Français"), ...]
# Derive the master seed — pass the words directly
seed = get_seed(words) # 64-byte master seed
fp = get_fingerprint(words) # "A3F1B2C4"
# Verify checksum (last 2 words)
verify_checksum(words) # True
# With a passphrase (second factor — same words, different passphrase = different seed)
seed = get_seed(words, "my secret passphrase")
# Hidden profiles — multiple accounts from one seed
from seed import get_profile
personal = get_profile(seed, "personal") # independent 64-byte key
business = get_profile(seed, "business") # completely unrelated key
# Post-quantum keypair from the same seed
from seed import generate_quantum_keypair
sk, pk = generate_quantum_keypair(seed) # ML-DSA-65 (default)
sk, pk = generate_quantum_keypair(seed, "hybrid-dsa-65") # Ed25519 + ML-DSA-65 hybrid
ek, dk = generate_quantum_keypair(seed, "hybrid-kem-768") # X25519 + ML-KEM-768 hybrid
# Also accepts plain word strings or raw indexes (must be 24 or 36 with valid checksum)
plain = [w for _, w in words] # extract word strings
seed = get_seed(plain) # resolve words → indexes → seed
seed = get_seed([i for i, _ in words]) # raw indexes work too
# Estimate total entropy
bits = get_entropy_bits(36, "my secret passphrase")
# → 383.8 (272 seed + 111.8 passphrase)from seed import resolve, search
# Resolve any word in any of 42 languages → icon index
resolve("dog") # → 15
resolve("perro") # → 15 (Spanish)
resolve("犬") # → 15 (Japanese)
resolve("🐕") # → 15 (emoji)
resolve("corazón") # → 8 (with accent)
resolve("corazon") # → 8 (without accent — same result)
resolve("собака") # → 15 (Russian)
resolve("unknown") # → None
# Autocomplete suggestions
search("do")
# → [("doctor", 211), ("dog", 15), ("dolphin", 54), ("door", 158)]
# Resolve a full seed phrase at once (pass a list)
indexes, errors = resolve(["dog", "sun", "key", "heart"])
# indexes = [15, 63, 136, 8], errors = []from seed import mouse_entropy, generate_words
# Create an entropy pool
pool = mouse_entropy()
# Feed mouse movements (call on each mouse move event)
pool.add_sample(x=412, y=308) # → True (new position)
pool.add_sample(x=412, y=308) # → False (duplicate, skipped)
pool.add_sample(x=415, y=310) # → True
# Check progress
pool.bits_collected # → 4 (2 bits per unique sample)
pool.sample_count # → 2
# Extract and use
extra = pool.digest() # 64 bytes of entropy
words = generate_words(36, extra_entropy=extra) # mixed into generationVerify that the entropy source is producing high-quality randomness before trusting it for seed generation:
from seed import verify_randomness
result = verify_randomness()
# Check overall result
if not result["pass"]:
raise RuntimeError("Weak randomness detected!")
# Iterate individual tests
for test in result["tests"]:
status = "PASS" if test["pass"] else "FAIL"
print(f"{test['test']}: {status}")Four statistical tests based on NIST SP 800-22:
| Test | What It Detects |
|---|---|
| Monobit | Bit bias — 0s and 1s should be ~50/50 |
| Chi-squared | Byte frequency bias — all 256 values should appear uniformly |
| Runs | Stuck patterns — bit transitions should be random |
| Autocorrelation | Bit correlations — each bit position should be independent |
The test app (examples/universal.py) includes a RandomnessDialog window that runs these tests with a progress bar and displays checkmarks for each passing test.
from seed import kdf_info
print(kdf_info())
# → "PBKDF2-SHA512 (600,000 rounds) + Argon2id (mem=65536KB, t=3, p=4)"| Function | Signature | Returns |
|---|---|---|
generate_words |
generate_words(word_count=36, extra_entropy=None, language=None) |
list[(int, str)] — index/word pairs (last 2 are checksum) |
verify_checksum |
verify_checksum(words) |
bool — True if last 2 words match expected checksum |
get_seed |
get_seed(words, passphrase="") |
bytes — 64-byte master seed (checksum verified & stripped) |
get_profile |
get_profile(seed, profile_password) |
bytes — 64-byte profile key (instant HMAC, no KDF) |
get_fingerprint |
get_fingerprint(seed, passphrase="") |
str — 8-char hex (checksum stripped) |
get_entropy_bits |
get_entropy_bits(word_count, passphrase="") |
float — estimated total entropy |
resolve |
resolve(word_or_list, strict=False) |
str → int | None; list → (indexes, errors) |
search |
search(prefix, limit=10) |
list[(str, int)] — word/index pairs |
verify_randomness |
verify_randomness(sample_bytes=None, sample_size=2048, num_samples=5) |
dict — {"pass": bool, "tests": [...], "summary": str} |
mouse_entropy |
class | Entropy collection pool |
get_languages |
get_languages() |
list[(str, str)] — (code, label) pairs |
get_quantum_seed |
get_quantum_seed(master_key, algorithm="ml-dsa-65", key_index=0) |
bytes — raw quantum seed material (32–96 bytes) |
generate_quantum_keypair |
generate_quantum_keypair(master_key, algorithm="ml-dsa-65", key_index=0) |
tuple[bytes, bytes] — (secret_key, public_key) |
hybrid_dsa_keygen |
hybrid_dsa_keygen(seed_64B) |
tuple[bytes, bytes] — (4,096B sk, 1,984B pk) |
hybrid_dsa_sign |
hybrid_dsa_sign(message, sk, ctx=b"") |
bytes — 3,373B hybrid signature |
hybrid_dsa_verify |
hybrid_dsa_verify(message, sig, pk, ctx=b"") |
bool — True if both Ed25519 AND ML-DSA verify |
hybrid_kem_keygen |
hybrid_kem_keygen(seed_96B) |
tuple[bytes, bytes] — (1,216B ek, 2,432B dk) |
hybrid_kem_encaps |
hybrid_kem_encaps(ek, randomness=None) |
tuple[bytes, bytes] — (1,120B ct, 32B shared_secret) |
hybrid_kem_decaps |
hybrid_kem_decaps(dk, ct) |
bytes — 32B shared secret |
kdf_info |
kdf_info() |
str — chained KDF pipeline description |
42 languages covering 85%+ of the world's internet-connected population.
| # | Flag | Language | Native Name | Script |
|---|---|---|---|---|
| 1 | 🇸🇦 | Arabic | العربية | Arabic |
| 2 | 🇧🇩 | Bengali | বাংলা | Bengali |
| 3 | 🇭🇰 | Chinese (Cantonese) | 廣東話 | Traditional Chinese |
| 4 | 🇨🇳 | Chinese (Simplified) | 简体中文 | Simplified Chinese |
| 5 | 🇹🇼 | Chinese (Traditional) | 繁體中文 | Traditional Chinese |
| 6 | 🇨🇿 | Czech | Čeština | Latin |
| 7 | 🇩🇰 | Danish | Dansk | Latin |
| 8 | 🇳🇱 | Dutch | Nederlands | Latin |
| 9 | 🇬🇧 | English | English | Latin |
| 10 | 🇵🇭 | Filipino | Filipino | Latin |
| 11 | 🇫🇷 | French | Français | Latin |
| 12 | 🇩🇪 | German | Deutsch | Latin |
| 13 | 🇬🇷 | Greek | Ελληνικά | Greek |
| 14 | 🇳🇬 | Hausa | Hausa | Latin |
| 15 | 🇮🇱 | Hebrew | עברית | Hebrew |
| 16 | 🇮🇳 | Hindi | हिन्दी | Devanagari |
| 17 | 🇭🇺 | Hungarian | Magyar | Latin |
| 18 | 🇮🇸 | Icelandic | Íslenska | Latin |
| 19 | 🇮🇩 | Indonesian | Bahasa Indonesia | Latin |
| 20 | 🇮🇪 | Irish | Gaeilge | Latin |
| 21 | 🇮🇹 | Italian | Italiano | Latin |
| 22 | 🇯🇵 | Japanese | 日本語 | Kanji / Kana |
| 23 | 🇰🇷 | Korean | 한국어 | Hangul |
| 24 | 🇱🇺 | Luxembourgish | Lëtzebuergesch | Latin |
| 25 | 🇲🇾 | Malay | Bahasa Melayu | Latin |
| 26 | 🇮🇳 | Marathi | मराठी | Devanagari |
| 27 | 🇳🇴 | Norwegian | Norsk | Latin |
| 28 | 🇮🇷 | Persian | فارسی | Arabic |
| 29 | 🇵🇱 | Polish | Polski | Latin |
| 30 | 🇧🇷 | Portuguese | Português | Latin |
| 31 | 🇮🇳 | Punjabi | ਪੰਜਾਬੀ | Gurmukhi |
| 32 | 🇷🇴 | Romanian | Română | Latin |
| 33 | 🇷🇺 | Russian | Русский | Cyrillic |
| 34 | 🇪🇸 | Spanish | Español | Latin |
| 35 | 🇹🇿 | Swahili | Kiswahili | Latin |
| 36 | 🇮🇳 | Tamil | தமிழ் | Tamil |
| 37 | 🇮🇳 | Telugu | తెలుగు | Telugu |
| 38 | 🇹🇭 | Thai | ไทย | Thai |
| 39 | 🇹🇷 | Turkish | Türkçe | Latin |
| 40 | 🇺🇦 | Ukrainian | Українська | Cyrillic |
| 41 | 🇵🇰 | Urdu | اردو | Arabic |
| 42 | 🇻🇳 | Vietnamese | Tiếng Việt | Latin |
10 scripts supported — Latin · Arabic · Hebrew · Devanagari · Bengali · Gurmukhi · Tamil · Telugu · Thai · CJK
256 universally recognizable icons — a dog is a dog everywhere, the sun is the sun, a key is a key.
All icons are available as PNG (256×256, transparent background) in visuals/png/ and SVG in visuals/svg/, named by index (0.png through 255.png).
Body Parts · 0 – 14
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 0 | 👁️ | eye | 8 | ❤️ | heart | |
| 1 | 👂 | ear | 9 | 🧠 | brain | |
| 2 | 👃 | nose | 10 | 👶 | baby | |
| 3 | 👄 | mouth | 11 | 🦶 | foot | |
| 4 | 👅 | tongue | 12 | 💪 | muscle | |
| 5 | 🦴 | bone | 13 | ✋ | hand | |
| 6 | 🦷 | tooth | 14 | 🦵 | leg | |
| 7 | 💀 | skull |
Mammals · 15 – 37
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 15 | 🐕 | dog | 27 | 🦌 | deer | |
| 16 | 🐈 | cat | 28 | 🐘 | elephant | |
| 17 | 🐎 | horse | 29 | 🦇 | bat | |
| 18 | 🐄 | cow | 30 | 🐫 | camel | |
| 19 | 🐖 | pig | 31 | 🦓 | zebra | |
| 20 | 🐐 | goat | 32 | 🦒 | giraffe | |
| 21 | 🐇 | rabbit | 33 | 🦊 | fox | |
| 22 | 🐁 | mouse | 34 | 🦁 | lion | |
| 23 | 🐅 | tiger | 35 | 🐒 | monkey | |
| 24 | 🐺 | wolf | 36 | 🐼 | panda | |
| 25 | 🐻 | bear | 37 | 🦙 | llama | |
| 26 | 🐿️ | squirrel |
Birds · 38 – 44
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 38 | 🐔 | chicken | 42 | 🦚 | peacock | |
| 39 | 🐦 | bird | 43 | 🦉 | owl | |
| 40 | 🦆 | duck | 44 | 🦅 | eagle | |
| 41 | 🐧 | penguin |
Reptiles & Amphibians · 45 – 49
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 45 | 🐍 | snake | 48 | 🐊 | crocodile | |
| 46 | 🐸 | frog | 49 | 🦎 | lizard | |
| 47 | 🐢 | turtle |
Aquatic · 50 – 55
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 50 | 🐟 | fish | 53 | 🐳 | whale | |
| 51 | 🐙 | octopus | 54 | 🐬 | dolphin | |
| 52 | 🦀 | crab | 55 | 🦈 | shark |
Bugs & Crawlers · 56 – 62
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 56 | 🐌 | snail | 60 | 🪱 | worm | |
| 57 | 🐜 | ant | 61 | 🕷️ | spider | |
| 58 | 🐝 | bee | 62 | 🦂 | scorpion | |
| 59 | 🦋 | butterfly |
Sky & Weather · 63 – 78
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 63 | ☀️ | sun | 71 | 🌈 | rainbow | |
| 64 | 🌙 | moon | 72 | 💨 | wind | |
| 65 | ⭐ | star | 73 | ⚡ | thunder | |
| 66 | 🌍 | earth | 74 | 🌋 | volcano | |
| 67 | 🔥 | fire | 75 | 🌪️ | tornado | |
| 68 | 💧 | water | 76 | ☄️ | comet | |
| 69 | ❄️ | snow | 77 | 🌊 | wave | |
| 70 | ☁️ | cloud | 78 | 🌧️ | rain |
Landscapes · 79 – 84
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 79 | 🏜️ | desert | 82 | 🪨 | rock | |
| 80 | 🏝️ | island | 83 | 💎 | diamond | |
| 81 | ⛰️ | mountain | 84 | 🪶 | feather |
Plants & Fungi · 85 – 90
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 85 | 🌳 | tree | 88 | 🍃 | leaf | |
| 86 | 🌵 | cactus | 89 | 🍄 | mushroom | |
| 87 | 🌸 | flower | 90 | 🪵 | wood |
Fruits · 91 – 104
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 91 | 🥭 | mango | 98 | 🍍 | pineapple | |
| 92 | 🍎 | apple | 99 | 🍒 | cherry | |
| 93 | 🍌 | banana | 100 | 🍋 | lemon | |
| 94 | 🍇 | grape | 101 | 🥥 | coconut | |
| 95 | 🍊 | orange | 102 | 🥒 | cucumber | |
| 96 | 🍈 | melon | 103 | 🌱 | seed | |
| 97 | 🍑 | peach | 104 | 🍓 | strawberry |
Vegetables · 105 – 112
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 105 | 🌽 | corn | 109 | 🌶️ | pepper | |
| 106 | 🥕 | carrot | 110 | 🍅 | tomato | |
| 107 | 🧅 | onion | 111 | 🧄 | garlic | |
| 108 | 🥔 | potato | 112 | 🥜 | peanut |
Prepared Food · 113 – 120
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 113 | 🍞 | bread | 117 | 🍚 | rice | |
| 114 | 🧀 | cheese | 118 | 🎂 | cake | |
| 115 | 🥚 | egg | 119 | 🍿 | snack | |
| 116 | 🥩 | meat | 120 | 🍬 | sweet |
Food & Drink · 121 – 128
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 121 | 🍯 | honey | 125 | 🍷 | wine | |
| 122 | 🥛 | milk | 126 | 🍺 | beer | |
| 123 | ☕ | coffee | 127 | 🧃 | juice | |
| 124 | 🍵 | tea | 128 | 🧂 | salt |
Kitchen · 129 – 135
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 129 | 🍴 | fork | 133 | 🍶 | bottle | |
| 130 | 🥄 | spoon | 134 | 🍜 | soup | |
| 131 | 🥣 | bowl | 135 | 🍳 | pan | |
| 132 | 🔪 | knife |
Tools & Weapons · 136 – 152
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 136 | 🔑 | key | 145 | 🧭 | compass | |
| 137 | 🔒 | lock | 146 | 🪝 | hook | |
| 138 | 🔔 | bell | 147 | 🧵 | thread | |
| 139 | 🔨 | hammer | 148 | 🪡 | needle | |
| 140 | 🪓 | axe | 149 | ✂️ | scissors | |
| 141 | ⚙️ | gear | 150 | ✏️ | pencil | |
| 142 | 🧲 | magnet | 151 | 🛡️ | shield | |
| 143 | ⚔️ | sword | 152 | 💣 | bomb | |
| 144 | 🏹 | bow |
Buildings · 153 – 164
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 153 | 🏠 | house | 159 | 🪟 | window | |
| 154 | 🏰 | castle | 160 | ⛺ | tent | |
| 155 | ⛩️ | temple | 161 | 🏖️ | beach | |
| 156 | 🌉 | bridge | 162 | 🏦 | bank | |
| 157 | 🏭 | factory | 163 | 🗼 | tower | |
| 158 | 🚪 | door | 164 | 🗽 | statue |
Transport · 165 – 176
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 165 | 🛞 | wheel | 171 | 🚀 | rocket | |
| 166 | ⛵ | boat | 172 | 🚁 | helicopter | |
| 167 | 🚂 | train | 173 | 🚑 | ambulance | |
| 168 | 🚗 | car | 174 | ⛽ | fuel | |
| 169 | 🚲 | bike | 175 | 🛤️ | track | |
| 170 | plane | 176 | 🗺️ | map |
Music & Arts · 177 – 188
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 177 | 🥁 | drum | 183 | 🎭 | mask | |
| 178 | 🎸 | guitar | 184 | 📷 | camera | |
| 179 | 🎻 | violin | 185 | 🎤 | microphone | |
| 180 | 🎹 | piano | 186 | 🎧 | headset | |
| 181 | 🎨 | paint | 187 | 🎬 | movie | |
| 182 | 📖 | book | 188 | 🎵 | music |
Clothing · 189 – 195
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 189 | 👗 | dress | 193 | 👔 | shirt | |
| 190 | 🧥 | coat | 194 | 👟 | shoes | |
| 191 | 👖 | pants | 195 | 🎩 | hat | |
| 192 | 🧤 | glove |
Symbols · 196 – 207
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 196 | 🚩 | flag | 202 | alert | ||
| 197 | ✝️ | cross | 203 | 💤 | sleep | |
| 198 | ⭕ | circle | 204 | 🪄 | magic | |
| 199 | 🔺 | triangle | 205 | 💬 | message | |
| 200 | 🟦 | square | 206 | 🩸 | blood | |
| 201 | ✅ | check | 207 | 🔁 | repeat |
Science & Tech · 208 – 223
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 208 | 🧬 | dna | 216 | 🛰️ | satellite | |
| 209 | 🦠 | germ | 217 | 🔋 | battery | |
| 210 | 💊 | pill | 218 | 🔭 | telescope | |
| 211 | 🩺 | doctor | 219 | 📺 | tv | |
| 212 | 🔬 | microscope | 220 | 📻 | radio | |
| 213 | 🌌 | galaxy | 221 | 📱 | phone | |
| 214 | 🧪 | flask | 222 | 💡 | bulb | |
| 215 | ⚛️ | atom | 223 | ⌨️ | keyboard |
Home · 224 – 235
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 224 | 🪑 | chair | 230 | 🏺 | vase | |
| 225 | 🛏️ | bed | 231 | 🚿 | shower | |
| 226 | 🕯️ | candle | 232 | 🪒 | razor | |
| 227 | 🪞 | mirror | 233 | 🧼 | soap | |
| 228 | 🪜 | ladder | 234 | 💻 | computer | |
| 229 | 🧺 | basket | 235 | 🗑️ | trash |
Everyday Items · 236 – 245
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 236 | ☔ | umbrella | 241 | 💍 | ring | |
| 237 | 💰 | money | 242 | 🎲 | dice | |
| 238 | 🙏 | prayer | 243 | 🧩 | piece | |
| 239 | 🧸 | toy | 244 | 🪙 | coin | |
| 240 | 👑 | crown | 245 | 📆 | calendar |
Sports & Games · 246 – 249
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 246 | 🥊 | boxing | 248 | 🎮 | game | |
| 247 | 🏊♂️ | swimming | 249 | ⚽ | soccer |
Fantasy · 250 – 254
| Index | Icon | Word | Index | Icon | Word | |
|---|---|---|---|---|---|---|
| 250 | 👻 | ghost | 253 | 👼 | angel | |
| 251 | 👽 | alien | 254 | 🐉 | dragon | |
| 252 | 🤖 | robot |
Time · 255
| Index | Icon | Word |
|---|---|---|
| 255 | 🕐 | clock |
All 42 language word lists plus emoji characters are compiled into a single Python module (words.py) containing a flat hash table for instant word resolution and embedded language maps for generation in any language.
| Feature | How it works |
|---|---|
| Emoji input | Typing or pasting an emoji (e.g. 💪 🐕 ☀️) resolves directly to its visual index |
| NFKC normalization | Full-width characters, ligatures, and composed forms unified automatically |
| Zero-width removal | ZWJ, ZWNJ, soft hyphens, BOM, and invisible characters stripped |
| Case insensitive | All lookups are lowercase-normalized |
| Prefix search | Built-in search() for autocomplete / search UI |
Smart per-script handling — marks are only stripped where it's safe:
| Script | Behavior | Example |
|---|---|---|
| Latin | Accents removed | corazón → corazon, ß → ss, ø → o, æ → ae |
| Greek | Tonos removed | σκύλος → σκυλος |
| Arabic | Tashkeel removed | Vowel marks (harakat) are optional |
| Hebrew | Niqqud removed | Vowel points are optional |
| Cyrillic | ё → е | Common Russian substitution |
| Thai, Devanagari, Bengali, Tamil, Telugu, Gurmukhi | Preserved | Marks change meaning — never stripped |
| Operation | Time | Notes |
|---|---|---|
| Import | ~50 ms | One-time at startup (38,730 keys, cached .pyc) |
| Generate 36 words | ~9 ms | Full entropy collection + checksum |
| Generate 24 words | ~5 ms | Full entropy collection + checksum |
| Key derivation | ~2 sec | PBKDF2 (600k rounds) + Argon2id (64 MiB) |
| Word resolve | ~0.01 ms | O(1) hash table lookup |
| Prefix search | ~0.04 ms | Binary search + dedup |
| 36-word seed resolve | ~0.3 ms | 36 × resolve |
| Fingerprint (no passphrase) | <0.01 ms | HMAC only |
| Fingerprint (with passphrase) | ~2 sec | Full chained KDF pipeline |
| Stat | Value |
|---|---|
| Total lookup keys | 38,730 across 42 languages + emoji |
| Avg words per position | 3.5 |
| Max word length (shortest per index) | 7 chars across all languages |
| Languages with 0 single-word indexes | 36 of 42 |
| Cross-language collisions | 0 |
| File size (words.py) | 1,186 KB |
A user generates a seed and sees:
Position 1: 🐕 dog
Position 2: ☀️ sun
Position 3: 🔑 key
...
They write their backup on paper — in any language, using any accepted word, or even a personal synonym that reminds them of the visual:
| Approach | Backup | Why it works |
|---|---|---|
| 🇬🇧 English | dog sun key ... |
Direct base words |
| 🇪🇸 Spanish | perro sol llave ... |
Native language |
| 🇯🇵 Japanese | 犬 太陽 鍵 ... |
Any supported script |
| Mixed | dog soleil key ... |
Languages can be combined freely |
| Personal hints | puppy bright lock ... |
Any accepted synonym — write what makes sense to you |
To recover, they type what they wrote — in any language — and the system maps each word back to its visual position. Alternatively, they can select the 36 icons directly, bypassing language entirely.
A test app is included for trying out seed generation and recovery.
pip install PySide6
pip install argon2-cffi # optional — faster key derivation
python examples/universal.pyContributions are welcome — especially for improving word coverage across languages. Adding more synonyms, shorter alternatives, regional variants, and colloquial terms for each visual position would be very appreciated.
See CONTRIBUTING.md for details on how to contribute, the file format, and guidelines for adding words.
Visual icons are from Microsoft Fluent Emoji (flat style), used under the MIT License. See visuals/LICENSE for details.
MIT License. See LICENSE.
Quantum-safe. Built for everyone, everywhere.
Post-quantum signatures · 42 languages · 256 icons · PBKDF2 + Argon2id hardened · 272-bit quantum-safe (36 words) · 16-bit checksum


